unit uMainMobile;

{
  FmxPdfTools - Mobile (Android) Demo
  ===========================================================================
  On Android the engine produces a real PDF with android.graphics.pdf and
  hands it to the system viewer. This demo showcases the Android surface of
  TReportEngine:

      ShowReport   - render one .frp to a PDF and open it
      ShowReports  - merge several .frp templates into a single PDF (bundle)

  Templates, the logo image and the SQLite database are deployed inside the
  APK (assets\internal) and force-copied to the app's Documents folder on
  every launch, so design changes appear immediately after each deploy.

  The same English sample set as the Windows demo is used (see ..\Raporlar,
  produced by Demos\ReportGen). PDF generation runs on a background thread with
  a loading overlay, so the UI never freezes (no ANR).
}

interface

uses
  System.SysUtils, System.Types, System.UITypes, System.Classes, System.IOUtils,
  FMX.Types, FMX.Controls, FMX.Forms, FMX.Graphics, FMX.Dialogs,
  FMX.Controls.Presentation, FMX.StdCtrls, FMX.Layouts, FMX.ListBox,
  FireDAC.Stan.Intf, FireDAC.Stan.Option, FireDAC.Stan.Error, FireDAC.Stan.Def,
  FireDAC.Stan.Pool, FireDAC.Stan.Async, FireDAC.Phys, FireDAC.Phys.SQLiteDef,
  FireDAC.Phys.SQLite, FireDAC.Stan.Param, FireDAC.DatS, FireDAC.DApt.Intf,
  FireDAC.DApt, FireDAC.Phys.Intf, FireDAC.UI.Intf, FireDAC.FMXUI.Wait,
  FireDAC.Comp.Client, FireDAC.Comp.DataSet, Data.DB,
  PDFDrawing, uReportEngine, uRptDataSet;

type
  TMobileDemoForm = class(TForm)
    FDConn       : TFDConnection;
    FDSQLiteDrv  : TFDPhysSQLiteDriverLink;
    QryCustomers : TFDQuery;
    DSCustomers  : TDataSource;
    QryOrders    : TFDQuery;
    DSOrders     : TDataSource;
    QryItems     : TFDQuery;
    PDFDrawing1  : TPDFDrawing;
    ReportEngine1: TReportEngine;
    RptCustomers : TRptDataSet;
    RptOrders    : TRptDataSet;
    RptItems     : TRptDataSet;
    ToolBar1     : TToolBar;
    lblTitle     : TLabel;
    Root         : TLayout;
    lblPick      : TLabel;
    cbxSample    : TComboBox;
    lblDesc      : TLabel;
    btnGenerate  : TButton;
    lblStatus    : TLabel;
    procedure FormCreate(Sender: TObject);
    procedure FormDestroy(Sender: TObject);
    procedure cbxSampleChange(Sender: TObject);
    procedure btnGenerateClick(Sender: TObject);
  private
    procedure SetStatus(const AMsg: string);
    procedure OpenDatabase;
    procedure ApplyContext;
    procedure RestoreDataset;
  end;

var
  MobileDemoForm: TMobileDemoForm;

implementation

{$R *.fmx}

uses
  uPDFOpen
  {$IFDEF ANDROID}
  , Androidapi.AssetManager, Androidapi.NativeActivity
  {$ENDIF};

type
  TSample = record
    Caption  : string;
    FileBase : string;   // ';'-joined => bundle rendered with ShowReports
    Desc     : string;
    Unlink   : Boolean;
    SortField: string;
  end;

const
  SAMPLES: array[0..8] of TSample = (
    (Caption: 'Sales Statement / Invoice';          FileBase: 'sample_invoice_A4';
     Desc: 'Logo, bill-to variables, barcode + QR, one row per order and SUM/AVG/COUNT totals.';
     Unlink: True; SortField: 'ShipCountry'),
    (Caption: 'Business Cards';                      FileBase: 'sample_business_card_A5L';
     Desc: 'One card per customer with shapes, logo and a vCard QR code.';
     Unlink: False; SortField: ''),
    (Caption: 'Customer Directory (grouped)';        FileBase: 'sample_catalog_grouped_A4';
     Desc: 'Customers grouped by country with per-group COUNT subtotals.';
     Unlink: False; SortField: ''),
    (Caption: 'Account Statement (3 levels)';        FileBase: 'sample_statement_3level_A4';
     Desc: 'customers / orders / items master-detail-subdetail with qualified fields.';
     Unlink: False; SortField: ''),
    (Caption: 'Certificate of Partnership';          FileBase: 'sample_certificate_A5';
     Desc: 'Decorative page: shapes, wax seal, centered RTF and the [Date] token.';
     Unlink: False; SortField: ''),
    (Caption: 'Feature Showcase';                    FileBase: 'sample_showcase_A4';
     Desc: 'Every element type plus live expression results.';
     Unlink: False; SortField: ''),
    (Caption: 'Shipping Labels';                     FileBase: 'sample_labels_A4';
     Desc: 'One bordered label per order with a barcode and IIF priority badge.';
     Unlink: True; SortField: 'ShipCountry'),
    (Caption: 'Quarterly Pack (3-part bundle)';
     FileBase: 'sample_bundle_cover_A4;sample_bundle_summary_A4;sample_bundle_detail_A4';
     Desc: 'Cover + summary + detail merged into ONE PDF via ShowReports.';
     Unlink: True; SortField: 'ShipVia'),
    (Caption: 'Regional Roll-up (subreport)';        FileBase: 'sample_subreport_main_A4';
     Desc: 'A host page embedding a flowing sub-report that runs its own loop.';
     Unlink: False; SortField: '')
  );

{$IFDEF ANDROID}
// Always overwrite the Documents copy from the APK asset so each deploy ships
// the latest template / database / image (System.StartUpCopy only copies once).
procedure ForceCopyAssetToDocs(const FileName: string);
const
  ASSET_PATH = 'internal/';
var
  AM: PAAssetManager;
  Asset: PAAsset;
  Buffer: array[0..65535] of Byte;
  ReadCount: Integer;
  FH: THandle;
  DocPath: string;
  M: TMarshaller;
begin
  if System.DelphiActivity = nil then Exit;
  AM := PANativeActivity(System.DelphiActivity)^.assetManager;
  if AM = nil then Exit;
  Asset := AAssetManager_open(AM, M.AsUtf8(ASSET_PATH + FileName).ToPointer,
                              AASSET_MODE_BUFFER);
  if Asset = nil then Exit;
  try
    DocPath := TPath.Combine(TPath.GetDocumentsPath, FileName);
    FH := FileCreate(DocPath);
    if FH = THandle(-1) then Exit;
    try
      repeat
        ReadCount := AAsset_read(Asset, @Buffer[0], SizeOf(Buffer));
        if ReadCount > 0 then FileWrite(FH, Buffer, ReadCount);
      until ReadCount <= 0;
    finally
      FileClose(FH);
    end;
  finally
    AAsset_close(Asset);
  end;
end;

procedure RefreshAssets;
var
  I, J: Integer;
  Parts: TArray<string>;
begin
  for I := Low(SAMPLES) to High(SAMPLES) do
  begin
    Parts := SAMPLES[I].FileBase.Split([';']);
    for J := 0 to High(Parts) do
      ForceCopyAssetToDocs(Parts[J] + '.frp');
  end;
  ForceCopyAssetToDocs('sample_subreport_child_A4.frp');  // loaded by the host
  ForceCopyAssetToDocs('logo.png');                        // image used by templates
  ForceCopyAssetToDocs('db5.db');                          // SQLite database
end;
{$ENDIF}

{ TMobileDemoForm }

procedure TMobileDemoForm.FormCreate(Sender: TObject);
var
  I: Integer;
begin
  {$IFDEF ANDROID}
  RefreshAssets;   // APK assets -> Documents (overwrite)
  {$ENDIF}

  cbxSample.BeginUpdate;
  try
    for I := Low(SAMPLES) to High(SAMPLES) do
      cbxSample.Items.Add(SAMPLES[I].Caption);
  finally
    cbxSample.EndUpdate;
  end;
  cbxSample.ItemIndex := 0;
  cbxSampleChange(nil);

  ReportEngine1.SetVariable('Company', 'ACME Trading Co.');
  ReportEngine1.SetVariable('DocNo',   'STMT-2026-0042');
  ReportEngine1.SetVariable('Address', '12 Harbour Road, Rotterdam, NL');

  try
    OpenDatabase;
  except
    on E: Exception do SetStatus('Database error: ' + E.Message);
  end;
end;

procedure TMobileDemoForm.FormDestroy(Sender: TObject);
begin
  QryItems.Close;
  QryOrders.Close;
  QryCustomers.Close;
  if FDConn.Connected then FDConn.Connected := False;
end;

procedure TMobileDemoForm.OpenDatabase;
var
  DBFile: string;
begin
  {$IFDEF ANDROID}
  DBFile := TPath.Combine(TPath.GetDocumentsPath, 'db5.db');
  {$ELSE}
  DBFile := TPath.Combine(ExtractFilePath(ParamStr(0)), 'db5.db');
  {$ENDIF}
  if not TFile.Exists(DBFile) then
  begin
    SetStatus('db5.db not found.');
    Exit;
  end;

  FDConn.Params.Clear;
  FDConn.Params.Add('DriverID=SQLite');
  FDConn.Params.Add('Database=' + DBFile);
  FDConn.LoginPrompt := False;
  FDConn.Connected   := True;

  QryCustomers.SQL.Text := 'SELECT * FROM customers ORDER BY Country, CompanyName';
  QryCustomers.Open;
  QryOrders.SQL.Text := 'SELECT * FROM orders';
  QryOrders.Open;
  QryItems.SQL.Text := 'SELECT * FROM orderitems';
  QryItems.Open;

  SetStatus(Format('Ready - %d customers.', [QryCustomers.RecordCount]));
end;

procedure TMobileDemoForm.cbxSampleChange(Sender: TObject);
begin
  if cbxSample.ItemIndex >= 0 then
    lblDesc.Text := SAMPLES[cbxSample.ItemIndex].Desc;
end;

procedure TMobileDemoForm.ApplyContext;
var
  I: Integer;
begin
  I := cbxSample.ItemIndex;
  if (I >= 0) and SAMPLES[I].Unlink and (QryOrders.MasterSource <> nil) then
  begin
    QryOrders.MasterSource := nil;
    QryOrders.Close;
    if SAMPLES[I].SortField <> '' then
      QryOrders.IndexFieldNames := SAMPLES[I].SortField;
    QryOrders.Open;
  end;
end;

procedure TMobileDemoForm.RestoreDataset;
begin
  if QryOrders.MasterSource = nil then
  begin
    QryOrders.Close;
    QryOrders.IndexFieldNames := '';
    QryOrders.MasterSource := DSCustomers;
    QryOrders.Open;
  end;
end;

procedure TMobileDemoForm.btnGenerateClick(Sender: TObject);
var
  I, K: Integer;
  Parts: TArray<string>;
  Files: TArray<string>;
  OutPdf: string;
begin
  I := cbxSample.ItemIndex;
  if I < 0 then Exit;
  if not QryCustomers.Active then
  begin
    SetStatus('No data loaded.');
    Exit;
  end;

  ApplyContext;
  Parts := SAMPLES[I].FileBase.Split([';']);
  try
    if Length(Parts) > 1 then
    begin
      // Bundle: merge several templates into a single PDF.
      SetLength(Files, Length(Parts));
      for K := 0 to High(Parts) do
        Files[K] := TPath.Combine(TPath.GetDocumentsPath, Parts[K] + '.frp');
      OutPdf := TPath.Combine(TPath.GetDocumentsPath, Parts[0] + '_bundle.pdf');
      SetStatus('Generating bundle PDF...');
      ReportEngine1.ShowReports(Files, OutPdf);
    end
    else
    begin
      OutPdf := TPath.Combine(TPath.GetDocumentsPath, Parts[0] + '.pdf');
      SetStatus('Generating PDF...');
      ReportEngine1.ShowReport(
        TPath.Combine(TPath.GetDocumentsPath, Parts[0] + '.frp'), OutPdf);
    end;
    SetStatus('Done: ' + ExtractFileName(OutPdf));
  except
    on E: Exception do SetStatus('ERROR: ' + E.Message);
  end;
  RestoreDataset;
end;

procedure TMobileDemoForm.SetStatus(const AMsg: string);
begin
  lblStatus.Text := AMsg;
end;

end.
