unit Unit1;

interface

uses
  Winapi.Windows, Winapi.Messages,
  System.SysUtils, System.Variants, System.Classes,
  Vcl.Graphics, Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls,
  IdBaseComponent, IdComponent, IdTCPConnection, IdTCPClient,
  IdIOHandler, IdIOHandlerSocket, IdIOHandlerStack, IdGlobal, Vcl.ExtCtrls,
  Vcl.Buttons;

type
  TForm1 = class(TForm)
    ButtonConnect: TButton;
    ButtonDisconnect: TButton;
    LabelStatus: TLabel;
    TCPClient: TIdTCPClient;
    ButtonClosePump: TButton;
    Button1: TButton;
    Edit1: TEdit;
    gp: TEdit;
    gr: TEdit;
    dp: TEdit;
    dr: TEdit;
    kr: TEdit;
    rc: TEdit;
    Label1: TLabel;
    Label2: TLabel;
    Label3: TLabel;
    Label4: TLabel;
    Label5: TLabel;
    Label6: TLabel;
    Button2: TButton;
    Label7: TLabel;
    surtidor1: TLabel;
    Label8: TLabel;
    surtidor2: TLabel;
    Label9: TLabel;
    surtidor3: TLabel;
    Label10: TLabel;
    surtidor4: TLabel;
    Label11: TLabel;
    surtidor5: TLabel;
    Label12: TLabel;
    surtidor6: TLabel;
    Label13: TLabel;
    surtidor7: TLabel;
    Label14: TLabel;
    surtidor8: TLabel;
    Label15: TLabel;
    surtidor9: TLabel;
    Label16: TLabel;
    surtidor10: TLabel;
    Label17: TLabel;
    surtidor11: TLabel;
    Label18: TLabel;
    surtidor12: TLabel;
    Label19: TLabel;
    surtidor13: TLabel;
    Label20: TLabel;
    surtidor14: TLabel;
    Label21: TLabel;
    surtidor15: TLabel;
    Label22: TLabel;
    surtidor16: TLabel;
    Label23: TLabel;
    surtidor17: TLabel;
    Label24: TLabel;
    surtidor18: TLabel;
    Label25: TLabel;
    surtidor19: TLabel;
    Label26: TLabel;
    surtidor20: TLabel;
    Timer1: TTimer;
    SpeedButton1: TSpeedButton;
    Button3: TButton;
    procedure ButtonConnectClick(Sender: TObject);
    procedure ButtonDisconnectClick(Sender: TObject);
    procedure ButtonClosePumpClick(Sender: TObject);
    procedure Button1Click(Sender: TObject);
    procedure FormCreate(Sender: TObject);
    procedure Button2Click(Sender: TObject);
    procedure Timer1Timer(Sender: TObject);
    procedure Button3Click(Sender: TObject);
  private
    FLastStatus: array[1..20] of string;
    FAuthSentCount: array[1..20] of Integer;
    FSurtidorLabels: array[1..20] of TLabel;

    function GetPumpId3: string;
    procedure UpdateStatus(const AMsg: string; AColor: TColor);
    procedure EnsureConnected;
    procedure SendRawCommand(const ACmd: string);
    function ExtractStatusFromString(const Response, Prefix: string): string;
    function ReceiveResponse: string;
    procedure UpdatePumpStatus(PumpNumber: Integer; const Status: string);
    procedure SendAuthorizationCommand(PumpId: string);
    procedure CheckPumpStatus(PumpNumber: Integer);
  public
  end;

const
  DEFAULT_PORT = 3011;

var
  Form1: TForm1;

implementation

{$R *.dfm}

procedure TForm1.FormCreate(Sender: TObject);
var
  i: Integer;
begin
  TCPClient.Port := DEFAULT_PORT;
  TCPClient.ConnectTimeout := 3000;
  TCPClient.ReadTimeout := 3000;

  if Assigned(TCPClient.IOHandler) then
  begin
    TCPClient.IOHandler.DefStringEncoding := IndyTextEncoding_ASCII;
  end;

  if Trim(Edit1.Text) = '' then
    Edit1.Text := '001';

  // Asignar los labels a cada surtidor
  FSurtidorLabels[1] := surtidor1;
  FSurtidorLabels[2] := surtidor2;
  FSurtidorLabels[3] := surtidor3;
  FSurtidorLabels[4] := surtidor4;
  FSurtidorLabels[5] := surtidor5;
  FSurtidorLabels[6] := surtidor6;
  FSurtidorLabels[7] := surtidor7;
  FSurtidorLabels[8] := surtidor8;
  FSurtidorLabels[9] := surtidor9;
  FSurtidorLabels[10] := surtidor10;
  FSurtidorLabels[11] := surtidor11;
  FSurtidorLabels[12] := surtidor12;
  FSurtidorLabels[13] := surtidor13;
  FSurtidorLabels[14] := surtidor14;
  FSurtidorLabels[15] := surtidor15;
  FSurtidorLabels[16] := surtidor16;
  FSurtidorLabels[17] := surtidor17;
  FSurtidorLabels[18] := surtidor18;
  FSurtidorLabels[19] := surtidor19;
  FSurtidorLabels[20] := surtidor20;

  for i := 1 to 20 do
  begin
    FLastStatus[i] := '';
    FAuthSentCount[i] := 0;
  end;

  UpdateStatus('Desconectado', clRed);
end;

procedure TForm1.UpdateStatus(const AMsg: string; AColor: TColor);
begin
  LabelStatus.Caption := AMsg;
  LabelStatus.Font.Color := AColor;
end;

function TForm1.GetPumpId3: string;
var
  n: Integer;
  s: string;
begin
  s := Trim(Edit1.Text);
  if s = '' then
    s := '1';

  if TryStrToInt(s, n) then
    Result := Format('%.3d', [n])
  else
  begin
    s := StringReplace(s, ' ', '', [rfReplaceAll]);
    if Length(s) >= 3 then
      Result := Copy(s, Length(s) - 2, 3)
    else
      Result := StringOfChar('0', 3 - Length(s)) + s;
  end;
end;

procedure TForm1.EnsureConnected;
begin
  if TCPClient.Connected then
    Exit;

  TCPClient.Connect;
end;

procedure TForm1.SendRawCommand(const ACmd: string);
begin
  if not TCPClient.Connected then
    raise Exception.Create('No hay conexión activa.');

  TCPClient.IOHandler.InputBuffer.Clear;
  TCPClient.IOHandler.DefStringEncoding := IndyTextEncoding_ASCII;
  TCPClient.IOHandler.Write(ACmd);
  TCPClient.IOHandler.WriteBufferFlush;
end;

function TForm1.ReceiveResponse: string;
begin
  Result := '';

  try
    TCPClient.IOHandler.CheckForDataOnSource(3000);
    if not TCPClient.IOHandler.InputBufferIsEmpty then
      Result := TCPClient.IOHandler.InputBufferAsString;
  except
    on E: Exception do
      UpdateStatus('Error al recibir respuesta: ' + E.Message, clRed);
  end;
end;

function TForm1.ExtractStatusFromString(const Response, Prefix: string): string;
var
  PosStart, PosEnd: Integer;
  SubStr: string;
begin
  Result := '';

  PosStart := Pos(Prefix, Response);
  if PosStart = 0 then
    Exit;

  PosStart := PosStart + Length(Prefix);
  SubStr := Copy(Response, PosStart, Length(Response) - PosStart + 1);

  PosEnd := Pos('|', SubStr);
  if PosEnd = 0 then
    Result := SubStr
  else
    Result := Copy(SubStr, 1, PosEnd - 1);
end;

procedure TForm1.UpdatePumpStatus(PumpNumber: Integer; const Status: string);
begin
  if (PumpNumber < 1) or (PumpNumber > 20) then
    Exit;

  if SameText(Status, 'IDLE') then
    FSurtidorLabels[PumpNumber].Caption := 'IDLE'
  else if SameText(Status, 'CALLING') then
    FSurtidorLabels[PumpNumber].Caption := 'CALLING'
  else if SameText(Status, 'FUELLING') then
    FSurtidorLabels[PumpNumber].Caption := 'FUELLING'
  else if SameText(Status, 'CLOSED') then
    FSurtidorLabels[PumpNumber].Caption := 'CLOSED'
  else if SameText(Status, 'STARTING') then
    FSurtidorLabels[PumpNumber].Caption := 'STARTING'
  else if SameText(Status, 'ERROR') then
    FSurtidorLabels[PumpNumber].Caption := 'ERROR'
  else
    FSurtidorLabels[PumpNumber].Caption := 'No reconocido';
end;

procedure TForm1.SendAuthorizationCommand(PumpId: string);
var
  Command: string;
  i: Integer;
begin
  Command := '00044|5|2||POST|REQ_PUMP_AUTH_ID_' + PumpId + '|||OS=CALLING|^';
  
  try
    for i := 1 to 4 do
    begin
      SendRawCommand(Command);
      Sleep(50);
    end;
  except
    on E: Exception do
      UpdateStatus('Error al enviar autorización P' + PumpId + ': ' + E.Message, clRed);
  end;
end;

procedure TForm1.CheckPumpStatus(PumpNumber: Integer);
var
  Command, Response, StatusSurtidor, PumpId: string;
begin
  PumpId := Format('%.3d', [PumpNumber]);
  Command := '00035|5|2||POST|REQ_PUMP_STATUS_ID_' + PumpId + '||||^';

  try
    SendRawCommand(Command);
    Response := ReceiveResponse;

    if Response <> '' then
    begin
      StatusSurtidor := ExtractStatusFromString(Response, 'ST=');
      
      // Actualizar el label del surtidor correspondiente
      UpdatePumpStatus(PumpNumber, StatusSurtidor);
      
      // Si el estado cambió a CALLING y no hemos enviado la autorización
      if SameText(StatusSurtidor, 'CALLING') then
      begin
        if not SameText(FLastStatus[PumpNumber], 'CALLING') then
        begin
          FAuthSentCount[PumpNumber] := 0;
        end;
        
        if FAuthSentCount[PumpNumber] < 4 then
        begin
          SendAuthorizationCommand(PumpId);
          FAuthSentCount[PumpNumber] := 4;
          UpdateStatus('Autorización enviada a surtidor ' + IntToStr(PumpNumber), clGreen);
        end;
      end
      else
      begin
        if not SameText(StatusSurtidor, FLastStatus[PumpNumber]) then
        begin
          FAuthSentCount[PumpNumber] := 0;
        end;
      end;
      
      FLastStatus[PumpNumber] := StatusSurtidor;
    end;
  except
    on E: Exception do
      // Silenciar errores individuales
  end;
end;

procedure TForm1.Timer1Timer(Sender: TObject);
var
  i: Integer;
begin
  try
    EnsureConnected;
    
    for i := 1 to 20 do
    begin
      CheckPumpStatus(i);
    end;
    
    UpdateStatus('Monitoreando 20 surtidores...', clBlue);
  except
    on E: Exception do
      UpdateStatus('Error al procesar estados: ' + E.Message, clRed);
  end;
end;

procedure TForm1.ButtonConnectClick(Sender: TObject);
begin
  try
    TCPClient.Port := DEFAULT_PORT;

    EnsureConnected;

    if TCPClient.Connected then
      UpdateStatus('Conectado', clGreen);
  except
    on E: Exception do
      UpdateStatus('No se pudo conectar: ' + E.Message, clRed);
  end;
end;

procedure TForm1.ButtonDisconnectClick(Sender: TObject);
var
  i: Integer;
begin
  try
    if TCPClient.Connected then
    begin
      TCPClient.Disconnect;
      UpdateStatus('Desconectado', clRed);
      
      for i := 1 to 20 do
      begin
        FLastStatus[i] := '';
        FAuthSentCount[i] := 0;
        FSurtidorLabels[i].Caption := 'Esperando...';
      end;
    end
    else
      UpdateStatus('No hay conexión activa', clBlack);
  except
    on E: Exception do
      UpdateStatus('Error al desconectar: ' + E.Message, clRed);
  end;
end;

procedure TForm1.Button1Click(Sender: TObject);
var
  Command: string;
begin
  Command := '00033|5|2||POST|REQ_PUMP_OPEN_ID_' + GetPumpId3 + '||||^';

  try
    EnsureConnected;
    SendRawCommand(Command);
    UpdateStatus('Instrucción enviada: Abrir surtidor', clBlue);
  except
    on E: Exception do
      UpdateStatus('Error al enviar (Abrir): ' + E.Message, clRed);
  end;
end;

procedure TForm1.Button2Click(Sender: TObject);
var
  Command, Command2: string;
begin
  Command := '00218|5|2||POST|REQ_PRICES_SET_NEW_PRICE_CHANGE|||QTY=6|G01NR=1|G01LV=1|G01PR='+gp.Text+'|G02NR=2|G02LV=1|G02PR='+gr.Text;
  Command2 := '|G03NR=4|G03LV=1|G03PR='+dr.Text+'|G04NR=3|G04LV=1|G04PR='+dp.Text+'|G05NR=5|G05LV=1|G05PR='+kr.Text+'|G06NR=6|G06LV=1|G06PR='+rc.Text+'||^';

  try
    EnsureConnected;
    SendRawCommand(Command + Command2);
    UpdateStatus('Instrucción enviada: Cambio de precio', clBlue);
  except
    on E: Exception do
      UpdateStatus('Error al enviar instrucción: ' + E.Message, clRed);
  end;
end;

procedure TForm1.Button3Click(Sender: TObject);
begin
    Timer1.Enabled:=False;
    ShowMessage('Tarea finalizada');
end;

Procedure TForm1.ButtonClosePumpClick(Sender: TObject);
var
  Command: string;
begin
  Command := '00034|5|2||POST|REQ_PUMP_CLOSE_ID_' + GetPumpId3 + '||||^';

  try
    EnsureConnected;
    SendRawCommand(Command);
    UpdateStatus('Instrucción enviada: Cerrar surtidor', clBlue);
  except
    on E: Exception do
      UpdateStatus('Error al enviar (Cerrar): ' + E.Message, clRed);
  end;
end;

end.