unit uPrintPDF;

{$I ..\..\..\Lib\PDFiumVcl.inc}

interface

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

type
  // Print quality enumeration
  TPrintQuality= (pqDraft, pqNormal, pqHigh);

  // Print layout enumeration
  TPrintLayout= (plPortrait, plLandscape, plAuto);

  // Page scaling enumeration
  TPageScaling= (psNone, psFitToPage, psShrinkToFit, psCustom);

  TFormMain= class(TForm)
    // Main panels
    pnlMain: TPanel;
    pnlPreview: TPanel;
    pnlControls: TPanel;
    pnlStatus: TPanel;

    // File selection group
    grpFileSelection: TGroupBox;
    lblPdfFile: TLabel;
    edtPdfFile: TEdit;
    btnBrowse: TSpeedButton;
    lstRecentFiles: TListBox;
    lblRecentFiles: TLabel;

    // Print options group
    grpPrintOptions: TGroupBox;
    lblPageRange: TLabel;
    cmbPageRange: TComboBox;
    edtFromPage: TEdit;
    edtToPage: TEdit;
    lblTo: TLabel;
    lblCopies: TLabel;
    edtCopies: TEdit;
    udCopies: TUpDown;
    chkCollate: TCheckBox;

    // Layout options group
    grpLayoutOptions: TGroupBox;
    lblLayout: TLabel;
    rbPortrait: TRadioButton;
    rbLandscape: TRadioButton;
    rbAutoRotate: TRadioButton;
    lblScaling: TLabel;
    cmbScaling: TComboBox;
    lblCustomScale: TLabel;
    edtCustomScale: TEdit;
    udCustomScale: TUpDown;
    chkDuplex: TCheckBox;

    // Quality options group
    grpQualityOptions: TGroupBox;
    lblQuality: TLabel;
    cmbQuality: TComboBox;
    lblDPI: TLabel;
    edtDPI: TEdit;
    udDPI: TUpDown;
    chkGrayscale: TCheckBox;

    // Preview area
    grpPreview: TGroupBox;
    pdfPreview: TPdfView;
    lblPreviewPage: TLabel;
    edtPreviewPage: TEdit;
    udPreviewPage: TUpDown;
    btnFirstPage: TButton;
    btnPrevPage: TButton;
    btnNextPage: TButton;
    btnLastPage: TButton;
    lblZoom: TLabel;
    cmbZoom: TComboBox;

    // Control buttons
    btnPrint: TButton;
    btnPageSetup: TButton;
    btnCancel: TButton;
    btnExit: TButton;

    // Progress and status
    lblStatus: TLabel;
    progressPrint: TProgressBar;
    lblPageInfo: TLabel;

    // Components
    Pdf: TPdf;
    OpenDialog: TOpenDialog;
    PrintDialog: TPrintDialog;
    PageSetupDialog: TPageSetupDialog;
    PopupMenu: TPopupMenu;
    mnuOpenFile: TMenuItem;
    mnuClearRecent: TMenuItem;
    mnuSeparator: TMenuItem;
    mnuExit: TMenuItem;

    // Timer for auto-save settings
    tmrAutoSave: TTimer;
    lblLink: TLabel;

    // Event handlers
    procedure FormCreate(Sender: TObject);
    procedure FormDestroy(Sender: TObject);
    procedure FormShow(Sender: TObject);
    procedure FormClose(
      Sender    : TObject;
      var Action: TCloseAction);

    // File operations
    procedure btnBrowseClick(Sender: TObject);
    procedure edtPdfFileChange(Sender: TObject);
    procedure lstRecentFilesClick(Sender: TObject);
    procedure lstRecentFilesDblClick(Sender: TObject);
    procedure mnuOpenFileClick(Sender: TObject);
    procedure mnuClearRecentClick(Sender: TObject);

    // Print operations
    procedure btnPrintClick(Sender: TObject);
    procedure btnPageSetupClick(Sender: TObject);
    procedure btnCancelClick(Sender: TObject);

    // Link operations
    procedure lblLinkClick(Sender: TObject);
    procedure lblLinkMouseEnter(Sender: TObject);
    procedure lblLinkMouseLeave(Sender: TObject);

    // Preview operations
    procedure UpdatePreview;
    procedure btnFirstPageClick(Sender: TObject);
    procedure btnPrevPageClick(Sender: TObject);
    procedure btnNextPageClick(Sender: TObject);
    procedure btnLastPageClick(Sender: TObject);
    procedure edtPreviewPageChange(Sender: TObject);
    procedure cmbZoomChange(Sender: TObject);

    // Settings change handlers
    procedure cmbPageRangeChange(Sender: TObject);
    procedure edtFromPageChange(Sender: TObject);
    procedure edtToPageChange(Sender: TObject);
    procedure cmbScalingChange(Sender: TObject);
    procedure edtCustomScaleChange(Sender: TObject);
    procedure cmbQualityChange(Sender: TObject);
    procedure edtDPIChange(Sender: TObject);

    // UI handlers
    procedure btnExitClick(Sender: TObject);
    procedure tmrAutoSaveTimer(Sender: TObject);

    // Drag and drop support
    procedure FormDropFiles(
      Sender         : TObject;
      const FileNames: array of string);

  private
    FProcessing: Boolean;
    FCancelled: Boolean;
    FSettingsFile: string;
    FRecentFiles: TStringList;
    FCurrentPreviewPage: Integer;
    FCurrentZoom: Integer;
    FPreviewBitmap: TBitmap;

    // Private methods
    procedure LoadSettings;
    procedure SaveSettings;
    procedure LoadRecentFiles;
    procedure SaveRecentFiles;
    procedure AddToRecentFiles(const FileName: string);
    procedure UpdateRecentFilesList;
    procedure EnableDisableControls(Enabled: Boolean);
    procedure UpdateUI;
    procedure UpdatePageInfo;
    procedure UpdatePreviewControls;
    procedure ValidatePageRange;
    procedure LogMessage(
      const Msg  : string;
      const Level: string= 'INFO');
    procedure ShowError(const Msg: string);
    procedure PrintPDFPages;

  public
    procedure LoadPDFFile(const FileName: string);
  end;

var
  FormMain: TFormMain;

implementation

{$R *.dfm}

uses
{$IFDEF XE2+}
  Vcl.Printers,
  System.Math,
  System.Win.Registry
{$ELSE}
    Printers,
  Math,
  Registry
{$ENDIF};

const
  MAX_RECENT_FILES= 10;
  DEFAULT_DPI= 300;
  MIN_ZOOM= 25;
  MAX_ZOOM= 500;

  { TFormMain }

procedure TFormMain.FormCreate(Sender: TObject);
begin
  FProcessing:= False;
  FCancelled:= False;
  FCurrentPreviewPage:= 1;
  FCurrentZoom:= 100;
  FPreviewBitmap:= TBitmap.Create;
  FRecentFiles:= TStringList.Create;

  // Set up settings file path
  FSettingsFile:= ChangeFileExt(Application.ExeName, '.ini');

  // Initialize UI
  cmbPageRange.ItemIndex:= 0; // All pages
  cmbScaling.ItemIndex:= 1; // Fit to page
  cmbQuality.ItemIndex:= 1; // Normal quality
  cmbZoom.ItemIndex:= 3; // 100%

  edtCopies.Text:= '1';
  udCopies.Position:= 1;
  edtCustomScale.Text:= '100';
  udCustomScale.Position:= 100;
  edtDPI.Text:= IntToStr(DEFAULT_DPI);
  udDPI.Position:= DEFAULT_DPI;

  // TODO: Enable drag and drop (requires additional implementation)
  // AllowDropFiles := True;
  // OnDropFiles := FormDropFiles;

  // Load settings and recent files
  LoadSettings;
  LoadRecentFiles;
  UpdateRecentFilesList;
  UpdateUI;
end;

procedure TFormMain.FormDestroy(Sender: TObject);
begin
  SaveSettings;
  SaveRecentFiles;

  FPreviewBitmap.Free;
  FRecentFiles.Free;
end;

procedure TFormMain.FormShow(Sender: TObject);
begin
  UpdateUI;
  // Ensure TPdfView is properly initialized
  if Pdf.Active and (Pdf.PageCount> 0)
  then
  begin
    pdfPreview.Active:= True;
    pdfPreview.PageNumber:= FCurrentPreviewPage;
    pdfPreview.Zoom:= FCurrentZoom/ 100.0;
    pdfPreview.Invalidate;
  end;
end;

procedure TFormMain.FormClose(
  Sender    : TObject;
  var Action: TCloseAction);
begin
  if FProcessing
  then
  begin
    if MessageDlg('Printing is in progress. Cancel printing and exit?', mtConfirmation, [mbYes, mbNo], 0)= mrYes
    then
    begin
      FCancelled:= True;
      Action:= caFree;
    end
    else
      Action:= caNone;
  end;
end;

procedure TFormMain.btnBrowseClick(Sender: TObject);
begin
  OpenDialog.FileName:= edtPdfFile.Text;
  if OpenDialog.Execute
  then
  begin
    LoadPDFFile(OpenDialog.FileName);
  end;
end;

procedure TFormMain.LoadPDFFile(const FileName: string);
begin
  if not TFile.Exists(FileName)
  then
  begin
    ShowError('File not found: '+ FileName);
    Exit;
  end;

  try
    edtPdfFile.Text:= FileName;
    AddToRecentFiles(FileName);

    // Load PDF for preview and printing
    Pdf.Active:= False;
    Pdf.FileName:= FileName;
    Pdf.Active:= True;

    if Pdf.PageCount> 0
    then
    begin
      FCurrentPreviewPage:= 1;
      udPreviewPage.Max:= Pdf.PageCount;
      edtPreviewPage.Text:= '1';

      // Activate TPdfView to display PDF
      pdfPreview.Active:= True;

      // Update preview display
      UpdatePreview;
      UpdatePageInfo;
      UpdateUI;

      LogMessage('PDF loaded successfully: '+ ExtractFileName(FileName)+ ' ('+ IntToStr(Pdf.PageCount)+ ' pages)');
    end
    else
    begin
      ShowError('Invalid PDF file or file is corrupted.');
    end;

  except
    on E: Exception do
    begin
      ShowError('Error loading PDF file: '+ E.Message);
    end;
  end;
end;

procedure TFormMain.edtPdfFileChange(Sender: TObject);
begin
  UpdateUI;
end;

procedure TFormMain.lstRecentFilesClick(Sender: TObject);
begin
  if (lstRecentFiles.ItemIndex>= 0)and (lstRecentFiles.ItemIndex< FRecentFiles.Count)
  then
  begin
    edtPdfFile.Text:= FRecentFiles[lstRecentFiles.ItemIndex];
  end;
end;

procedure TFormMain.lstRecentFilesDblClick(Sender: TObject);
begin
  if (lstRecentFiles.ItemIndex>= 0)and (lstRecentFiles.ItemIndex< FRecentFiles.Count)
  then
  begin
    LoadPDFFile(FRecentFiles[lstRecentFiles.ItemIndex]);
  end;
end;

procedure TFormMain.UpdatePreview;
begin
  if not Pdf.Active or (Pdf.PageCount= 0)
  then
    Exit;

  try
    // Ensure TPdfView is active before updating
    if not pdfPreview.Active
    then
      pdfPreview.Active:= True;

    // Update TPdfView display
    pdfPreview.PageNumber:= FCurrentPreviewPage;
    pdfPreview.Zoom:= FCurrentZoom/ 100.0; // Convert percentage to ratio
    pdfPreview.Invalidate; // Force repaint
    UpdatePreviewControls;

  except
    on E: Exception do
    begin
      LogMessage('Error updating preview: '+ E.Message, 'ERROR');
    end;
  end;
end;

procedure TFormMain.btnFirstPageClick(Sender: TObject);
begin
  if Pdf.Active and (Pdf.PageCount> 0)
  then
  begin
    FCurrentPreviewPage:= 1;
    edtPreviewPage.Text:= IntToStr(FCurrentPreviewPage);
    UpdatePreview;
  end;
end;

procedure TFormMain.btnPrevPageClick(Sender: TObject);
begin
  if Pdf.Active and (FCurrentPreviewPage> 1)
  then
  begin
    Dec(FCurrentPreviewPage);
    edtPreviewPage.Text:= IntToStr(FCurrentPreviewPage);
    UpdatePreview;
  end;
end;

procedure TFormMain.btnNextPageClick(Sender: TObject);
begin
  if Pdf.Active and (FCurrentPreviewPage< Pdf.PageCount)
  then
  begin
    Inc(FCurrentPreviewPage);
    edtPreviewPage.Text:= IntToStr(FCurrentPreviewPage);
    UpdatePreview;
  end;
end;

procedure TFormMain.btnLastPageClick(Sender: TObject);
begin
  if Pdf.Active and (Pdf.PageCount> 0)
  then
  begin
    FCurrentPreviewPage:= Pdf.PageCount;
    edtPreviewPage.Text:= IntToStr(FCurrentPreviewPage);
    UpdatePreview;
  end;
end;

procedure TFormMain.edtPreviewPageChange(Sender: TObject);
var
  PageNum: Integer;
begin
  if TryStrToInt(edtPreviewPage.Text, PageNum)and Pdf.Active
  then
  begin
    if (PageNum>= 1)and (PageNum<= Pdf.PageCount)
    then
    begin
      FCurrentPreviewPage:= PageNum;
      UpdatePreview;
    end;
  end;
end;

procedure TFormMain.cmbZoomChange(Sender: TObject);
var
  ZoomText: string;
  ZoomValue: Integer;
begin
  ZoomText:= cmbZoom.Text;

  // Remove % sign if present
  if Pos('%', ZoomText)> 0
  then
    ZoomText:= StringReplace(ZoomText, '%', '', [rfReplaceAll]);

  if TryStrToInt(ZoomText, ZoomValue)
  then
  begin
    FCurrentZoom:= ZoomValue;
    if FCurrentZoom< MIN_ZOOM
    then
      FCurrentZoom:= MIN_ZOOM
    else if FCurrentZoom> MAX_ZOOM
    then
      FCurrentZoom:= MAX_ZOOM;

    UpdatePreview;
  end;
end;

procedure TFormMain.btnPrintClick(Sender: TObject);
begin
  if not TFile.Exists(edtPdfFile.Text)
  then
  begin
    ShowError('Please select a valid PDF file first.');
    Exit;
  end;

  ValidatePageRange;

  if PrintDialog.Execute
  then
  begin
    PrintPDFPages;
  end;
end;

procedure TFormMain.PrintPDFPages;
var
  FromPage, ToPage, Page, Copy, CopyCount: Integer;
  FirstPage: Boolean;
  Bitmap: TBitmap;
  ScaleX, ScaleY, Scale: Double;
  DestWidth, DestHeight: Integer;
begin
  FProcessing:= True;
  FCancelled:= False;
  EnableDisableControls(False);
  btnCancel.Visible:= True;
  progressPrint.Visible:= True;

  try
    // Load PDF for printing
    Pdf.Active:= False;
    Pdf.FileName:= edtPdfFile.Text;
    Pdf.Active:= True;

    // Determine page range
    case cmbPageRange.ItemIndex of
    0:
      begin // All pages
        FromPage:= 1;
        ToPage:= Pdf.PageCount;
      end;
    1:
      begin // Current page (preview page)
        FromPage:= FCurrentPreviewPage;
        ToPage:= FCurrentPreviewPage;
      end;
    2:
      begin // Custom range
        FromPage:= StrToIntDef(edtFromPage.Text, 1);
        ToPage:= StrToIntDef(edtToPage.Text, Pdf.PageCount);
      end;
  else
    FromPage:= 1;
    ToPage:= Pdf.PageCount;
    end;

    // Validate range
    FromPage:= Max(1, Min(FromPage, Pdf.PageCount));
    ToPage:= Max(FromPage, Min(ToPage, Pdf.PageCount));

    CopyCount:= udCopies.Position;

    // Set printer title
    if Pdf.Title<> ''
    then
      Printer.Title:= Pdf.Title
    else
      Printer.Title:= 'PDF Print - '+ ExtractFileName(edtPdfFile.Text);

    FirstPage:= True;
    Printer.BeginDoc;
    try
      progressPrint.Max:= (ToPage- FromPage+ 1)* CopyCount;
      progressPrint.Position:= 0;

      LogMessage('Starting print job: Pages '+ IntToStr(FromPage)+ '-'+ IntToStr(ToPage)+ ', Copies: '+ IntToStr(CopyCount));

      for Copy:= 1 to CopyCount do
      begin
        for Page:= FromPage to ToPage do
        begin
          if FCancelled
          then
          begin
            LogMessage('Print job cancelled by user');
            Exit;
          end;

          if FirstPage
          then
            FirstPage:= False
          else
            Printer.NewPage;

          lblStatus.Caption:= Format('Printing page %d of %d (Copy %d of %d)...', [Page, ToPage, Copy, CopyCount]);
          progressPrint.Position:= progressPrint.Position+ 1;
          Application.ProcessMessages;

          Pdf.PageNumber:= Page;

          // Calculate scaling based on selected option
          case cmbScaling.ItemIndex of
          0:
            begin // No scaling
              DestWidth:= Round(Pdf.PageWidth);
              DestHeight:= Round(Pdf.PageHeight);
            end;
          1:
            begin // Fit to page
              ScaleX:= Printer.PageWidth/ Pdf.PageWidth;
              ScaleY:= Printer.PageHeight/ Pdf.PageHeight;
              Scale:= Min(ScaleX, ScaleY);
              DestWidth:= Round(Pdf.PageWidth* Scale);
              DestHeight:= Round(Pdf.PageHeight* Scale);
            end;
          2:
            begin // Shrink to fit
              ScaleX:= Printer.PageWidth/ Pdf.PageWidth;
              ScaleY:= Printer.PageHeight/ Pdf.PageHeight;
              Scale:= Min(1.0, Min(ScaleX, ScaleY));
              DestWidth:= Round(Pdf.PageWidth* Scale);
              DestHeight:= Round(Pdf.PageHeight* Scale);
            end;
          3:
            begin // Custom scaling
              Scale:= udCustomScale.Position/ 100.0;
              DestWidth:= Round(Pdf.PageWidth* Scale);
              DestHeight:= Round(Pdf.PageHeight* Scale);
            end;
        else
          DestWidth:= Round(Pdf.PageWidth);
          DestHeight:= Round(Pdf.PageHeight);
          end;

          // Render page with high quality for printing
          Bitmap:= Pdf.RenderPage(0, 0, DestWidth, DestHeight, ro0, [rePrinting]);
          try
            // Print bitmap centered on page
            Printer.Canvas.StretchDraw(Rect((Printer.PageWidth- DestWidth)div 2, (Printer.PageHeight- DestHeight)div 2, (Printer.PageWidth+ DestWidth)div 2,
              (Printer.PageHeight+ DestHeight)div 2), Bitmap);
          finally
            Bitmap.Free;
          end;
        end;
      end;

      LogMessage('Print job completed successfully');

    finally
      Printer.EndDoc;
    end;

  except
    on E: Exception do
    begin
      ShowError('Print error: '+ E.Message);
      LogMessage('Print error: '+ E.Message, 'ERROR');
    end;
  end;

  FProcessing:= False;
  EnableDisableControls(True);
  btnCancel.Visible:= False;
  progressPrint.Visible:= False;
  lblStatus.Caption:= 'Ready';
  Pdf.Active:= False;
end;

procedure TFormMain.btnCancelClick(Sender: TObject);
begin
  FCancelled:= True;
  lblStatus.Caption:= 'Cancelling...';
end;

procedure TFormMain.ValidatePageRange;
var
  FromPage, ToPage: Integer;
begin
  if not Pdf.Active
  then
    Exit;

  if cmbPageRange.ItemIndex= 2
  then // Custom range
  begin
    FromPage:= StrToIntDef(edtFromPage.Text, 1);
    ToPage:= StrToIntDef(edtToPage.Text, Pdf.PageCount);

    FromPage:= Max(1, Min(FromPage, Pdf.PageCount));
    ToPage:= Max(FromPage, Min(ToPage, Pdf.PageCount));

    edtFromPage.Text:= IntToStr(FromPage);
    edtToPage.Text:= IntToStr(ToPage);
  end;
end;

procedure TFormMain.cmbPageRangeChange(Sender: TObject);
begin
  edtFromPage.Enabled:= cmbPageRange.ItemIndex= 2;
  edtToPage.Enabled:= cmbPageRange.ItemIndex= 2;
  lblTo.Enabled:= cmbPageRange.ItemIndex= 2;
end;

procedure TFormMain.cmbScalingChange(Sender: TObject);
begin
  edtCustomScale.Enabled:= cmbScaling.ItemIndex= 3;
  udCustomScale.Enabled:= cmbScaling.ItemIndex= 3;
  lblCustomScale.Enabled:= cmbScaling.ItemIndex= 3;
end;

procedure TFormMain.UpdateUI;
var
  HasFile: Boolean;
begin
  HasFile:= TFile.Exists(edtPdfFile.Text);

  btnPrint.Enabled:= HasFile and not FProcessing;

  // Update page range controls
  if Pdf.Active
  then
  begin
    edtFromPage.Text:= '1';
    edtToPage.Text:= IntToStr(Pdf.PageCount);
  end;

  // Enable/disable controls based on selections
  cmbPageRangeChange(nil);
  cmbScalingChange(nil);

  UpdatePreviewControls;
end;

procedure TFormMain.UpdatePreviewControls;
begin
  if Pdf.Active
  then
  begin
    btnFirstPage.Enabled:= FCurrentPreviewPage> 1;
    btnPrevPage.Enabled:= FCurrentPreviewPage> 1;
    btnNextPage.Enabled:= FCurrentPreviewPage< Pdf.PageCount;
    btnLastPage.Enabled:= FCurrentPreviewPage< Pdf.PageCount;

    udPreviewPage.Min:= 1;
    udPreviewPage.Max:= Pdf.PageCount;
    udPreviewPage.Position:= FCurrentPreviewPage;
  end
  else
  begin
    btnFirstPage.Enabled:= False;
    btnPrevPage.Enabled:= False;
    btnNextPage.Enabled:= False;
    btnLastPage.Enabled:= False;
  end;
end;

procedure TFormMain.UpdatePageInfo;
begin
  if Pdf.Active
  then
  begin
    lblPageInfo.Caption:= Format('Page %d of %d', [FCurrentPreviewPage, Pdf.PageCount]);
  end
  else
  begin
    lblPageInfo.Caption:= 'No document loaded';
  end;
end;

procedure TFormMain.EnableDisableControls(Enabled: Boolean);
begin
  pnlControls.Enabled:= Enabled;
  grpFileSelection.Enabled:= Enabled;
  grpPrintOptions.Enabled:= Enabled;
  grpLayoutOptions.Enabled:= Enabled;
  grpQualityOptions.Enabled:= Enabled;
  btnPrint.Enabled:= Enabled;
  btnPageSetup.Enabled:= Enabled;
end;

procedure TFormMain.LogMessage(
  const Msg  : string;
  const Level: string);
begin
  // For now, just update status label
  // In a more sophisticated version, this could log to a file or list
  lblStatus.Caption:= Msg;
  Application.ProcessMessages;
end;

procedure TFormMain.ShowError(const Msg: string);
begin
  MessageDlg(Msg, mtError, [mbOK], 0);
end;

procedure TFormMain.LoadSettings;
var
  Ini: TIniFile;
begin
  if not TFile.Exists(FSettingsFile)
  then
    Exit;

  Ini:= TIniFile.Create(FSettingsFile);
  try
    // Window position and size
    Left:= Ini.ReadInteger('Window', 'Left', Left);
    Top:= Ini.ReadInteger('Window', 'Top', Top);
    Width:= Ini.ReadInteger('Window', 'Width', Width);
    Height:= Ini.ReadInteger('Window', 'Height', Height);

    // Print settings
    cmbQuality.ItemIndex:= Ini.ReadInteger('Print', 'Quality', 1);
    edtDPI.Text:= IntToStr(Ini.ReadInteger('Print', 'DPI', DEFAULT_DPI));
    udDPI.Position:= Ini.ReadInteger('Print', 'DPI', DEFAULT_DPI);
    chkGrayscale.Checked:= Ini.ReadBool('Print', 'Grayscale', False);
    chkDuplex.Checked:= Ini.ReadBool('Print', 'Duplex', False);

    // Layout settings
    cmbScaling.ItemIndex:= Ini.ReadInteger('Layout', 'Scaling', 1);
    edtCustomScale.Text:= IntToStr(Ini.ReadInteger('Layout', 'CustomScale', 100));
    udCustomScale.Position:= Ini.ReadInteger('Layout', 'CustomScale', 100);

    if Ini.ReadBool('Layout', 'Portrait', True)
    then
      rbPortrait.Checked:= True
    else if Ini.ReadBool('Layout', 'Landscape', False)
    then
      rbLandscape.Checked:= True
    else
      rbAutoRotate.Checked:= True;

    // Preview settings
    FCurrentZoom:= Ini.ReadInteger('Preview', 'Zoom', 100);
    cmbZoom.Text:= IntToStr(FCurrentZoom)+ '%';

  finally
    Ini.Free;
  end;
end;

procedure TFormMain.SaveSettings;
var
  Ini: TIniFile;
begin
  Ini:= TIniFile.Create(FSettingsFile);
  try
    // Window position and size
    Ini.WriteInteger('Window', 'Left', Left);
    Ini.WriteInteger('Window', 'Top', Top);
    Ini.WriteInteger('Window', 'Width', Width);
    Ini.WriteInteger('Window', 'Height', Height);

    // Print settings
    Ini.WriteInteger('Print', 'Quality', cmbQuality.ItemIndex);
    Ini.WriteInteger('Print', 'DPI', udDPI.Position);
    Ini.WriteBool('Print', 'Grayscale', chkGrayscale.Checked);
    Ini.WriteBool('Print', 'Duplex', chkDuplex.Checked);

    // Layout settings
    Ini.WriteInteger('Layout', 'Scaling', cmbScaling.ItemIndex);
    Ini.WriteInteger('Layout', 'CustomScale', udCustomScale.Position);
    Ini.WriteBool('Layout', 'Portrait', rbPortrait.Checked);
    Ini.WriteBool('Layout', 'Landscape', rbLandscape.Checked);
    Ini.WriteBool('Layout', 'AutoRotate', rbAutoRotate.Checked);

    // Preview settings
    Ini.WriteInteger('Preview', 'Zoom', FCurrentZoom);

  finally
    Ini.Free;
  end;
end;

procedure TFormMain.LoadRecentFiles;
var
  Ini: TIniFile;
  I: Integer;
  FileName: string;
begin
  FRecentFiles.Clear;

  if not TFile.Exists(FSettingsFile)
  then
    Exit;

  Ini:= TIniFile.Create(FSettingsFile);
  try
    for I:= 1 to MAX_RECENT_FILES do
    begin
      FileName:= Ini.ReadString('RecentFiles', 'File'+ IntToStr(I), '');
      if (FileName<> '')and TFile.Exists(FileName)
      then
        FRecentFiles.Add(FileName);
    end;
  finally
    Ini.Free;
  end;
end;

procedure TFormMain.SaveRecentFiles;
var
  Ini: TIniFile;
  I: Integer;
begin
  Ini:= TIniFile.Create(FSettingsFile);
  try
    // Clear existing entries
    for I:= 1 to MAX_RECENT_FILES do
    begin
      Ini.DeleteKey('RecentFiles', 'File'+ IntToStr(I));
    end;

    // Save current list
    for I:= 0 to Min(FRecentFiles.Count- 1, MAX_RECENT_FILES- 1) do
    begin
      Ini.WriteString('RecentFiles', 'File'+ IntToStr(I+ 1), FRecentFiles[I]);
    end;
  finally
    Ini.Free;
  end;
end;

procedure TFormMain.AddToRecentFiles(const FileName: string);
var
  Index: Integer;
begin
  Index:= FRecentFiles.IndexOf(FileName);
  if Index>= 0
  then
    FRecentFiles.Delete(Index);

  FRecentFiles.Insert(0, FileName);

  while FRecentFiles.Count> MAX_RECENT_FILES do
    FRecentFiles.Delete(FRecentFiles.Count- 1);

  UpdateRecentFilesList;
end;

procedure TFormMain.UpdateRecentFilesList;
var
  I: Integer;
begin
  lstRecentFiles.Clear;
  for I:= 0 to FRecentFiles.Count- 1 do
  begin
    lstRecentFiles.Items.Add(ExtractFileName(FRecentFiles[I])+ ' - '+ ExtractFilePath(FRecentFiles[I]));
  end;
end;

procedure TFormMain.FormDropFiles(
  Sender         : TObject;
  const FileNames: array of string);
var
  FileName: string;
begin
  if Length(FileNames)> 0
  then
  begin
    FileName:= FileNames[0];
    if LowerCase(ExtractFileExt(FileName))= '.pdf'
    then
      LoadPDFFile(FileName)
    else
      ShowError('Only PDF files are supported.');
  end;
end;

// Additional event handlers implementation...

procedure TFormMain.btnPageSetupClick(Sender: TObject);
begin
  PageSetupDialog.Execute;
end;

procedure TFormMain.btnExitClick(Sender: TObject);
begin
  Close;
end;

procedure TFormMain.lblLinkClick(Sender: TObject);
begin
  ShellExecute(0, 'open', 'https://www.loslab.com', nil, nil, SW_SHOWNORMAL);
end;

procedure TFormMain.lblLinkMouseEnter(Sender: TObject);
begin
  lblLink.Font.Color:= clRed;
  lblLink.Font.Style:= [fsUnderline, fsBold];
end;

procedure TFormMain.lblLinkMouseLeave(Sender: TObject);
begin
  lblLink.Font.Color:= clBlue;
  lblLink.Font.Style:= [fsUnderline];
end;

procedure TFormMain.mnuOpenFileClick(Sender: TObject);
begin
  btnBrowseClick(Sender);
end;

procedure TFormMain.mnuClearRecentClick(Sender: TObject);
begin
  FRecentFiles.Clear;
  UpdateRecentFilesList;
  SaveRecentFiles;
end;

procedure TFormMain.tmrAutoSaveTimer(Sender: TObject);
begin
  SaveSettings;
end;

procedure TFormMain.edtFromPageChange(Sender: TObject);
begin
  ValidatePageRange;
end;

procedure TFormMain.edtToPageChange(Sender: TObject);
begin
  ValidatePageRange;
end;

procedure TFormMain.edtCustomScaleChange(Sender: TObject);
begin
  // Auto-update UpDown control
  udCustomScale.Position:= StrToIntDef(edtCustomScale.Text, 100);
end;

procedure TFormMain.cmbQualityChange(Sender: TObject);
begin
  // Update DPI based on quality selection
  case cmbQuality.ItemIndex of
  0:
    begin // Draft
      edtDPI.Text:= '150';
      udDPI.Position:= 150;
    end;
  1:
    begin // Normal
      edtDPI.Text:= '300';
      udDPI.Position:= 300;
    end;
  2:
    begin // High
      edtDPI.Text:= '600';
      udDPI.Position:= 600;
    end;
  end;
end;

procedure TFormMain.edtDPIChange(Sender: TObject);
begin
  // Auto-update UpDown control
  udDPI.Position:= StrToIntDef(edtDPI.Text, DEFAULT_DPI);
end;

end.
