unit MainForm;

{$mode objfpc}{H+}

interface

uses
  Classes, SysUtils, FileUtil, Forms, Controls, Graphics, Dialogs, StdCtrls,
  ComCtrls, Spin, ExtCtrls, Grids, TMS.MQTT.Client, TMS.MQTT.Global, IdHTTP,
  DCPrc4, DCPsha256, IniFiles, uHCTypeDefs,
  uHCAnnexDefs, uHCAnnexHelpers, types;

type

  { TfrmMain }

  TfrmMain = class(TForm)
    ckbCredentials: TCheckBox;
    edtUsername: TEdit;
    edtPassword: TEdit;
    Label1: TLabel;
    lblUser: TLabel;
    lblMQTTConnectionStatus: TLabel;
    lblPassword: TLabel;
    memMQTT: TMemo;
    MQTTClient: TTMSMQTTClient;
    pnlLeft: TPanel;
    pnlDLLInit: TPanel;
    pnlRight: TPanel;
    pnlCredentials: TPanel;
    Splitter1: TSplitter;
    tmrMQTTConnect: TTimer;
    memDLLInit: TMemo;
    tshDLLInit: TTabSheet;
    PageControl1: TPageControl;
    procedure ckbCredentialsChange(Sender: TObject);
    procedure HHQueueEventMessageClick(Sender: TObject);
    procedure MQTTClientConnectedStatusChanged(ASender: TObject;
      const AConnected: Boolean; AStatus: TTMSMQTTConnectionStatus);
    procedure tmrMQTTConnectTimer(Sender: TObject);
  private
    { private declarations }
  public
    { public declarations }
    // ALE 20130812 Big bad pointers that hold most of the game data
    PStartGroup: PHCGroup;
    PHarpEventHead: PHCHarpoonEvent;
    PSonobuoyRegionHead: PHCSonobuoyRegion;
    PAllDeadUnitsPtr: PHCDeadListItem;
    PWeatherGroups: PHCGroup;
    PVictoryConditionsList: PHCVictoryConditions;
    // ALE 20130812 transient pointers
    PSelectedUnit: PHCUnit;
    // ALE 20130812 misc globals
    //blnInvestigatingStructs: Boolean;
    // ALE 20130908 pointers to functions exported by the GE
    HCQueueHarpoonEvent: THCQueueHarpoonEvent;
    NewPtr: THCNewPtr;
    DisposPtr: THCDisposPtr;
    HCdllCheckLatLonDepth: THCdllCheckLatLonDepth;
    HCannex_id_to_disk_id: THCannex_id_to_disk_id;
    HCdisk_id_to_annex_id: THCdisk_id_to_annex_id;
    HCMessageLogLine: THCMessageLogLine;
    procedure MQTTPublish(const pTopic, pMessage: String);
    function EncodeString(const pString: String): String;
    function DecodeString(const pString: String): String;
  end;

var
  frmMain: TfrmMain;

implementation

uses uLazHarpoonEventsMQTTGlobals, uHCDefines, uHCEventHelpers;

{$R *.lfm}

{ TfrmMain }

// ALE 20130908 An example of setting up a HarpoonEvent that
//  will go in the HarpoonEventHead list of events that holds
//  all of the events that are not associated with course
//  points.
//  HCQueueHarpoonEvent will add it to the HarpoonEventHead
//   list in the proper order.
procedure TfrmMain.HHQueueEventMessageClick(Sender: TObject);
var
  PHarpoonEvent: PHCHarpoonEvent;
  PEventMessage: PHCMessageEvent;
begin
  PHarpoonEvent := NewPtr(SizeOf(THCHarpoonEvent));
  PEventMessage := NewPtr(SizeOf(THCMessageEvent));

  PHarpoonEvent^.in_scroll_flag := CFALSE;
  PHarpoonEvent^.DataPtr := PEventMessage;
  PHarpoonEvent^.flag := MESSAGE_EVENT or TimeDelta;
  PHarpoonEvent^.Next := nil;
  PHarpoonEvent^.ourGroup := nil;
  PHarpoonEvent^.UnitID := 0;
  PHarpoonEvent^.Time := 5; // ALE 20130908 Show us message 5 game seconds from now
  PEventMessage^.Message := FormatDateTime('yyyy-mm-dd hh:nn:ss', Now()) +
    #10 + ' Message Created';
  byte(uLazHarpoonEventsMQTTGlobals.PDLLBusy^) := CTRUE;
  HCQueueHarpoonEvent(PHarpoonEvent);
  byte(uLazHarpoonEventsMQTTGlobals.PDLLBusy^) := CFALSE;
end;

procedure TfrmMain.ckbCredentialsChange(Sender: TObject);
var
  lIniHC: TIniFile;
  lStr: String;
begin
  if ckbCredentials.Checked then
  begin
    lIniHC := TIniFile.Create(ExtractFilePath(Application.ExeName) + HCINIFILENAME);
    lIniHC.WriteString('ExportDLL_lazHarpoonEventsMQTT', 'MQTT_Username', edtUsername.Text);
    //lIniHC.WriteString('ExportDLL_lazHarpoonEventsMQTT', 'MQTT_Password', 'BlahBlahBlah');
    //lEdit:=edtPassword.Text;
    lStr:=EncodeString(edtPassword.Text);
    lIniHC.WriteString('ExportDLL_lazHarpoonEventsMQTT', 'MQTT_Password', lStr);
    lIniHC.Free;
  end;
end;

//  HCMessageLogLine(PAnsiChar(MLedtMessageToPost.Text));

procedure TfrmMain.MQTTClientConnectedStatusChanged(ASender: TObject;
  const AConnected: Boolean; AStatus: TTMSMQTTConnectionStatus);
var
  lIniHC: TIniFile;
begin
  if (AConnected) then
  begin
    tmrMQTTConnect.Enabled:=False;
    { Done in checkbox onChange
    lIniHC := TIniFile.Create(ExtractFilePath(Application.ExeName) + HCINIFILENAME);
    lIniHC.WriteString('ExportDLL_lazHarpoonEventsMQTT', 'MQTT_Username', edtUsername.Text);
    lIniHC.WriteString('ExportDLL_lazHarpoonEventsMQTT', 'MQTT_Password', EncodeString(edtPassword.Text));
    lIniHC.Free;
    }
  end;

  case AStatus of
    TTMSMQTTConnectionStatus.csConnected:
      begin
      lblMQTTConnectionStatus.Caption:='MQTT Connection: csConnected';
      memMQTT.Lines.Insert(0, FormatDateTime('hh:nn:ss.zzz ', Now()) + 'Connected!');
      end;
    TTMSMQTTConnectionStatus.csConnecting:
    begin
      lblMQTTConnectionStatus.Caption:='MQTT Connection: csConnecting';
      memMQTT.Lines.Insert(0, FormatDateTime('hh:nn:ss.zzz ', Now()) + 'Connecting');
    end;
    TTMSMQTTConnectionStatus.csConnectionLost:
      begin
      lblMQTTConnectionStatus.Caption:='MQTT Connection: csConnectionLost';
      memMQTT.Lines.Insert(0, FormatDateTime('hh:nn:ss.zzz ', Now()) + 'Connection lost, triggering Connect');
      tmrMQTTConnect.Enabled:=True;
      end;
    TTMSMQTTConnectionStatus.csNotConnected:
      lblMQTTConnectionStatus.Caption:='MQTT Connection: csNotConnected';
    TTMSMQTTConnectionStatus.csReconnecting:
      lblMQTTConnectionStatus.Caption:='MQTT Connection: csReconnecting';
    TTMSMQTTConnectionStatus.csConnectionRejected_ClientNotAuthorized:
      lblMQTTConnectionStatus.Caption:='MQTT Connection: csConnectionRejected_ClientNotAuthorized';
    TTMSMQTTConnectionStatus.csConnectionRejected_InvalidCredentials:
      lblMQTTConnectionStatus.Caption:='MQTT Connection: csConnectionRejected_InvalidCredentials';
    TTMSMQTTConnectionStatus.csConnectionRejected_InvalidIdentifier:
      lblMQTTConnectionStatus.Caption:='MQTT Connection: csConnectionRejected_InvalidIdentifier';
    TTMSMQTTConnectionStatus.csConnectionRejected_InvalidProtocolVersion:
      lblMQTTConnectionStatus.Caption:='MQTT Connection: csConnectionRejected_InvalidProtocolVersion';
    TTMSMQTTConnectionStatus.csConnectionRejected_ServerUnavailable:
      lblMQTTConnectionStatus.Caption:='MQTT Connection: csConnectionRejected_ServerUnavailable';
  end;
end;

procedure TfrmMain.tmrMQTTConnectTimer(Sender: TObject);
begin
  //tmrMQTTConnect.Enabled:=False;
  //memMQTT.Lines.Insert(0, FormatDateTime('hh:nn:ss.zzz ', Now()) + 'Connect timer triggered');
  //if ((MQTTClient.ConnectionStatus = TTMSMQTTConnectionStatus.csNotConnected)
  //    OR (MQTTClient.ConnectionStatus = TTMSMQTTConnectionStatus.csConnectionLost)) then
  if (NOT ((MQTTClient.ConnectionStatus = TTMSMQTTConnectionStatus.csConnecting)
      OR (MQTTClient.ConnectionStatus = TTMSMQTTConnectionStatus.csReconnecting))) then
  begin
    //if ((edtUsername.Text <> '') AND (edtPassword.Text <> '')) then
    if (ckbCredentials.Checked) then
    begin
      if (edtUsername.Text <> MQTTClient.Credentials.Username) then
        MQTTClient.Credentials.Username := edtUsername.Text;
      if (edtPassword.Text <> MQTTClient.Credentials.Password) then
        MQTTClient.Credentials.Password := edtPassword.Text;
      MQTTClient.Connect(True);
      memMQTT.Lines.Insert(0, FormatDateTime('hh:nn:ss.zzz ', Now()) + 'Connect timer Connecting');
    end;
  end;
end;

procedure TfrmMain.MQTTPublish(const pTopic, pMessage: String);
var
  lTopicBase: String;
begin
  lTopicBase := StringReplace('TGP/HC/uuid/', 'uuid', uLazHarpoonEventsMQTTGlobals.gGUID, []);
  if ((edtUsername.Text <> '') AND (edtPassword.Text <> '')) then
  begin
    if (edtUsername.Text <> MQTTClient.Credentials.Username) then
      MQTTClient.Credentials.Username := edtUsername.Text;
    if (edtPassword.Text <> MQTTClient.Credentials.Password) then
      MQTTClient.Credentials.Password := edtPassword.Text;

    byte(uLazHarpoonEventsMQTTGlobals.PDLLBusy^) := CTRUE;
    Application.ProcessMessages;
    MQTTClient.Publish(lTopicBase + pTopic, pMessage);
    Application.ProcessMessages;
    byte(uLazHarpoonEventsMQTTGlobals.PDLLBusy^) := CFALSE;
  end;
end;

function TfrmMain.EncodeString(const pString: String): String;
var
  Cipher: TDCP_rc4;
begin
  Cipher:= TDCP_rc4.Create(Self);
  Cipher.InitStr(MAGIC_DATA, TDCP_sha256);         // initialize the cipher with a hash of the passphrase
  Result:= Cipher.EncryptString(pString);
  Cipher.Burn;
  Cipher.Free;
end;

function TfrmMain.DecodeString(const pString: String): String;
var
  Cipher: TDCP_rc4;
begin
  if (pString <> '') then
  begin
    Cipher:= TDCP_rc4.Create(Self);
    Cipher.InitStr(MAGIC_DATA, TDCP_sha256);         // initialize the cipher with a hash of the passphrase
    Result:= Cipher.DecryptString(pString);
    Cipher.Burn;
    Cipher.Free;
  end
  else
    Result:='';
end;

end.
