var
  ChangeList: TTntStringList;

var
  ConfigForm: TvxSceneForm;
  OffsetLevel: TvxSpinBox;
  CountryMapping: TvxCheckBox;
  StateMapping: TvxCheckBox;
  CityMapping: TvxCheckBox;
  LocationMapping: TvxCheckBox;

  function BuildForm: TvxSceneForm;
  var
    ALabel: TvxLabel;
  begin
    result := TvxSceneForm.Create(nil);

    result.Caption := 'Places Hierarchy Mapping to Metadata';

    ALabel := TvxLabel.Create(result.Root);
    ALabel.Text := 'At what level below the Places category are your Countries? (1 = immediatly under Places):';
    ALabel.TextAlign := vgTextAlignNear;
    ALabel.Padding.Rect := vxRect(15, 35, 5, 5);
    ALabel.Height := 40;
    ALabel.Align := vaTop;
    result.Root.AddObject(ALabel);

    OffsetLevel := TvxSpinBox.Create(result.Root);
    OffsetLevel.Min   := 1;
    OffsetLevel.Max   := 5;
    OffsetLevel.Value := ReadFromRegistry('ScriptPlacesHierarchy', 'OffsetLevel', 1);
    OffsetLevel.Padding.Rect := vxRect(35, 5, result.Width * 0.3, 5);
    OffsetLevel.Align := vaTop;
    result.Root.AddObject(OffsetLevel);

    CountryMapping := TvxCheckBox.Create(result.Root);
    CountryMapping.Text := 'Map Country Catalog Labels';
    CountryMapping.TextAlign := vgTextAlignNear;
    CountryMapping.Padding.Rect := vxRect(35, 5, 5, 5);
    CountryMapping.Align := vaTop;
    CountryMapping.IsChecked := ReadFromRegistry('ScriptPlacesHierarchy', 'CountryMapping', Ord(PublicOptions.XMPSyncReadSettings.GEOLevel) >= Ord(glCountry)) = 1;
    result.Root.AddObject(CountryMapping);

    StateMapping := TvxCheckBox.Create(result.Root);
    StateMapping.Text := 'Map State Catalog Labels';
    StateMapping.TextAlign := vgTextAlignNear;
    StateMapping.Padding.Rect := vxRect(45, 5, 5, 5);
    StateMapping.Align := vaTop;
    StateMapping.IsChecked := ReadFromRegistry('ScriptPlacesHierarchy', 'StateMapping', Ord(PublicOptions.XMPSyncReadSettings.GEOLevel) >= Ord(glState)) = 1;
    result.Root.AddObject(StateMapping);

    CityMapping := TvxCheckBox.Create(result.Root);
    CityMapping.Text := 'Map City Catalog Labels';
    CityMapping.TextAlign := vgTextAlignNear;
    CityMapping.Padding.Rect := vxRect(55, 5, 5, 5);
    CityMapping.Align := vaTop;
    CityMapping.IsChecked := ReadFromRegistry('ScriptPlacesHierarchy', 'CityMapping', Ord(PublicOptions.XMPSyncReadSettings.GEOLevel) >= Ord(glCity)) = 1;
    result.Root.AddObject(CityMapping);

    LocationMapping := TvxCheckBox.Create(result.Root);
    LocationMapping.Text := 'Map Location Catalog Labels';
    LocationMapping.TextAlign := vgTextAlignNear;
    LocationMapping.Padding.Rect := vxRect(65, 5, 5, 5);
    LocationMapping.Align := vaTop;
    LocationMapping.IsChecked := ReadFromRegistry('ScriptPlacesHierarchy', 'LocationMapping', Ord(PublicOptions.XMPSyncReadSettings.GEOLevel) >= Ord(glLocation)) = 1;
    result.Root.AddObject(LocationMapping);
  end;

  procedure HandlePropMapping(AProp: TCatalogItemProp; ALocationLevel: Integer);
  const
    cCountryLevel  = 0;
    cStateLevel    = 1;
    cCityLevel     = 2;
    cLocationLevel = 3;
  var
    ANewLink: WideString;
  begin
    {
    ALocationLevel:
      0 = Country Level:
        photoshop:Country,Iptc4xmpExt:LocationCreated.Iptc4xmpExt:CountryName,Iptc4xmpExt:LocationShown.Iptc4xmpExt:CountryName
      1 = State Level:
        photoshop:State,Iptc4xmpExt:LocationCreated.Iptc4xmpExt:ProvinceState,Iptc4xmpExt:LocationShown.Iptc4xmpExt:ProvinceState
      2 = City Level:
        photoshop:City,Iptc4xmpExt:LocationCreated.Iptc4xmpExt:City,Iptc4xmpExt:LocationShown.Iptc4xmpExt:City
      3 = Location Level:
        Iptc4xmpCore:Location,Iptc4xmpExt:LocationCreated.Iptc4xmpExt:Sublocation,Iptc4xmpExt:LocationShown.Iptc4xmpExt:Sublocation
    }

    ANewLink := AProp.PropXMPLink;
    if (ALocationLevel = cCountryLevel) and (CountryMapping.IsChecked) then
      ANewLink := 'photoshop:Country,Iptc4xmpExt:LocationCreated.Iptc4xmpExt:CountryName,Iptc4xmpExt:LocationShown.Iptc4xmpExt:CountryName'
    else if (ALocationLevel = cStateLevel) and (StateMapping.IsChecked) then
      ANewLink := 'photoshop:State,Iptc4xmpExt:LocationCreated.Iptc4xmpExt:ProvinceState,Iptc4xmpExt:LocationShown.Iptc4xmpExt:ProvinceState'
    else if (ALocationLevel = cCityLevel) and (CityMapping.IsChecked) then
      ANewLink := 'photoshop:City,Iptc4xmpExt:LocationCreated.Iptc4xmpExt:City,Iptc4xmpExt:LocationShown.Iptc4xmpExt:City'
    else if (ALocationLevel = cLocationLevel) and (LocationMapping.IsChecked) then
      ANewLink := 'Iptc4xmpCore:Location,Iptc4xmpExt:LocationCreated.Iptc4xmpExt:Sublocation,Iptc4xmpExt:LocationShown.Iptc4xmpExt:Sublocation';

    if not WideSameText(ANewLink, AProp.PropXMPLink) then
    begin
      ChangeList.Values[AProp.GUID] := ANewLink;
    end;
  end;

  procedure HandlePropsAtLevel(AProps: TCatalogItemProps; ALevel: Integer);
  var
    ASubs: TCatalogItemProps;
    i: Integer;
  begin
    if ALevel >= OffsetLevel.Value then
    begin
      for i := 0 to AProps.Count - 1 do
        HandlePropMapping(AProps.Items[i], ALevel - OffsetLevel.Value);
    end;

    // read subs for next level
    for i := 0 to AProps.Count - 1 do
    begin
      ASubs := TCatalogItemProps.Create(TCatalogItemProp, '');
      try
        PublicCatalog.EnumPropsForParent(AProps.Items[i].GUID, ASubs, False, False);
        HandlePropsAtLevel(ASubs, ALevel + 1);
      finally
        ASubs.Free;
      end;
    end;
  end;

  procedure HandleChangeList;
  var
    i: Integer;
    AGUID, ANewLink: WideString;
    AProps: TCatalogItemProps;
    AProp: TCatalogItemProp;

    ACheckList: TTntStringList;
    AChecked: Boolean;
    ARes: Integer;
  begin
    ACheckList := TTntStringList.Create;
    AProps := TCatalogItemProps.Create(TCatalogItemProp, '');
    try
      for i := 0 to ChangeList.Count - 1 do
      begin
        AGUID := ChangeList.Names[i];
        ANewLink := ChangeList.ValueFromIndex[i];

        AProp := TCatalogItemProp.Create(AProps);
        if Catalog.EnumProp(AGUID, AProp, False) then
        begin
          AProp.ParentXMPLinkAssign := True;
          AProp.PropXMPLink := ANewLink;
          ACheckList.AddObject('?' + PublicCatalog.PathNameForProp(AProp, '::', True), AProp);
        end
        else
          AProp.Free;
      end;

      if Ask2('Would you line to update all ' + IntToStr(ACheckList.Count) + ' found Catalog Labels?', 'select No to manually select the Catalog Labels to update') then
      begin
        ARes := mrOk;
        for i := 0 to ACheckList.Count - 1 do
          ACheckList.Strings[i] := '1' + MidStr(ACheckList.Strings[i], 2, Length(ACheckList.Strings[i]));
      end
      else
      begin
        ARes := AskCustom(
                  'Select the labels to update...',
                  '',
                  '',   // 'info text'
                  '',   // 'check this'
                  AChecked,
                  ACheckList
                );
      end;

      if ARes <> -1 then
      begin
        AHit := 0;
        for i := 0 to ACheckList.Count - 1 do
        begin
          if LeftStr(ACheckList.Strings[i], 1) = '1' then
          begin
            AProp := TCatalogItemProp(ACheckList.Objects[i]);
            PublicCatalog.UpdateProp(AProp);
            AHit := AHit + 1;
          end;
        end;

        if AHit > 0 then
        begin
          Say(IntToStr(AHit) + ' Catalog Label updated.');
        end;
      end;
    finally
      AProps.Free;
      ACheckList.Free;
    end;
  end;

var
  AProps: TCatalogItemProps;
begin
  ChangeList := TTntStringList.Create;
  ConfigForm := BuildForm;
  try
    ConfigForm.PrepareModal([mbOk, mbCancel]);
    ConfigForm.ShowModal;

    if ConfigForm.ModalResult = mrOk then
    begin
      WriteToRegistry('ScriptPlacesHierarchy', 'OffsetLevel', OffsetLevel.Value);
      WriteToRegistry('ScriptPlacesHierarchy', 'CountryMapping', CountryMapping.IsChecked);
      WriteToRegistry('ScriptPlacesHierarchy', 'StateMapping', StateMapping.IsChecked);
      WriteToRegistry('ScriptPlacesHierarchy', 'CityMapping', CityMapping.IsChecked);
      WriteToRegistry('ScriptPlacesHierarchy', 'LocationMapping', LocationMapping.IsChecked);

      AProps := TCatalogItemProps.Create(TCatalogItemProp, '');
      try
        // read first level
        PublicCatalog.EnumPropsForParent(PlacesGUID, AProps, False, False);

        ChangeCount := 0;
        HandlePropsAtLevel(AProps, 1);
      finally
        AProps.Free;
      end;

      if ChangeList.Count > 0 then
        HandleChangeList
      else
        Say('No changes needed');
    end;
  finally
    ConfigForm.Free;
    ChangeList.Free;
  end;
end;

