unit uAttachment;
{$I ..\..\..\Lib\PDFiumVcl.inc}

interface

uses
{$IFDEF XE2+}
  Winapi.Windows,
  Winapi.Messages,
  Winapi.ShellAPI,
  System.SysUtils,
  System.Classes,
  System.UITypes,
  Vcl.Graphics,
  Vcl.Controls,
  Vcl.Forms,
  Vcl.Dialogs,
  Vcl.StdCtrls,
  Vcl.ExtCtrls,
  Vcl.ComCtrls,
{$ELSE}
  Windows,
  ShellAPI,
  Messages,
  SysUtils,
  Classes,
  Graphics,
  Controls,
  Forms,
  Dialogs,
  StdCtrls,
  ExtCtrls,
  ComCtrls,
{$ENDIF}
  PDFium;

type
  TFormMain= class(TForm)
    // PDFium Component
    Pdf: TPdf;

    // Dialogs
    OpenDialog: TOpenDialog;
    SaveDialog: TSaveDialog;

    // UI Layout
    PanelTop: TPanel;
    PanelMiddle: TPanel;
    PanelBottom: TPanel;

    // Top Panel - Document Operations
    GroupBoxDocument: TGroupBox;
    ButtonNewDocument: TButton;
    ButtonLoadDocument: TButton;
    ButtonSaveDocument: TButton;
    LabelDocumentStatus: TLabel;

    // Middle Panel - Attachment Operations
    GroupBoxAttachments: TGroupBox;
    ButtonAddAttachment: TButton;
    ButtonExtractAttachment: TButton;
    ButtonDeleteAttachment: TButton;
    ButtonRefreshAttachments: TButton;

    // Attachment List
    ListBoxAttachments: TListBox;
    LabelAttachmentCount: TLabel;

    // Bottom Panel - Status and Info
    MemoStatus: TMemo;
    lnkLabel: TLinkLabel;

    // Event Handlers
    procedure FormCreate(Sender: TObject);
    procedure ButtonNewDocumentClick(Sender: TObject);
    procedure ButtonLoadDocumentClick(Sender: TObject);
    procedure ButtonSaveDocumentClick(Sender: TObject);
    procedure ButtonAddAttachmentClick(Sender: TObject);
    procedure ButtonExtractAttachmentClick(Sender: TObject);
    procedure ButtonDeleteAttachmentClick(Sender: TObject);
    procedure ButtonRefreshAttachmentsClick(Sender: TObject);
    procedure ListBoxAttachmentsClick(Sender: TObject);
    procedure lnkLabelLinkClick(
      Sender    : TObject;
      const Link: string;
      LinkType  : TSysLinkType);

  private
    procedure LogStatus(const Message: string);
    procedure RefreshAttachmentList;
    procedure UpdateUI;
    function GetSelectedAttachmentIndex: Integer;

  public
    { Public declarations }
  end;

var
  FormMain: TFormMain;

implementation

{$R *.dfm}

procedure TFormMain.FormCreate(Sender: TObject);
begin
  // Initialize UI
  Caption:= 'PDF Attachment Demo - PDFium VCL';

  // Setup status memo
  MemoStatus.ScrollBars:= ssVertical;
  MemoStatus.ReadOnly:= True;

  // Log initial message
  LogStatus('PDF Attachment Demo initialized.');
  LogStatus('You can create a new PDF document or load an existing one.');
  LogStatus('');

  UpdateUI;
end;

procedure TFormMain.ButtonNewDocumentClick(Sender: TObject);
begin
  try
    Pdf.CreateDocument;
    // Explicitly activate and add the first page (A4) since CreateDocument no longer creates a default page
    Pdf.Active:= True;
    Pdf.AddPage(1, 595, 842);
    Pdf.PageNumber:= 1;
    LogStatus('New PDF document created successfully.');
    LabelDocumentStatus.Caption:= 'Status: New Document';
    RefreshAttachmentList;
  except
    on E: Exception do
    begin
      LogStatus('Error creating new document: '+ E.Message);
    end;
  end;
  UpdateUI;
end;

procedure TFormMain.ButtonLoadDocumentClick(Sender: TObject);
begin
  OpenDialog.Filter:= 'PDF Files|*.pdf|All Files|*.*';
  OpenDialog.DefaultExt:= 'pdf';

  if OpenDialog.Execute
  then
  begin
    try
      // Close current document if active
      if Pdf.Active
      then
        Pdf.Active:= False;

      Pdf.FileName:= OpenDialog.FileName;
      Pdf.Active:= True;
      LogStatus('PDF document loaded successfully: '+ ExtractFileName(OpenDialog.FileName));
      LabelDocumentStatus.Caption:= 'Status: '+ ExtractFileName(OpenDialog.FileName);
      RefreshAttachmentList;
    except
      on E: Exception do
      begin
        LogStatus('Error loading document: '+ E.Message);
      end;
    end;
  end;
  UpdateUI;
end;

procedure TFormMain.ButtonSaveDocumentClick(Sender: TObject);
var
  CurrentFileName: string;
  TempFileName: string;
  WasActive: Boolean;
begin
  SaveDialog.Filter:= 'PDF Files|*.pdf|All Files|*.*';
  SaveDialog.DefaultExt:= 'pdf';

  if SaveDialog.Execute
  then
  begin
    try
      CurrentFileName:= Pdf.FileName;
      WasActive:= Pdf.Active;

      // If saving to the same file that's currently loaded, use a temporary file approach
      if WasActive and SameText(CurrentFileName, SaveDialog.FileName)
      then
      begin
        LogStatus('Saving to same file, using temporary file approach...');
        TempFileName:= SaveDialog.FileName+ '.tmp';

        // Save to temporary file first
        if Pdf.SaveAs(TempFileName)
        then
        begin
          // Close current document to release file lock
          Pdf.Active:= False;

          // Replace original file with temporary file
          if FileExists(SaveDialog.FileName)
          then
            DeleteFile(SaveDialog.FileName);

          if RenameFile(TempFileName, SaveDialog.FileName)
          then
          begin
            LogStatus('PDF document saved successfully: '+ ExtractFileName(SaveDialog.FileName));

            // Reload the document
            Pdf.FileName:= SaveDialog.FileName;
            Pdf.Active:= True;
            LogStatus('Document reloaded after saving.');
            RefreshAttachmentList;
          end
          else
          begin
            LogStatus('Failed to replace original file after saving.');
            // Try to restore from temp file
            if FileExists(TempFileName)
            then
              RenameFile(TempFileName, SaveDialog.FileName);
          end;
        end
        else
        begin
          LogStatus('Failed to save to temporary file.');
        end;
      end
      else
      begin
        // Normal save to different file
        if Pdf.SaveAs(SaveDialog.FileName)
        then
        begin
          LogStatus('PDF document saved successfully: '+ ExtractFileName(SaveDialog.FileName));
        end
        else
        begin
          LogStatus('Failed to save PDF document.');
        end;
      end;
    except
      on E: Exception do
      begin
        LogStatus('Error saving document: '+ E.Message);

        // Clean up temporary file if it exists
        if TempFileName<> ''
        then
        begin
          if FileExists(TempFileName)
          then
          begin
            try
              DeleteFile(TempFileName);
            except
              // Ignore cleanup errors
            end;
          end;
        end;
      end;
    end;
  end;
  UpdateUI;
end;

procedure TFormMain.ButtonAddAttachmentClick(Sender: TObject);
var
  AttachmentName: string;
  FileData: TBytes;
  FileStream: TFileStream;
begin
  if not Pdf.Active
  then
  begin
    LogStatus('No PDF document is loaded. Please create or load a document first.');
    Exit;
  end;

  OpenDialog.Filter:= 'All Files|*.*';
  OpenDialog.Title:= 'Select File to Attach';

  if OpenDialog.Execute
  then
  begin
    try
      // Get attachment name
      AttachmentName:= ExtractFileName(OpenDialog.FileName);

      // Read file data
      FileStream:= TFileStream.Create(OpenDialog.FileName, fmOpenRead);
      try
        SetLength(FileData, FileStream.Size);
        if FileStream.Size> 0
        then
          FileStream.ReadBuffer(FileData[0], FileStream.Size);
      finally
        FileStream.Free;
      end;

      // Create attachment in PDF
      if Pdf.CreateAttachment(AttachmentName)
      then
      begin
        // Set attachment data
        Pdf.Attachment[Pdf.AttachmentCount- 1]:= FileData;

        LogStatus('File attached successfully: '+ AttachmentName+ ' ('+ IntToStr(Length(FileData))+ ' bytes)');
        RefreshAttachmentList;
      end
      else
      begin
        LogStatus('Failed to create attachment: '+ AttachmentName);
      end;

    except
      on E: Exception do
      begin
        LogStatus('Error adding attachment: '+ E.Message);
      end;
    end;
  end;
  UpdateUI;
end;

procedure TFormMain.ButtonExtractAttachmentClick(Sender: TObject);
var
  Index: Integer;
  AttachmentName: string;
  AttachmentData: TBytes;
  FileStream: TFileStream;
begin
  Index:= GetSelectedAttachmentIndex;
  if Index< 0
  then
  begin
    LogStatus('Please select an attachment to extract.');
    Exit;
  end;

  try
    AttachmentName:= Pdf.AttachmentName[Index];
    AttachmentData:= Pdf.Attachment[Index];

    if Length(AttachmentData)= 0
    then
    begin
      LogStatus('Selected attachment has no data or cannot be read.');
      Exit;
    end;

    SaveDialog.Filter:= 'All Files|*.*';
    SaveDialog.FileName:= AttachmentName;
    SaveDialog.Title:= 'Save Attachment As';

    if SaveDialog.Execute
    then
    begin
      FileStream:= TFileStream.Create(SaveDialog.FileName, fmCreate);
      try
        FileStream.WriteBuffer(AttachmentData[0], Length(AttachmentData));
        LogStatus('Attachment extracted successfully: '+ ExtractFileName(SaveDialog.FileName)+ ' ('+ IntToStr(Length(AttachmentData))+ ' bytes)');
      finally
        FileStream.Free;
      end;
    end;

  except
    on E: Exception do
    begin
      LogStatus('Error extracting attachment: '+ E.Message);
    end;
  end;
end;

procedure TFormMain.ButtonDeleteAttachmentClick(Sender: TObject);
var
  Index: Integer;
  AttachmentName: string;
begin
  Index:= GetSelectedAttachmentIndex;
  if Index< 0
  then
  begin
    LogStatus('Please select an attachment to delete.');
    Exit;
  end;

  try
    AttachmentName:= Pdf.AttachmentName[Index];

    if MessageDlg('Are you sure you want to delete the attachment "'+ AttachmentName+ '"?', mtConfirmation, [mbYes, mbNo], 0)= mrYes
    then
    begin
      if Pdf.DeleteAttachment(Index)
      then
      begin
        LogStatus('Attachment deleted successfully: '+ AttachmentName);
        RefreshAttachmentList;
      end
      else
      begin
        LogStatus('Failed to delete attachment: '+ AttachmentName);
      end;
    end;

  except
    on E: Exception do
    begin
      LogStatus('Error deleting attachment: '+ E.Message);
    end;
  end;
  UpdateUI;
end;

procedure TFormMain.ButtonRefreshAttachmentsClick(Sender: TObject);
begin
  RefreshAttachmentList;
end;

procedure TFormMain.lnkLabelLinkClick(
  Sender    : TObject;
  const Link: string;
  LinkType  : TSysLinkType);
begin
  // Open the link in default browser
  ShellExecute(Handle, 'open', PChar(Link), nil, nil, SW_SHOWNORMAL);
end;

procedure TFormMain.ListBoxAttachmentsClick(Sender: TObject);
var
  Index: Integer;
  AttachmentName: string;
  AttachmentData: TBytes;
begin
  Index:= GetSelectedAttachmentIndex;
  if Index>= 0
  then
  begin
    try
      AttachmentName:= Pdf.AttachmentName[Index];
      AttachmentData:= Pdf.Attachment[Index];

      LogStatus('Selected attachment: '+ AttachmentName+ ' ('+ IntToStr(Length(AttachmentData))+ ' bytes)');
    except
      on E: Exception do
      begin
        LogStatus('Error reading attachment info: '+ E.Message);
      end;
    end;
  end;
  UpdateUI;
end;

procedure TFormMain.LogStatus(const Message: string);
begin
  MemoStatus.Lines.Add(FormatDateTime('hh:nn:ss', Now)+ ' - '+ Message);

  // Auto-scroll to bottom
  SendMessage(MemoStatus.Handle, WM_VSCROLL, SB_BOTTOM, 0);
end;

procedure TFormMain.RefreshAttachmentList;
var
  I: Integer;
  AttachmentCount: Integer;
  AttachmentName: string;
begin
  ListBoxAttachments.Items.Clear;

  if not Pdf.Active
  then
  begin
    LabelAttachmentCount.Caption:= 'Attachments: No document loaded';
    UpdateUI;
    Exit;
  end;

  try
    AttachmentCount:= Pdf.AttachmentCount;
    LabelAttachmentCount.Caption:= 'Attachments: '+ IntToStr(AttachmentCount);

    if AttachmentCount> 0
    then
    begin
      LogStatus('Found '+ IntToStr(AttachmentCount)+ ' attachment(s) in the document.');

      for I:= 0 to AttachmentCount- 1 do
      begin
        try
          AttachmentName:= Pdf.AttachmentName[I];
          ListBoxAttachments.Items.Add(IntToStr(I+ 1)+ '. '+ AttachmentName);
        except
          on E: Exception do
          begin
            ListBoxAttachments.Items.Add(IntToStr(I+ 1)+ '. <Error reading name>');
            LogStatus('Error reading attachment name at index '+ IntToStr(I)+ ': '+ E.Message);
          end;
        end;
      end;
    end
    else
    begin
      LogStatus('No attachments found in the document.');
    end;

  except
    on E: Exception do
    begin
      LogStatus('Error refreshing attachment list: '+ E.Message);
      LabelAttachmentCount.Caption:= 'Attachments: Error';
    end;
  end;

  UpdateUI;
end;

procedure TFormMain.UpdateUI;
var
  HasDocument: Boolean;
  HasAttachments: Boolean;
  HasSelection: Boolean;
begin
  HasDocument:= Pdf.Active;
  HasAttachments:= HasDocument and (Pdf.AttachmentCount> 0);
  HasSelection:= HasAttachments and (ListBoxAttachments.ItemIndex>= 0);

  // Document operations
  ButtonSaveDocument.Enabled:= HasDocument;

  // Attachment operations
  ButtonAddAttachment.Enabled:= HasDocument;
  ButtonRefreshAttachments.Enabled:= HasDocument;
  ButtonExtractAttachment.Enabled:= HasSelection;
  ButtonDeleteAttachment.Enabled:= HasSelection;

  // List box
  ListBoxAttachments.Enabled:= HasDocument;
end;

function TFormMain.GetSelectedAttachmentIndex: Integer;
begin
  Result:= ListBoxAttachments.ItemIndex;
end;

end.
