unit Main;

interface

uses
  Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, Winapi.ShellApi,
  System.Classes, Vcl.Graphics, Vcl.Controls, Vcl.Forms, Vcl.Dialogs,
  Vcl.StdCtrls, Vcl.ExtCtrls, Vcl.ComCtrls, Vcl.Grids,
  System.Generics.Collections, System.IniFiles,
  Auth.Service, Auth.Server.Module, Api.Server.Module, App.Server.Module,
  ExeInfo, Api.Service, Ws.Server.Module, Ws.Service;

type
  TFMain = class(TForm)
    btnApiSwaggerUI: TButton;
    btnExit: TButton;
    initTimer: TTimer;
    btnAuthSwaggerUI: TButton;
    ExeInfo1: TExeInfo;
    pgcMain: TPageControl;
    tabLog: TTabSheet;
    tabClients: TTabSheet;
    memoInfo: TMemo;
    sgClients: TStringGrid;
    procedure btnApiSwaggerUIClick(Sender: TObject);
    procedure btnExitClick(Sender: TObject);
    procedure ContactFormData(AText: String);
    procedure FormClose(Sender: TObject; var Action: TCloseAction);
    procedure initTimerTimer(Sender: TObject);
    procedure btnAuthSwaggerUIClick(Sender: TObject);
  strict private
    procedure StartServers;
    procedure RefreshClientList;
    function LogValue(const LabelName: string; const Value: string; FromIni: Boolean): string;
  private
    function LocalBrowserUrl(const Url: string): string;
  end;

var
  FMain: TFMain;

implementation

uses
  Common.Logging,
  Common.Config,
  Common.Ini,
  Sparkle.Utils,
  Api.Database,
  System.StrUtils;

{$R *.dfm}

{ --- Event Handlers --- }

procedure TFMain.btnExitClick(Sender: TObject);
begin
  Close;
end;

function TFMain.LocalBrowserUrl(const Url: string): string;
begin
  Result := StringReplace(Url, '://0.0.0.0:', '://localhost:', [rfIgnoreCase]);
end;

procedure TFMain.btnApiSwaggerUIClick(Sender: TObject);
var
  url: string;
begin
  url := TSparkleUtils.CombineUrlFast(ApiServerModule.XDataServer1.BaseUrl, 'swaggerui');
  ShellExecute(Handle, 'open', PChar(LocalBrowserUrl(url)), nil, nil, SW_SHOWNORMAL);
end;

procedure TFMain.btnAuthSwaggerUIClick(Sender: TObject);
var
  url: string;
begin
  url := TSparkleUtils.CombineUrlFast(AuthServerModule.XDataServer.BaseUrl, 'swaggerui');
  ShellExecute(Handle, 'open', PChar(LocalBrowserUrl(url)), nil, nil, SW_SHOWNORMAL);
end;

procedure TFMain.ContactFormData(AText: String);
begin
  if memoInfo.CanFocus then
    TThread.Queue(nil, procedure begin memoInfo.Lines.Add(AText); end)
  else
    TThread.Synchronize(nil, procedure begin memoInfo.Lines.Add(AText); end);
end;

procedure TFMain.initTimerTimer(Sender: TObject);
begin
  initTimer.Enabled := False;
  Caption := Caption + ' ver ' + ExeInfo1.FileVersion;
  ServerConfig := TServerConfig.Create;
  LoadIniEntries;
  LoadServerConfig;
  StartServers;
end;

procedure TFMain.FormClose(Sender: TObject; var Action: TCloseAction);
begin
  WsServerModule.OnClientsChanged := nil;

  ServerConfig.Free;
  IniEntries.Free;
  AuthServerModule.Free;
  ApiServerModule.Free;
  AppServerModule.Free;
  WsServerModule.Free;
end;

{ --- Connected Client List --- }

procedure TFMain.RefreshClientList;
var
  snapshots: TArray<TConnectedClientSnapshot>;
  i: Integer;
begin
  snapshots := WsServerModule.GetClientSnapshots;

  sgClients.RowCount := Length(snapshots) + 1;  // +1 for header row

  // Header
  sgClients.Cells[0, 0] := 'Connection ID';
  sgClients.Cells[1, 0] := 'User ID';
  sgClients.Cells[2, 0] := 'Connected At';

  for i := 0 to Length(snapshots) - 1 do
  begin
    sgClients.Cells[0, i + 1] := snapshots[i].ConnectionId;
    sgClients.Cells[1, i + 1] := snapshots[i].UserId;
    sgClients.Cells[2, i + 1] :=
      FormatDateTime('yyyy-mm-dd hh:nn:ss', snapshots[i].ConnectedAt);
  end;

  tabClients.Caption :=
    Format('Connected Clients (%d)', [Length(snapshots)]);
end;

{ --- Helpers --- }

function TFMain.LogValue(const LabelName: string; const Value: string; FromIni: Boolean): string;
begin
  Result := LabelName + ': ' + Value + IfThen(FromIni, ' [from ini]', ' [default]');
end;

procedure TFMain.StartServers;
begin
  Logger.Log(1, '');
  Logger.Log(1, '*******************************************************');
  Logger.Log(1, '*                  emiMobile Server                   *');
  Logger.Log(1, Format('*               Version: %s', [FMain.ExeInfo1.FileVersion]));
  Logger.Log(1, '*            Developed by EM Systems, Inc.            *');
  Logger.Log(1, '*******************************************************');
  Logger.Log(1, '');

  Logger.Log(1, '--- Settings ---');
  Logger.Log(1, LogValue('--Settings->LogFileNum', IniEntries.LogFileNum.ToString, IniEntries.LogFileNumFromIni));
  Logger.Log(1, LogValue('--Settings->webClientVersion', IniEntries.WebClientVersion, IniEntries.WebClientVersionFromIni));

  Logger.Log(1, '');
  Logger.Log(1, '--- URLs ---');
  try
    AuthServerModule := TAuthServerModule.Create(Self);
    AuthServerModule.StartAuthServer(ServerConfig.url, AUTH_MODEL);

    ApiServerModule := TApiServerModule.Create(Self);
    ApiServerModule.StartApiServer(ServerConfig.url, API_MODEL);

    AppServerModule := TAppServerModule.Create(Self);
    AppServerModule.StartAppServer(ServerConfig.url);

    WsServerModule := TWsServerModule.Create(Self);
    WsServerModule.OnClientsChanged := RefreshClientList;
    WsServerModule.StartWsServer(ServerConfig.url, WS_MODEL);
  except
    on E: Exception do
      Logger.Log(2, 'Failed to start server modules: ' + E.Message);
  end;

  // Initialise the grid headers even before any client connects.
  RefreshClientList;
end;

end.
