{
  Version 2.2
  Author: H van Zwietering
  Date: 10 august 2020
  Changes: 
    a. Now uses PublicCatalog instead of Catalog
    b. You can configure to use catalog labels with a minimum name length

  Version 2.1
  Author: H van Zwietering
  Date: 28 november 2017
  Changes: When merging then first determine which label should be in the lead (typically the one not in Miscellaneous)

  Version 2.0
  Author: H van Zwietering
  Date: 3 April 2015
  Changes: Added option to save every name and added option to auto merge

  Version 1.1
  Author: H van Zwietering
  Date: 24 Januari 2014
  Changes: Made compatible with PSU


  Version 1.0
  Author: H van Zwietering
  Initial Date: 3 september 2010
}

const
  cMinLabelNameLength = 0;

  procedure FindDuplicates (AList: TTntStringList; AResult: TTntStringList);
  var
    AIdx: Integer;
    AName: WideString;
    AProp, AProp2: TCatalogItemProp;
    ADupProps: TStringList;
    AIsDup: Boolean;
  begin
    while AList.Count > 0 do
    begin
      AIsDup := False;
      AName := AList.Strings[AList.Count - 1];
      AProp := AList.Objects[AList.Count - 1];

      AList.Delete (AList.Count - 1);

      AIdx := AList.IndexOf (AName)
      if AIdx <> -1 then
      begin
        ADupProps := TStringList.Create;
        ADupProps.AddObject(AProp.GUID, AProp);
        AResult.AddObject (AProp.PropName, ADupProps);
        while AIdx <> -1 do
        begin
          AIsDup := True;
          AProp2 := AList.Objects[AIdx];
          ADupProps.AddObject(AProp2.GUID, AProp2);
          AList.Delete (AIdx);

          AIdx := AList.IndexOf (AName);
        end;
      end;
    end;
  end;

var
  ASl: TTntStringList;
  AProps: TCatalogItemProps;
  ACat: TCatalogPropCategory;
  AProp, AProp2: TCatalogItemProp;
  ADups: TTntStringList;
  APropDups: TStringList;
  ARes, i, j, ALeadIdx: Integer;
  ACheckResult: Boolean;
  AOptions: TTntStringList;
begin
  ASl := TTntStringList.Create;
  ADups := TTntStringList.Create;
  ADups.Sorted := True;

  Screen.Cursor := crHourglass;
  AProps := TCatalogItemProps.Create (TCatalogItemProp, '');
  PublicCatalog.EnumAllProps (AProps);

  // read all leaf-names to the stringlist
  for i := 0 to AProps.Count - 1 do
  begin
    // skip catalog labels with a length of 1
    if Length(AProps.Items[i].PropName) > cMinLabelNameLength then
      ASl.AddObject(AProps.Items[i].PropName, AProps.Items[i]);
  end;

  ASl.Sorted := True;
  ASl.CaseSensitive := False;

  FindDuplicates (ASl, ADups);
  Screen.Cursor := crDefault;

  if ADups.Count > 0 then
  begin
    ASl.Clear;

    // what to do with the duplicates
    AOptions := TTntStringList.Create;
    AOptions.Add('Save all duplicate names to file*');    // the * at the ends indicates the default option
    AOptions.Add('Save unique duplicate names to file');
    AOptions.Add('Automatically merge all found duplicates together');
    ACheckResult := False;
    ARes := AskCustom(
                       'What to do with the results?',
                       'select your option',
                       '',
                       '',
                       ACheckResult,
                       AOptions
                     );
    AOptions.Free;

    Screen.Cursor := crHourglass;
    for i := 0 to ADups.Count - 1 do
    begin
      APropDups := TStringList(ADups.Objects[i]);

      if APropDups.Count >= 2 then   // these are duplicates so it will be odd if this fails
      begin
        if ARes = 0 then   // all names to files
        begin
          for j := 0 to APropDups.Count - 1 do
          begin
            AProp := TCatalogItemProp(APropDups.Objects[j]);
            ASl.Add (PublicCatalog.PathNameForProp (AProp, '::', True));
          end;
        end

        else if ARes = 1 then  // only names to file
        begin
          AProp := TCatalogItemProp(APropDups.Objects[0]);
          ASl.Add (AProp.PropName);
        end

        else if ARes = 2 then   // auto merge all
        begin
          ALeadIdx := 0;
          for j := 0 to APropDups.Count - 1 do
          begin
            AProp := TCatalogItemProp(APropDups.Objects[j]);
            ACat := TCatalogPropCategory.Create(nil);
            PublicCatalog.EnumCategoryForProp(AProp, ACat);
            if ACat.GUID <> KeywordsGUID then
            begin
              ALeadIdx := j;
              break;
            end;
            ACat.Free;
          end;

          AProp := TCatalogItemProp(APropDups.Objects[ALeadIdx]);
          for j := 0 to APropDups.Count - 1 do
          begin
            if j = ALeadIdx then
              Continue;
            AProp2 := TCatalogItemProp(APropDups.Objects[j]);
            PublicCatalog.MergePropWithProp(AProp2, AProp, True);
          end;
        end;
      end;

      APropDups.Free;  // no more need...
    end;
    Screen.Cursor := crDefault;

    if ASl.Count > 0 then
    begin
      SaveDialog.Title := 'Save duplicates report as...';
      SaveDialog.InitialDir := WindowsDesktopDir;
      SaveDialog.DefaultExt := '.txt';
      SaveDialog.FileName := 'duplicate labels';

      if SaveDialog.Execute then
      begin
        ASl.SaveToFile (SaveDialog.FileName);
        Say ('Duplicates report saved as ' + SaveDialog.FileName);
      end;
    end

    else if ARes = 2 then
      Say('Merge complete...');
  end
  else
    Say ('No duplicates found');

  AProps.Free;
  ADups.Free;
  ASl.Free;
end;
