unit frm_FirmaRecogida;

interface

uses
  Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes,
  Vcl.Graphics, Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.ExtCtrls, Vcl.StdCtrls,
  Data.DB, FireDAC.Comp.Client, FireDAC.Stan.Param, uAppTheme, Vcl.Imaging.pngimage,
  Vcl.Imaging.jpeg, System.NetEncoding;

type
  TfrmFirmaRecogida = class(TForm)
    pnlHeader: TPanel;
    lblTitulo: TLabel;
    pnlMain: TPanel;
    lblNombre: TLabel;
    edtNombre: TEdit;
    lblDocumento: TLabel;
    edtDocumento: TEdit;
    lblMatricula: TLabel;
    edtMatricula: TEdit;
    pnlSignatureBorder: TPanel;
    imgSignature: TImage;
    lblSignatureHint: TLabel;
    pnlButtons: TPanel;
    btnGuardar: TButton;
    btnLimpiar: TButton;
    btnCancelar: TButton;
    procedure FormShow(Sender: TObject);
    procedure imgSignatureMouseDown(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
    procedure imgSignatureMouseMove(Sender: TObject; Shift: TShiftState; X, Y: Integer);
    procedure imgSignatureMouseUp(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
    procedure btnLimpiarClick(Sender: TObject);
    procedure btnGuardarClick(Sender: TObject);
    procedure btnCancelarClick(Sender: TObject);
  private
    FRecogidaId: Integer;
    FDrawing: Boolean;
    procedure LimpiarFirma;
  public
    property RecogidaId: Integer read FRecogidaId write FRecogidaId;
  end;

var
  frmFirmaRecogida: TfrmFirmaRecogida;

implementation

uses
  dmg_Main, uDbErrorHandler;

{$R *.dfm}

procedure CargarFirmaDeCampo(AField: TField; APicture: TPicture; AImg: TImage; ALblHint: TLabel);
var
  LStream: TStream;
  LMemStream: TMemoryStream;
  LStrStream: TStringStream;
  LHeader: array[0..15] of AnsiChar;
  LRawStr, LBase64: string;
  LCommaPos: Integer;
  LBytes: TBytes;
  LLoaded: Boolean;
  LWic: TWICImage;
  LPng: TPngImage;
begin
  if (AField = nil) or AField.IsNull then
  begin
    APicture.Assign(nil);
    if ALblHint <> nil then ALblHint.Visible := True;
    Exit;
  end;

  LStream := AField.DataSet.CreateBlobStream(AField, bmRead);
  try
    if LStream.Size = 0 then
    begin
      APicture.Assign(nil);
      if ALblHint <> nil then ALblHint.Visible := True;
      Exit;
    end;

    LLoaded := False;
    LMemStream := TMemoryStream.Create;
    try
      FillChar(LHeader, SizeOf(LHeader), 0);
      LStream.Position := 0;
      LStream.Read(LHeader, SizeOf(LHeader));
      LStream.Position := 0;

      if (Copy(string(LHeader), 1, 10) = 'data:image') or (Copy(string(LHeader), 1, 5) = 'iVBOR') then
      begin
        LStrStream := TStringStream.Create('', TEncoding.ANSI);
        try
          LStrStream.CopyFrom(LStream, LStream.Size);
          LRawStr := LStrStream.DataString;
          LCommaPos := Pos('base64,', LRawStr);
          if LCommaPos > 0 then
            LBase64 := Copy(LRawStr, LCommaPos + 7, Length(LRawStr))
          else
            LBase64 := LRawStr;
          LBytes := TNetEncoding.Base64.DecodeStringToBytes(Trim(LBase64));
          if Length(LBytes) > 0 then
          begin
            LMemStream.WriteBuffer(LBytes[0], Length(LBytes));
            LMemStream.Position := 0;
          end;
        finally
          LStrStream.Free;
        end;
      end
      else
      begin
        LMemStream.LoadFromStream(LStream);
        LMemStream.Position := 0;
      end;

      if LMemStream.Size > 0 then
      begin
        try
          LWic := TWICImage.Create;
          try
            LWic.LoadFromStream(LMemStream);
            APicture.Assign(LWic);
            LLoaded := True;
          finally
            LWic.Free;
          end;
        except
          LLoaded := False;
        end;

        if not LLoaded then
        begin
          LMemStream.Position := 0;
          try
            LPng := TPngImage.Create;
            try
              LPng.LoadFromStream(LMemStream);
              APicture.Assign(LPng);
              LLoaded := True;
            finally
              LPng.Free;
            end;
          except
            LLoaded := False;
          end;
        end;

        if not LLoaded then
        begin
          LMemStream.Position := 0;
          try
            APicture.Bitmap.LoadFromStream(LMemStream);
            LLoaded := True;
          except
            LLoaded := False;
          end;
        end;
      end;
    finally
      LMemStream.Free;
    end;

    if ALblHint <> nil then ALblHint.Visible := not LLoaded;
    if not LLoaded then APicture.Assign(nil);
  finally
    LStream.Free;
  end;
end;

procedure TfrmFirmaRecogida.FormShow(Sender: TObject);
var
  Qry: TFDQuery;
begin
  TAppTheme.ApplyToForm(Self);
  FDrawing := False;
  LimpiarFirma;

  // Cargar datos existentes si ya estuviera firmado
  if FRecogidaId > 0 then
  begin
    Qry := TFDQuery.Create(nil);
    try
      Qry.Connection := dmgMain.dbConn;
      Qry.SQL.Text := 'SELECT firma_nombre, firma_documento, firma_imagen, conductor, matricula FROM ge_envios_recogidas WHERE id = :id';
      Qry.ParamByName('id').AsInteger := FRecogidaId;
      Qry.Open;
      if not Qry.IsEmpty then
      begin
        edtNombre.Text := Qry.FieldByName('firma_nombre').AsString;
        if edtNombre.Text = '' then
          edtNombre.Text := Qry.FieldByName('conductor').AsString;
        edtDocumento.Text := Qry.FieldByName('firma_documento').AsString;
        edtMatricula.Text := Qry.FieldByName('matricula').AsString;
        
        CargarFirmaDeCampo(Qry.FindField('firma_imagen'), imgSignature.Picture, imgSignature, lblSignatureHint);
      end;
    finally
      Qry.Free;
    end;
  end;
end;

procedure TfrmFirmaRecogida.LimpiarFirma;
begin
  imgSignature.Picture.Bitmap.SetSize(imgSignature.Width, imgSignature.Height);
  imgSignature.Picture.Bitmap.Canvas.Brush.Color := clWhite;
  imgSignature.Picture.Bitmap.Canvas.FillRect(Rect(0, 0, imgSignature.Width, imgSignature.Height));
  imgSignature.Picture.Bitmap.Canvas.Pen.Color := clNavy;
  imgSignature.Picture.Bitmap.Canvas.Pen.Width := 3;
end;

procedure TfrmFirmaRecogida.imgSignatureMouseDown(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
begin
  FDrawing := True;
  imgSignature.Picture.Bitmap.Canvas.MoveTo(X, Y);
end;

procedure TfrmFirmaRecogida.imgSignatureMouseMove(Sender: TObject; Shift: TShiftState; X, Y: Integer);
begin
  if FDrawing then
  begin
    imgSignature.Picture.Bitmap.Canvas.LineTo(X, Y);
  end;
end;

procedure TfrmFirmaRecogida.imgSignatureMouseUp(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
begin
  FDrawing := False;
end;

procedure TfrmFirmaRecogida.btnLimpiarClick(Sender: TObject);
begin
  LimpiarFirma;
end;

procedure TfrmFirmaRecogida.btnCancelarClick(Sender: TObject);
begin
  ModalResult := mrCancel;
end;

procedure TfrmFirmaRecogida.btnGuardarClick(Sender: TObject);
var
  LStream: TMemoryStream;
begin
  if Trim(edtNombre.Text) = '' then
  begin
    ShowMessage('Por favor, introduzca el nombre del transportista.');
    edtNombre.SetFocus;
    Exit;
  end;
  if Trim(edtDocumento.Text) = '' then
  begin
    ShowMessage('Por favor, introduzca el DNI/Documento del transportista.');
    edtDocumento.SetFocus;
    Exit;
  end;
  if Trim(edtMatricula.Text) = '' then
  begin
    ShowMessage('Por favor, introduzca la matrícula del vehículo.');
    edtMatricula.SetFocus;
    Exit;
  end;

  LStream := TMemoryStream.Create;
  try
    imgSignature.Picture.Bitmap.SaveToStream(LStream);
    LStream.Position := 0;

    dmgMain.dbConn.StartTransaction;
    try
      dmgMain.qryExec.Close;
      dmgMain.qryExec.SQL.Text :=
        'UPDATE ge_envios_recogidas ' +
        'SET firma_nombre = :nom, firma_documento = :doc, firma_imagen = :img, ' +
        '    conductor = :cond, matricula = :mat, ' +
        '    firma_fecha = NOW(), id_user_update = :user, updated_at = NOW() ' +
        'WHERE id = :id';
      dmgMain.qryExec.ParamByName('nom').AsString := Trim(edtNombre.Text);
      dmgMain.qryExec.ParamByName('doc').AsString := Trim(edtDocumento.Text);
      dmgMain.qryExec.ParamByName('img').LoadFromStream(LStream, ftBlob);
      dmgMain.qryExec.ParamByName('cond').AsString := Trim(edtNombre.Text);
      dmgMain.qryExec.ParamByName('mat').AsString := Trim(edtMatricula.Text);
      dmgMain.qryExec.ParamByName('user').AsString := dmgMain.CurrentUserId;
      dmgMain.qryExec.ParamByName('id').AsInteger := FRecogidaId;
      dmgMain.qryExec.ExecSQL;
      
      dmgMain.dbConn.Commit;
      ModalResult := mrOk;
    except
      on E: Exception do
      begin
        dmgMain.dbConn.Rollback;
        TDbErrorHandler.HandleException(E, 'Error al guardar la firma del transportista');
      end;
    end;
  finally
    LStream.Free;
  end;
end;

end.
