unit uHCConflictDirectCostViewer;

interface

uses
  System.SysUtils, System.Types, System.UITypes, System.Classes,
  System.Variants,
  FMX.Types, FMX.Controls, FMX.Forms, FMX.Graphics, FMX.Dialogs, IPPeerClient,
  IPPeerServer, System.Tether.Manager, System.Tether.AppProfile, FMX.StdCtrls,
  FMX.Controls.Presentation,
{$IFDEF MSWINDOWS}
  Windows,
{$ENDIF}
  IdGlobal, IdException,
  IdSocketHandle, IdBaseComponent, IdComponent,
  IdIPMCastBase, IdIPMCastClient, IdStack,
  REST.JSON,
  uConflictDirectCostTypeDefs,
  TGPSvcDiscoverUDP, FMX.TabControl, FMX.TMSBaseControl, FMX.TMSGridCell,
  FMX.TMSGridOptions, FMX.TMSGridData, FMX.TMSCustomGrid, FMX.TMSGrid,
  FMX.ScrollBox, FMX.Memo;

type
  TfrmHCConflictDirectCostViewer = class(TForm)
    memStats: TMemo;
    lblGameTime: TLabel;
    lblGameTimeLeft: TLabel;
    btnActivate: TButton;
    Button1: TButton;
    pct: TTabControl;
    tabGS: TTabItem;
    tabGSGrid: TTabItem;
    lblGameTimeLeftGrid: TLabel;
    grdGS: TTMSFMXGrid;
    lblGameTimeGrid: TLabel;
    tabCDC: TTabItem;
    memCDC: TMemo;
    procedure FormCreate(Sender: TObject);
    procedure btnActivateClick(Sender: TObject);
    procedure FormDestroy(Sender: TObject);
    procedure Button1Click(Sender: TObject);
    procedure pctResize(Sender: TObject);
  private
    { Private declarations }
    MulticastClient: TTGPServiceDiscoveryUDPClient;
    procedure PrintGameStats(pConflictDirectCost: TMMFConflictDirectCost);
    procedure PrintGameStatsGrid(pConflictDirectCost: TMMFConflictDirectCost);
procedure IPMCastRead(Sender: TObject;
  const AData: TIdBytes; ABinding: TIdSocketHandle);
  public
    { Public declarations }
  end;

var
  frmHCConflictDirectCostViewer: TfrmHCConflictDirectCostViewer;

const
  version = 1; // ALE 20150820 TMMFConflictDirectCost structure version

implementation

{$R *.fmx}

uses uHCDefines, uHCDateTime, uHCTypeDefs;

procedure TfrmHCConflictDirectCostViewer.btnActivateClick(Sender: TObject);
begin
  MulticastClient.Active := True;
end;

procedure TfrmHCConflictDirectCostViewer.Button1Click(Sender: TObject);
var
  FLocalIPs: TIdStackLocalAddressList;
  lI: NativeInt;
  IdSH: TIdSocketHandle;
begin
{
 FLocalIPs := TIdStackLocalAddressList.Create;
  // ALE 20150903 tinker with listing local IPs for binding
  TIdStack.IncUsage;
  GStack.GetLocalAddressList(FLocalIPs);

  //for lI := 0 to FLocalIPs.Count - 1 do
  //begin
  //  ShowMessage(FLocalIPs[lI].IPAddress);
  //end;
  IdIPMCastClient.Bindings.Clear;
  for lI := 0 to FLocalIPs.Count - 1 do
  begin
    if FLocalIPs[lI].IPVersion = TIdIPVersion.Id_IPv4 then
    begin
      IdSH := IdIPMCastClient.Bindings.Add;
      IdSH.SetBinding(FLocalIPs[lI].IPAddress, IdIPMCastClient.DefaultPort, FLocalIPs[lI].IPVersion);
    //ShowMessage(FLocalIPs[lI].IPAddress);
    end;
  end;
  IdIPMCastClient.Bindings.Clear;
  IdSH := IdIPMCastClient.Bindings.Add;
  IdSH.SetBinding('192.168.77.145', 45353, TIdIPVersion.Id_IPv4);
  IdIPMCastClient.Active := True;
  FLocalIPs.Free;
  }
end;

procedure TfrmHCConflictDirectCostViewer.FormCreate(Sender: TObject);
begin
  Caption := Caption + ' v.' + CHCConflictDirectCostVersion;
  MulticastClient := TTGPServiceDiscoveryUDPClient.Create(Self);
  MulticastClient.OnIPMCastRead := IPMCastRead;
  MulticastClient.Group := 'HCConflictDirectCost';
  MulticastClient.GroupAddress := '224.0.0.1';
  MulticastClient.Active := True;
end;

procedure TfrmHCConflictDirectCostViewer.FormDestroy(Sender: TObject);
begin
  MulticastClient.Active := False;
end;

procedure TfrmHCConflictDirectCostViewer.IPMCastRead(Sender: TObject;
  const AData: TIdBytes; ABinding: TIdSocketHandle);
begin
  TThread.Queue(nil,
    procedure
    var
      stlStatus: TStringList;
      strData: String;
      strJSON: String;
      lConflictDirectCost: TMMFConflictDirectCost;
      dtNow: THCDateTimeRec;
    begin
      stlStatus := TStringList.Create;
      strData := BytesToString(AData);
      stlStatus.CommaText := strData;
      if ((stlStatus.Count > 0) AND (stlStatus[0] = 'HCConflictDirectCost')) then
      begin
        // ALE 20150909 We get here if we are in the right multicast group
        if (stlStatus.Count > 2) then
        begin
          try
            strJSON := Copy(strData, AnsiPos('{', strData), Length(strData));
            lConflictDirectCost := TJson.JsonToObject<TMMFConflictDirectCost>(strJSON);
            if ((lConflictDirectCost.version > 0) And (lConflictDirectCost.version <> version))
            then
            begin
              MessageDlg
                (Format('Viewer Server (%d) and Viewer (%d) structure versions do not match',
                [lConflictDirectCost.version, version]),
                System.UITypes.TMsgDlgType.mtError,
                [System.UITypes.TMsgDlgBtn.mbOK], 0);
              // Application.Terminate;
            end;
            lblGameTimeLeft.Text := Format('GameTimeLeft: %0.0n seconds',
              [lConflictDirectCost.gametimeleft/1.0]);
            lblGameTimeLeftGrid.Text := lblGameTimeLeft.Text;
            dtNow := HCDateTimeInc(lConflictDirectCost.gamestartdatetime,
              lConflictDirectCost.gametime);
            lblGameTime.Text := 'GameDateTime: ' +
              Format('%4.4d-%2.2d-%2.2d %2.2d:%2.2d:%2.2d',
              [dtNow.year, dtNow.month, dtNow.day, dtNow.hour, dtNow.minute,
              dtNow.second]);
            lblGameTimeGrid.Text := lblGameTime.Text;
            PrintGameStats(lConflictDirectCost);
            PrintGameStatsGrid(lConflictDirectCost);
            memCDC.BeginUpdate;
            memCDC.Lines.Clear;
            memCDC.Lines.Add('Human Cost: ' + IntToStr(lConflictDirectCost.humancost[BLUESIDE]));
            memCDC.Lines.Add('Monetary Cost: ' + IntToStr(lConflictDirectCost.monetarycost[BLUESIDE]));
          except
            // ALE 20150909 we only care if we get valid data
            ShowMessage('Error decoding JSON');
          end;
        end;
      end;

      // memMulticastMessages.Lines.Insert(0, ABinding.PeerIP + ' says ' +
      // BytesToString(AData));
      lConflictDirectCost.Free;
      stlStatus.Free;
    end);

end;

procedure TfrmHCConflictDirectCostViewer.pctResize(Sender: TObject);
begin
  grdGS.Height := pct.Height - grdGS.Position.Y - 30;
  grdGS.Width := pct.Width - grdGS.Position.X - 8;
end;

procedure TfrmHCConflictDirectCostViewer.PrintGameStats(pConflictDirectCost: TMMFConflictDirectCost);
var
  r, c: NativeInt;
  i: Integer;

begin
  grdGS.BeginUpdate;
  grdGS.Clear;
  c := 0;
  r := 0;
  Inc(c); grdGS.Cells[c,r] := 'Blue';
  Inc(c); grdGS.Cells[c,r] := 'Red';
  Inc(c); grdGS.Cells[c,r] := 'Green';
  c := 0;
  r := 0;
  Inc(r); grdGS.Cells[c,r] := 'Carriers';
  Inc(r); grdGS.Cells[c,r] := 'Ships';
  Inc(r); grdGS.Cells[c,r] := 'Subs';
  Inc(r); grdGS.Cells[c,r] := 'Planes';
  Inc(r); grdGS.Cells[c,r] := 'Helicopters';
  Inc(r); grdGS.Cells[c,r] := 'Installations';
  Inc(r); grdGS.Cells[c,r] := 'AD Fixed';
  Inc(r); grdGS.Cells[c,r] := 'AD Mobile';
  Inc(r); grdGS.Cells[c,r] := 'Armored';
  Inc(r); grdGS.Cells[c,r] := 'Unarmored';

  for r := 0 to 9 do
  begin
    for c := 0 to SIDES-1 do
    begin
      grdGS.Cells[c+1,r+1] := Format('%0.0n', [pConflictDirectCost.killcounts[r][c]/1.0]);
    end;
  end;

  grdGS.AutoSizeColumn(0, True);
  grdGS.EndUpdate;
end;

procedure TfrmHCConflictDirectCostViewer.PrintGameStatsGrid(pConflictDirectCost: TMMFConflictDirectCost);
var
  i: NativeInt;
begin
  memStats.BeginUpdate;
  memStats.Lines.Clear;
  memStats.Lines.Add('            Blue    Red  Green');
  i := 0;
  memStats.Lines.Add(Format('Carriers   %5d  %5d  %5d',
    [pConflictDirectCost.killcounts[i][BLUESIDE], pConflictDirectCost.killcounts[i][REDSIDE],
    pConflictDirectCost.killcounts[i][GREENSIDE]]));
  Inc(i);
  memStats.Lines.Add(Format('Ships      %5d  %5d  %5d',
    [pConflictDirectCost.killcounts[i][BLUESIDE], pConflictDirectCost.killcounts[i][REDSIDE],
    pConflictDirectCost.killcounts[i][GREENSIDE]]));
  Inc(i);
  memStats.Lines.Add(Format('Subs       %5d  %5d  %5d',
    [pConflictDirectCost.killcounts[i][BLUESIDE], pConflictDirectCost.killcounts[i][REDSIDE],
    pConflictDirectCost.killcounts[i][GREENSIDE]]));
  Inc(i);
  memStats.Lines.Add(Format('Planes     %5d  %5d  %5d',
    [pConflictDirectCost.killcounts[i][BLUESIDE], pConflictDirectCost.killcounts[i][REDSIDE],
    pConflictDirectCost.killcounts[i][GREENSIDE]]));
  Inc(i);
  memStats.Lines.Add(Format('Heli       %5d  %5d  %5d',
    [pConflictDirectCost.killcounts[i][BLUESIDE], pConflictDirectCost.killcounts[i][REDSIDE],
    pConflictDirectCost.killcounts[i][GREENSIDE]]));
  Inc(i);
  memStats.Lines.Add(Format('Bases      %5d  %5d  %5d',
    [pConflictDirectCost.killcounts[i][BLUESIDE], pConflictDirectCost.killcounts[i][REDSIDE],
    pConflictDirectCost.killcounts[i][GREENSIDE]]));
  Inc(i);
  memStats.Lines.Add(Format('AD Fixed   %5d  %5d  %5d',
    [pConflictDirectCost.killcounts[i][BLUESIDE], pConflictDirectCost.killcounts[i][REDSIDE],
    pConflictDirectCost.killcounts[i][GREENSIDE]]));
  Inc(i);
  memStats.Lines.Add(Format('AD Mobile  %5d  %5d  %5d',
    [pConflictDirectCost.killcounts[i][BLUESIDE], pConflictDirectCost.killcounts[i][REDSIDE],
    pConflictDirectCost.killcounts[i][GREENSIDE]]));
  Inc(i);
  memStats.Lines.Add(Format('Armored    %5d  %5d  %5d',
    [pConflictDirectCost.killcounts[i][BLUESIDE], pConflictDirectCost.killcounts[i][REDSIDE],
    pConflictDirectCost.killcounts[i][GREENSIDE]]));
  Inc(i);
  memStats.Lines.Add(Format('Unarmored  %5d  %5d  %5d',
    [pConflictDirectCost.killcounts[i][BLUESIDE], pConflictDirectCost.killcounts[i][REDSIDE],
    pConflictDirectCost.killcounts[i][GREENSIDE]]));
  memStats.EndUpdate;
end;

end.
