{
   Full Exif Dump will dump the full metadata as found in Exif. This
   could contain: technical Exif data, IPTC data, as well as XMP data

   Version 2.0; Author: IDimager Systems
                Date: 27 jun 2024
                Implemented tabs for multiple selected files

   Version 1.3; Author: IDimager Systems
                Date: 17 dec 2021
                When a sidecar XMP file exists then that is loaded as the XMP to display

   Version 1.2; Author: IDimager Systems
                Date: 18 oct 2018
                Added a save option

   Version 1.1; Author: IDimager Systems
                Date: 16 oct 2018
                Dump XMP details as separate strings

   Version 1.0; Author: IDimager Systems
                Date: 24 aug 2014

}

var
  AGrid: TvxStringGrid;
  row: Integer;

  ATabList: TStringList;
  tabcount: Integer;
  activetab: Integer;

  function TabButtonAtIndex(AIndex: Integer): TvxRectangle;
  begin
    result := ATabList.Objects[AIndex];
  end;

  function TabDataAtIndex(AIndex: Integer): TTntStringList;
  begin
    result := StrToPtrInt(TvxRectangle(TabButtonAtIndex(AIndex)).TagString);
  end;

  function TabImageAtIndex(AIndex: Integer): TImageItem;
  begin
    result := TImageItem(TvxRectangle(TabButtonAtIndex(AIndex)).TagObject);
  end;

  function ActiveTabButton: TvxRectangle;
  begin
    result := TabButtonAtIndex(activetab);
  end;

  function ActiveTabData: TTntStringList;
  begin
    result := TabDataAtIndex(activetab);
  end;

  function ActiveTabImage: TImageItem;
  begin
    result := TabImageAtIndex(activetab);
  end;

  procedure DumpExif(AImg: TImageItem; AGrid: TvxStringGrid; ADataSl: TTntStrings);
  var
    ATif: TTif;
    i: Integer;
    AXmp: TXMP;
    AXMPSidecarFileName: WideString;
    ASize: Int64;
  begin
    ATif := TTif.Create(nil);
    ATif.FileName := AImg.ExifFileName;
    ATif.Load(True, False);

    try
      ATif.DumpExif(ADataSl, [itBase, itExif, itIPTC, itInterExif, itGps, itMakerNotes], True);
    except
      ADataSl.Add('Error during reading of Exif');
    end;

    // if there's an XMP sidecar file then load that as the XMP to display
    AXMPSidecarFileName := WideChangeFileExt(AImg.ExifFileName, '.xmp');
    if WideFileExists(AXMPSidecarFileName) then
      ATif.XMPData.LoadFromFile(AXMPSidecarFileName);

    if IsAppVersion(4, 3, 0) then
    begin
      if ATif.XMPData.Count > 0 then
      begin
        AXmp := TXMP.Create(False);
        try
          AXmp.LoadFromStrings(ATif.XMPData, '');
          ADataSl.Add('=');
          AXmp.DumpToStrings(ADataSl);
        finally
          AXmp.Free;
        end;
      end;
    end;

    AGrid.Cells[0, row] := 'FileName';
    AGrid.Cells[1, row] := WideExtractFileName(ATif.FileName);
    Inc(row);
    AGrid.Cells[0, row] := 'FilePath';
    AGrid.Cells[1, row] := ATif.FileName;
    Inc(row);
    ASize := SizeOfFile(ATif.FileName);
    AGrid.Cells[0, row] := 'FileSize';
    AGrid.Cells[1, row] := IntToStr(ASize) + ' bytes (' + SizeString(ASize) + ')';
    Inc(row);
    AGrid.Cells[0, row] := 'FileStamp';
    AGrid.Cells[1, row] := FileStamp(ATif.FileName);
    Inc(row);

    Inc(row);
    for i := 0 to ADataSl.Count - 1 do
    begin
      AGrid.Cells[0, row] := ADataSl.Names[i];
      AGrid.Cells[1, row] := ADataSl.ValueFromIndex[i];
      Inc(row);
    end;

    if not IsAppVersion(4, 3, 0) then
    begin
      if ATif.XMPData.Count > 0 then
      begin
        Inc(row);
        AGrid.Cells[0, row] := 'eXtensible Metadata Platform (XMP)';
        Inc(row);
        for i := 0 to ATif.XMPData.Count -1 do
        begin
          //AGrid.Cells[0, row] := ATif.XMPData.Strings[i];
          AGrid.Cells[1, row] := ATif.XMPData.Strings[i];
          Inc(row);
        end;
      end;
    end;

    ATif.Free;
  end;

  function CheckVersion41: Boolean;
  var
    ATokens: TTntStringList;
  begin
    result := False;
    ATokens := TTntStringList.Create;
    Tokenize(xomProductVersion, '.', ATokens);
    if ATokens.Count = 4 then
    begin
      if (IntToStr(ATokens.Strings[0]) > 4) or
         (
           (IntToStr(ATokens.Strings[0]) = 4) and
           (IntToStr(ATokens.Strings[1]) >= 1)
         )
      then
        result := True;
    end;
    ATokens.Free;
  end;

  procedure ActivateTab(ATabIndex: Integer);
  begin
    TvxRectangle(ActiveTabButton).Fill.Style := vgBrushNone;

    // dump the first selected file to the grid
    AGrid.RowCount := 0;
    row := 0;
    activetab := ATabIndex;
    DumpExif(ActiveTabImage, AGrid, ActiveTabData);

    TvxRectangle(ActiveTabButton).Fill.Style := vgBrushSolid;
  end;

  procedure _SaveToFileClick(Sender: TObject);
  begin
    //SaveDialog.FileName := WideChangeFileExt(TImageItem(ActiveTabImage).FileNameOnly, '.txt');
    SaveDialog.FileName := TImageItem(ActiveTabImage).FileNameOnly + '.txt';
    if SaveDialog.Execute then
    begin
      TTntStringList(ActiveTabData).SaveToFile(SaveDialog.FileName);
      Say('Saved as ' + SaveDialog.FileName);
    end;
  end;

  procedure _TabButtonClick(Sender: TObject);
  var
    ATabButton: TvxRectangle;
  begin
    ATabButton := TvxRectangle(Sender);

    ActivateTab(ATabButton.Tag);
  end;

  function CreateTab(AImg: TImageItem; ATabBar: TvxScrollBox): TvxRectangle;
  var
    ATabButton: TvxRectangle;
    ALabel: TvxLabel;
  begin
    ATabButton := TvxRectangle.Create(ATabBar);
    ATabButton.Align := vaLeft;
    ATabButton.Padding.Rect := vgRect(0, 5, 0, 5);
    ALabel := TvxLabel.Create(ATabButton);
    ALabel.Align := vaClient;
    ALabel.Text := AImg.FileNameOnly;
    ATabButton.AddObject(ALabel);

    ATabButton.xRadius := 5;
    ATabButton.yRadius := ATabButton.xRadius;
    ATabButton.Corners := [vgCornerTopLeft, vgCornerTopRight];

    ATabButton.Fill.Color := '#80' + MidStr(GUIDesign.HighlightFill.Color, 4, Length(GUIDesign.HighlightFill.Color));
    ATabButton.Fill.Style := vgBrushNone;
    ATabButton.Stroke.Color := GUIDesign.DarkFill.Color;
    ATabButton.Stroke.Style := vgBrushSolid;

    ATabButton.Width := vgTextWidth(ALabel.Text, ALabel.Font, vgNormal) + 16;

    ATabButton.OnClick := '_TabButtonClick';

    ATabButton.TagString := PtrIntToStr(TTntStringList.Create);
    ATabButton.TagObject := AImg;
    ATabButton.Tag := tabcount;
    Inc(tabcount);

    ATabBar.AddObject(ATabButton);

    result := ATabButton;
  end;

// MAIN
var
  i: Integer;
  AForm: TvxSceneForm;
  ACol: TvxStringColumn;
  AButton: TvxButton;

  ATabBar: TvxScrollBox;
  ATab: TvxRectangle;
begin
  if not CheckVersion41 then
  begin
    Say('This script requires Photo Supreme 4.1 or higher');
    exit;
  end;

  if Selected.Count = 0 then
  begin
    Say ('No image selected');
    exit;
  end;

  AForm := TvxSceneForm.Create(nil);
  if Selected.Count = 1 then
    AForm.Caption := UTF8Encode(WideFormat('Full Exif Dump  [%s]', [WideExtractFileName(Selected.Items[0].ExifFileName)]))
  else
    AForm.Caption := IntToStr(Selected.Count) + ' images';
  AForm.Width := 520 * AForm.DPIFactor;
  AForm.Height := Trunc(Screen.Height * 0.6);
  AForm.BorderStyle := bsSizeable;

  ATabBar := TvxScrollBox.Create(AForm.Root);
  ATabBar.Height := 40;
  ATabBar.Align := vaTop;
  ATabBar.UseSmallScrollBars := True;
  AForm.Root.AddObject(ATabBar);

  AGrid := TvxStringGrid.Create(AForm.Root);
  AForm.Root.AddObject(AGrid);
  AGrid.Align := vaClient;
  AGrid.RowCount := 0;

  ACol := TvxStringColumn.Create(AGrid);
  ACol.Header := 'Tag';
  ACol.Width := 200;
  AGrid.AddObject(ACol);
  ACol := TvxStringColumn.Create(AGrid);
  ACol.Header := 'Value';
  ACol.Width := 300;
  AGrid.AddObject(ACol);

  ATabList := TStringList.Create;

  for i := 0 to Selected.Count - 1 do
  begin
    ATab := CreateTab(Selected.Items[i], ATabBar);
    ATabList.AddObject(Selected.Items[i].GUID, ATab);
  end;

  tabcount := 0;
  activetab := 0;

  ActivateTab(activetab);

  AForm.PrepareModal([mbOK]);

  AButton := TvxButton.Create(AForm.ModalPanel)
  AButton.Text := 'Save as text file';
  AButton.Align := vaLeft;
  AButton.Width := 110;
  AButton.Padding.Rect := vxRect(5, 8, 5, 8);
  AButton.OnClick := '_SaveToFileClick';
  AForm.ModalPanel.AddObject(AButton);

  AForm.ShowModal;

  for i := 0 to ATabList.Count - 1 do
  begin
    TTntStringList(StrToPtrInt(TvxRectangle(ATabList.Objects[i]).TagString)).Free;
  end;

  ATabList.Free;

  AForm.Free;
end;
