  function IsMatchingMask(AStr, AMask: WideString): Boolean;
  begin
    result := WideMatchesMask(StrTran(AStr, ':', '_'), StrTran(AMask, ':', '_'));
  end;

  procedure DumpParamToStrings(AParam: TMacroParam; AResult: TTntStrings; APrefix: WideString);
  var
    i: Integer;
  begin
    if (AParam.ParamType = ptConstant) or
       (AParam.ParamType = ptVariable) or
       (AParam.ParamType = ptSequence) or
       (AParam.ParamType = ptBag) or
       (AParam.ParamType = ptAlternative)
    then
      AResult.AddObject(APrefix + iif(APrefix = '', '', '.') + AParam.ParamCommand, AParam)
    else if AParam.ParamType = ptStructure then
    begin
      APrefix := APrefix + iif(APrefix = '', '', '.') + AParam.ParamCommand;
      for i := 0 to AParam.CustomParams.Count - 1 do
        DumpParamToStrings(AParam.CustomParams.Items[i], AResult, APrefix);
    end;

    for i := 0 to AParam.SubParams.Count - 1 do
      DumpParamToStrings(AParam.SubParams.Items[i], AResult, APrefix);
  end;

  procedure DumpXMPParamsToStrings(AXmp: TXMP; AResult: TTntStrings);
  var
    i: Integer;
  begin
    for i := 0 to AXmp.XMPDesign.Params.Count - 1 do
      DumpParamToStrings(AXmp.XMPDesign.Params.Items[i], AResult, '');
  end;

  function RemoveProperties(AXmp: TXMP; AXMPProps: TTntStrings): Boolean;
  var
    AXmpDump: TTntStringList;
    i, j: Integer;
    AParamCommand: WideString;
    AParam: TMacroCommand;
    AMatchList: TxomList;
  begin
    result := False;

    AMatchList := TxomList.Create;
    AXmpDump := TTntStringList.Create;
    try
      DumpXMPParamsToStrings(AXmp, AXmpDump);

      for i := 0 to AXmpDump.Count - 1 do
      begin
        AParamCommand := AXmpDump.Strings[i];
        AParam := AXmpDump.Objects[i];

        for j := 0 to AXMPProps.Count - 1 do
        begin
          if IsMatchingMask(AParamCommand, AXMPProps.Strings[j]) then
          begin
            AMatchList.Add(AParam);
            break;
          end;
        end;
      end;

      if AMatchList.Count > 0 then
      begin
        result := True;
        for i := 0 to AMatchList.Count - 1 do
        begin
          AParam := AMatchList.Items[i];
          AParam.Free;
        end;
      end;
    finally
      AXmpDump.Free;
      AMatchList.Free;
    end;
  end;

  function HandleImage(AItem: TImageItem; AXMPProps: TTntStrings): Boolean;
  var
    ACatItem: TCatalogItem;
    AXmp: TXMP;
  begin
    {
      1. Load XMP
      2. Iterate over all properties and remove matches
      3. If handled, save XMP
    }

    result := False;
    ACatItem := TCatalogItem.Create(nil);
    try
      if PublicCatalog.FindImageCombined(AItem, ACatItem, False, vptNone) then
      begin
        AXmp := TXMP.Create(False);
        try
          PublicCatalog.LoadXMPForItem(ACatItem, AXmp, PublicOptions.CachedXMP);

          if RemoveProperties(AXmp, AXMPProps) then
          begin
            // save the XMP again
            PublicCatalog.SaveXMPForItem(ACatItem, AXmp, PublicOptions.CachedXMP);
            result := True;
          end;
        finally
          AXmp.Free;
        end;
      end;
    finally
      ACatItem.Free;
    end;
  end;

var
  AXMPProps: TTntStringList;
  AProperties: WideString;
  AHit: Integer;
begin
  if Selected.Count = 0 then
  begin
    Say('Select at least one file to remove XMP properties from.');
    exit;
  end;

  AProperties := ReadFromRegistry('Scripts\RemoveXMPProp', 'Props', 'crs:Table_*');
  if not AskInput('Remove XMP Properties', 'Enter what XMP properties to remove', 'You can use * (asterisk) as a joker. Comma separate multiple entries.', AProperties, False, nil) then
    exit;

  if Trim(AProperties) = '' then
  begin
    Say('No properties entered');
    exit;
  end;

  if not AskYN('Are you sure that you want to remove these XMP properties for all ' + IntToStrW(Selected.Count) + ' selected images?' + CrLf2W + AProperties) then
    exit;

  WriteToRegistry('Scripts\RemoveXMPProp', 'Props', AProperties);
  AXMPProps := TTntStringList.Create;
  try
    Tokenize(AProperties, ',', AXmpProps);

    AHit := 0;
    for i := 0 to Selected.Count - 1 do
    begin
      if HandleImage(Selected.Items[i], AXmpProps) then
        AHit := AHit + 1;
    end;
  finally
    AXMPProps.Free;
  end;

  if AHit > 0 then
    Say('Removed the properties for ' + IntToStrW(AHit) + ' images.')
  else
    Say('No matching images found with matching properties in the XMP');
end;

