unit frm_TermometroMonitor;

interface

uses
  Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
  Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls, Vcl.ExtCtrls, Vcl.Samples.Spin,
  Data.DB, Vcl.Grids, Vcl.DBGrids, System.Bluetooth, System.Threading,
  uMeatMeetBLE, FireDAC.Comp.Client, FireDAC.Stan.Intf, FireDAC.Stan.Option,
  FireDAC.Stan.Param, FireDAC.Stan.Error, FireDAC.DatS, FireDAC.Phys.Intf,
  FireDAC.DApt.Intf, FireDAC.Comp.DataSet;

type
  TfrmTermometroMonitor = class(TForm)
    pnlTop: TPanel;
    cbDevices: TComboBox;
    btnScan: TButton;
    btnConnect: TButton;
    btnDisconnect: TButton;
    lblStatus: TLabel;
    lblBattery: TLabel;
    pnlCenter: TPanel;
    pnlZ1: TPanel;
    lblZ1Title: TLabel;
    lblZ1Temp: TLabel;
    pnlZ2: TPanel;
    lblZ2Title: TLabel;
    lblZ2Temp: TLabel;
    pnlZ3: TPanel;
    lblZ3Title: TLabel;
    lblZ3Temp: TLabel;
    pnlZ4: TPanel;
    lblZ4Title: TLabel;
    lblZ4Temp: TLabel;
    pnlZ5: TPanel;
    lblZ5Title: TLabel;
    lblZ5Temp: TLabel;
    pnlAmb: TPanel;
    lblAmbTitle: TLabel;
    lblAmbTemp: TLabel;
    memoLog: TMemo;
    pnlBottom: TPanel;
    pnlBottomControls: TPanel;
    chkAutoSave: TCheckBox;
    speInterval: TSpinEdit;
    lblInterval: TLabel;
    btnExport: TButton;
    btnClear: TButton;
    dbgLog: TDBGrid;
    tmrAutoSave: TTimer;
    dsLog: TDataSource;
    mdLog: TFDMemTable;
    SaveDialog: TSaveDialog;
    procedure FormCreate(Sender: TObject);
    procedure FormDestroy(Sender: TObject);
    procedure btnScanClick(Sender: TObject);
    procedure btnConnectClick(Sender: TObject);
    procedure btnDisconnectClick(Sender: TObject);
    procedure tmrAutoSaveTimer(Sender: TObject);
    procedure btnExportClick(Sender: TObject);
    procedure btnClearClick(Sender: TObject);
    procedure dbgLogTitleClick(Column: TColumn);
    procedure FormClose(Sender: TObject; var Action: TCloseAction);
  private
    FBLEManager: TMeatMeetBLEManager;
    FLastReading: TMeatMeetReading;

    procedure OnDeviceDiscovered(ADevice: TBluetoothLEDevice);
    procedure OnConnectionStateChanged(AConnected: Boolean);
    procedure OnTemperatureReceived(AReading: TMeatMeetReading);
    procedure OnBLELog(const AMsg: string);

    procedure UpdateLabelColor(ALabel: TLabel; ATemp: Double);
    procedure SaveReadingToDB(AReading: TMeatMeetReading);
    procedure AddLog(const AMsg: string);
  public
  end;

var
  frmTermometroMonitor: TfrmTermometroMonitor;

implementation

{$R *.dfm}

uses
  dmg_Main;

procedure TfrmTermometroMonitor.FormCreate(Sender: TObject);
begin
  // Crear el manager BLE y conectar callbacks
  FBLEManager := TMeatMeetBLEManager.Create;
  FBLEManager.OnDeviceDiscovered := OnDeviceDiscovered;
  FBLEManager.OnConnectionStateChanged := OnConnectionStateChanged;
  FBLEManager.OnTemperatureReceived := OnTemperatureReceived;
  FBLEManager.OnLogMessage := OnBLELog;

  // Crear el dataset en memoria para el histórico
  mdLog.FieldDefs.Add('FechaHora', ftDateTime);
  mdLog.FieldDefs.Add('Zona1', ftFloat);
  mdLog.FieldDefs.Add('Zona2', ftFloat);
  mdLog.FieldDefs.Add('Zona3', ftFloat);
  mdLog.FieldDefs.Add('Zona4', ftFloat);
  mdLog.FieldDefs.Add('Zona5', ftFloat);
  mdLog.FieldDefs.Add('Ambiente', ftFloat);
  mdLog.FieldDefs.Add('Bateria', ftInteger);
  mdLog.CreateDataSet;

  AddLog('═══ Monitor de Termómetro BLE MeatMeet Pro ═══');
  AddLog('');
  AddLog('ANTES DE ESCANEAR:');
  AddLog('  1. Encienda el termómetro MeatMeet');
  AddLog('  2. Si "ME_BOX01" aparece en Configuración');
  AddLog('     de Bluetooth de Windows → QUÍTELO');
  AddLog('     (el BLE no usa emparejamiento del SO)');
  AddLog('  3. Cierre la app del móvil si la tiene abierta');
  AddLog('     (solo puede conectarse 1 dispositivo a la vez)');
  AddLog('  4. Pulse "Escanear"');
  AddLog('');
end;

procedure TfrmTermometroMonitor.FormDestroy(Sender: TObject);
begin
  FBLEManager.Free;
end;

procedure TfrmTermometroMonitor.FormClose(Sender: TObject; var Action: TCloseAction);
begin
  tmrAutoSave.Enabled := False;
  FBLEManager.Disconnect;
end;

procedure TfrmTermometroMonitor.AddLog(const AMsg: string);
begin
  // Añadir línea al memo de log con timestamp
  if csDestroying in ComponentState then Exit;
  memoLog.Lines.Add(FormatDateTime('hh:nn:ss', Now) + ' ' + AMsg);
  // Auto-scroll al final
  SendMessage(memoLog.Handle, WM_VSCROLL, SB_BOTTOM, 0);
end;

procedure TfrmTermometroMonitor.OnBLELog(const AMsg: string);
begin
  AddLog(AMsg);
end;

procedure TfrmTermometroMonitor.btnScanClick(Sender: TObject);
begin
  cbDevices.Items.Clear;
  cbDevices.ItemIndex := -1;
  btnScan.Enabled := False;
  btnConnect.Enabled := False;
  lblStatus.Caption := 'Escaneando...';
  lblStatus.Font.Color := clBlue;
  AddLog('');

  FBLEManager.ScanForDevices;

  // Re-habilitar botones tras timeout (11 segundos, el scan dura 10)
  TThread.CreateAnonymousThread(
    procedure
    begin
      Sleep(11000);
      TThread.Queue(nil,
        TThreadProcedure(
          procedure
          begin
            if not (csDestroying in ComponentState) then
            begin
              btnScan.Enabled := True;
              if cbDevices.Items.Count > 0 then
                btnConnect.Enabled := True;
              if not FBLEManager.IsConnected then
              begin
                lblStatus.Caption := 'Desconectado';
                lblStatus.Font.Color := clRed;
              end;
            end;
          end));
    end).Start;
end;

procedure TfrmTermometroMonitor.OnDeviceDiscovered(ADevice: TBluetoothLEDevice);
begin
  // Ya se ejecuta en el hilo principal gracias a TThread.Queue en el manager
  cbDevices.Items.AddObject(ADevice.DeviceName + ' [' + ADevice.Address + ']', ADevice);
  if cbDevices.ItemIndex = -1 then
    cbDevices.ItemIndex := 0;
end;

procedure TfrmTermometroMonitor.btnConnectClick(Sender: TObject);
var
  LDevice: TBluetoothLEDevice;
begin
  if cbDevices.ItemIndex >= 0 then
  begin
    LDevice := TBluetoothLEDevice(cbDevices.Items.Objects[cbDevices.ItemIndex]);
    lblStatus.Caption := 'Conectando...';
    lblStatus.Font.Color := clOlive;
    btnConnect.Enabled := False;
    FBLEManager.ConnectToDevice(LDevice);
  end
  else
    AddLog('Seleccione un dispositivo del listado primero.');
end;

procedure TfrmTermometroMonitor.btnDisconnectClick(Sender: TObject);
begin
  FBLEManager.Disconnect;
end;

procedure TfrmTermometroMonitor.OnConnectionStateChanged(AConnected: Boolean);
begin
  // Ya se ejecuta en el hilo principal gracias a TThread.Queue en el manager
  if AConnected then
  begin
    lblStatus.Caption := '● Conectado';
    lblStatus.Font.Color := clGreen;
    btnConnect.Enabled := False;
    btnDisconnect.Enabled := True;
    btnScan.Enabled := False;
    tmrAutoSave.Interval := speInterval.Value * 1000;
    tmrAutoSave.Enabled := chkAutoSave.Checked;
  end
  else
  begin
    lblStatus.Caption := 'Desconectado';
    lblStatus.Font.Color := clRed;
    btnConnect.Enabled := True;
    btnDisconnect.Enabled := False;
    btnScan.Enabled := True;
    tmrAutoSave.Enabled := False;
  end;
end;

procedure TfrmTermometroMonitor.UpdateLabelColor(ALabel: TLabel; ATemp: Double);
begin
  if ATemp < 10.0 then
    ALabel.Font.Color := clBlue
  else if ATemp <= 40.0 then
    ALabel.Font.Color := $00228B22  // Forest Green
  else if ATemp <= 60.0 then
    ALabel.Font.Color := $000080FF  // Orange
  else
    ALabel.Font.Color := clRed;
end;

procedure TfrmTermometroMonitor.OnTemperatureReceived(AReading: TMeatMeetReading);
begin
  // Ya se ejecuta en el hilo principal gracias a TThread.Queue en el manager
  FLastReading := AReading;

  lblZ1Temp.Caption := Format('%.1f°C', [AReading.Zona1]);
  lblZ2Temp.Caption := Format('%.1f°C', [AReading.Zona2]);
  lblZ3Temp.Caption := Format('%.1f°C', [AReading.Zona3]);
  lblZ4Temp.Caption := Format('%.1f°C', [AReading.Zona4]);
  lblZ5Temp.Caption := Format('%.1f°C', [AReading.Zona5]);
  lblAmbTemp.Caption := Format('%.0f°C', [AReading.Ambiente]);
  lblBattery.Caption := Format('Batería: %d%%', [AReading.Bateria]);

  UpdateLabelColor(lblZ1Temp, AReading.Zona1);
  UpdateLabelColor(lblZ2Temp, AReading.Zona2);
  UpdateLabelColor(lblZ3Temp, AReading.Zona3);
  UpdateLabelColor(lblZ4Temp, AReading.Zona4);
  UpdateLabelColor(lblZ5Temp, AReading.Zona5);
  UpdateLabelColor(lblAmbTemp, AReading.Ambiente);

  // Añadir al historial en memoria
  mdLog.Append;
  mdLog.FieldByName('FechaHora').AsDateTime := AReading.Timestamp;
  mdLog.FieldByName('Zona1').AsFloat := AReading.Zona1;
  mdLog.FieldByName('Zona2').AsFloat := AReading.Zona2;
  mdLog.FieldByName('Zona3').AsFloat := AReading.Zona3;
  mdLog.FieldByName('Zona4').AsFloat := AReading.Zona4;
  mdLog.FieldByName('Zona5').AsFloat := AReading.Zona5;
  mdLog.FieldByName('Ambiente').AsFloat := AReading.Ambiente;
  mdLog.FieldByName('Bateria').AsInteger := AReading.Bateria;
  mdLog.Post;
end;

procedure TfrmTermometroMonitor.SaveReadingToDB(AReading: TMeatMeetReading);
var
  LQry: TFDQuery;
begin
  // Guardar lectura en la base de datos ge_temperaturas_log
  // Referencia directa a dmgMain.dbConn (TFDConnection del DataModule principal)
  if not Assigned(dmgMain) then Exit;
  if not Assigned(dmgMain.dbConn) then Exit;
  if not dmgMain.dbConn.Connected then Exit;

  LQry := TFDQuery.Create(nil);
  try
    try
      LQry.Connection := dmgMain.dbConn;
      LQry.SQL.Text :=
        'INSERT INTO ge_temperaturas_log ' +
        '(dispositivo, fecha_hora, zona1_temp, zona2_temp, zona3_temp, ' +
        'zona4_temp, zona5_temp, temp_ambiente, bateria, empresa_id) ' +
        'VALUES (:DISP, NOW(), :Z1, :Z2, :Z3, :Z4, :Z5, :AMB, :BAT, :EMP)';

      LQry.ParamByName('DISP').DataType := ftWideString;
      LQry.ParamByName('DISP').AsWideString := cbDevices.Text;
      LQry.ParamByName('Z1').AsFloat := AReading.Zona1;
      LQry.ParamByName('Z2').AsFloat := AReading.Zona2;
      LQry.ParamByName('Z3').AsFloat := AReading.Zona3;
      LQry.ParamByName('Z4').AsFloat := AReading.Zona4;
      LQry.ParamByName('Z5').AsFloat := AReading.Zona5;
      LQry.ParamByName('AMB').AsFloat := AReading.Ambiente;
      LQry.ParamByName('BAT').AsInteger := AReading.Bateria;
      LQry.ParamByName('EMP').AsInteger := StrToIntDef(dmgMain.CurrentCompanyId, 1);

      LQry.ExecSQL;
    except
      on E: Exception do
        TThread.Queue(nil,
          TThreadProcedure(
            procedure
            begin
              AddLog('Error BD: ' + E.Message);
            end));
    end;
  finally
    LQry.Free;
  end;
end;

procedure TfrmTermometroMonitor.tmrAutoSaveTimer(Sender: TObject);
begin
  if chkAutoSave.Checked and FLastReading.IsValid then
  begin
    tmrAutoSave.Interval := speInterval.Value * 1000;

    TTask.Run(
      TProc(
        procedure
        var
          LReading: TMeatMeetReading;
        begin
          LReading := FLastReading;
          SaveReadingToDB(LReading);
          TThread.Queue(nil,
            TThreadProcedure(
              procedure
              begin
                AddLog('Lectura guardada en BD.');
              end));
        end));
  end;
end;

procedure TfrmTermometroMonitor.btnExportClick(Sender: TObject);
var
  LStrings: TStringList;
  LBookmark: TBookmark;
begin
  if mdLog.IsEmpty then
  begin
    ShowMessage('No hay datos para exportar.');
    Exit;
  end;

  if SaveDialog.Execute then
  begin
    LStrings := TStringList.Create;
    try
      LStrings.Add('FechaHora;Zona1;Zona2;Zona3;Zona4;Zona5;Ambiente;Bateria');

      mdLog.DisableControls;
      LBookmark := mdLog.GetBookmark;
      try
        mdLog.First;
        while not mdLog.Eof do
        begin
          LStrings.Add(
            FormatDateTime('yyyy-mm-dd hh:nn:ss', mdLog.FieldByName('FechaHora').AsDateTime) + ';' +
            mdLog.FieldByName('Zona1').AsString + ';' +
            mdLog.FieldByName('Zona2').AsString + ';' +
            mdLog.FieldByName('Zona3').AsString + ';' +
            mdLog.FieldByName('Zona4').AsString + ';' +
            mdLog.FieldByName('Zona5').AsString + ';' +
            mdLog.FieldByName('Ambiente').AsString + ';' +
            mdLog.FieldByName('Bateria').AsString);
          mdLog.Next;
        end;
      finally
        mdLog.GotoBookmark(LBookmark);
        mdLog.FreeBookmark(LBookmark);
        mdLog.EnableControls;
      end;

      LStrings.SaveToFile(SaveDialog.FileName);
      AddLog('CSV exportado: ' + SaveDialog.FileName);
      ShowMessage('Datos exportados correctamente.');
    finally
      LStrings.Free;
    end;
  end;
end;

procedure TfrmTermometroMonitor.btnClearClick(Sender: TObject);
begin
  mdLog.EmptyDataSet;
  AddLog('Historial limpiado.');
end;

procedure TfrmTermometroMonitor.dbgLogTitleClick(Column: TColumn);
var
  LIndexName: string;
begin
  if Column.Field = nil then Exit;

  LIndexName := 'idx_' + Column.Field.FieldName;

  if mdLog.IndexName = LIndexName then
  begin
    mdLog.IndexName := '';
    mdLog.Indexes.Clear;
    mdLog.IndexDefs.Clear;
    with mdLog.IndexDefs.AddIndexDef do
    begin
      Name := LIndexName + '_Desc';
      Fields := Column.Field.FieldName;
      Options := [ixDescending];
    end;
    mdLog.IndexName := LIndexName + '_Desc';
  end
  else
  begin
    mdLog.IndexName := '';
    mdLog.Indexes.Clear;
    mdLog.IndexDefs.Clear;
    with mdLog.IndexDefs.AddIndexDef do
    begin
      Name := LIndexName;
      Fields := Column.Field.FieldName;
    end;
    mdLog.IndexName := LIndexName;
  end;
end;

end.
