unit MainForm;

{$mode objfpc}{$H+}

interface

uses
  Classes, SysUtils, FileUtil, Forms, Controls, Graphics, RTTICtrls,
  Dialogs, StdCtrls, ComCtrls, Spin, ExtCtrls, uHCTypeDefs, uHCDefines, uHCEventHelpers;

type

  { TfrmMain }

  TfrmMain = class(TForm)
    AGFlbxGroup: TListBox;
    AGFlbxUnit: TListBox;
    AGFbtnSrch: TButton;
    AGFlblUnitAlt: TLabel;
    AGFlblS: TLabel;
    AGFlblE: TLabel;
    AirGrpForm: TTabSheet;
    AGFspnbxUnitAlt: TSpinEdit;
    AGFspnbxLon: TSpinEdit;
    AGFspnbxLat: TSpinEdit;
    AGFbtnGrp1: TButton;
    AGFbtnGrp2: TButton;
    AGFbtnMeet: TButton;
    AGFbtnSqPat: TButton;
    Button1: TButton;
    Memo1: TMemo;
    SETmemHelp: TMemo;
    PathEvent: TTabSheet;
    TedbxPatrol: TEdit;
    SETlblMLcount: TLabel;
    MLtmemLog: TMemo;
    PageControl1: TPageControl;
    MessgLog: TTabSheet;
    Settings: TTabSheet;
    SETspnMLC: TSpinEdit;
    AGFspnbxPatrl: TSpinEdit;
    procedure AGFbtnSqPatEnter(Sender: TObject);
    procedure AGFbtnGrp1Enter(Sender: TObject);
    procedure AGFbtnGrp2Enter(Sender: TObject);
    procedure AGFbtnMeetEnter(Sender: TObject);
    procedure AGFbtnSrchEnter(Sender: TObject);
    procedure AGFgrpSrch();
    procedure AGFlblEClick(Sender: TObject);
    procedure AGFlblSClick(Sender: TObject);
    procedure AGFlbxGroupSelectionChange(Sender: TObject; User: boolean);
    procedure AGFlbxUnitSelectionChange(Sender: TObject; User: boolean);
    procedure AGFspnbxLonEditingDone(Sender: TObject);
    procedure AGFspnbxUnitAltEditingDone(Sender: TObject);
    procedure AGFUnitUpdate();
    procedure Button1Click(Sender: TObject);
    procedure DelPath(SGroup: PHCGroup);
    procedure SqPatrl(SGroup: PHCGroup);
    function LatFctGrp(SGroup: PHCGroup): double;
  private
    { private declarations }
  public
    { public declarations }
    PStartGroup, PSelectedGroup, SGroup1, SGroup2: PHCGroup;
    PSelectedUnit: PHCUnit;
    strMySide: string;
    dblLatFact: double;
    NewPtr: THCNewPtr;              //ALE added with GE/DLL code for pointers
    DisposPtr: THCDisposPtr;
    del_HarpoonEvent_list: THCdel_HarpoonEvent_list;
    function CountUnits(PGroup: PHCGroup): NativeUInt;
  end;

var
  frmMain: TfrmMain;

implementation

{$R *.lfm}

{ TfrmMain }

function TfrmMain.LatFctGrp(SGroup: PHCGroup): double;
begin
  Result := Cos((90 - (SGroup^.sLat / sliver1)) / 57.2957);
end;


procedure TfrmMain.AGFgrpSrch();
var
  PHCGThis: PHCGroup;
begin
  PSelectedUnit := nil;
  AGFspnbxUnitAlt.Value := 11;
  AGFspnbxLon.Value := 0;
  AGFspnbxLat.Value := 0;
  AGFbtnGrp1.Caption := 'Grp1';
  AGFbtnGrp2.Caption := 'Grp2';
  SGroup1 := nil;
  SGroup2 := nil;
  AGFlbxUnit.Items.Clear;
  AGFlbxGroup.Items.Clear;
  AGFlbxGroup.Sorted := True;
  PHCGThis := PStartGroup;

  while (PHCGThis <> nil) do
  begin
    if (format('%d', [PHCGThis^.side]) = strMySide) then
      AGFlbxGroup.Items.Add(PHCGThis^.id + format('-%d kts', [PHCGThis^.kts]));
    PHCGThis := PHCGThis^.toSouth;
  end;
end;

procedure TfrmMain.AGFlblEClick(Sender: TObject);
begin
  if (AGFlblE.Caption = 'E') then
    AGFlblE.Caption := 'W'
  else
    AGFlblE.Caption := 'E';
end;

procedure TfrmMain.AGFlblSClick(Sender: TObject);
begin
  if (AGFlblS.Caption = 'S') then
    AGFlblS.Caption := 'N'
  else
    AGFlblS.Caption := 'S';
end;

procedure TfrmMain.AGFbtnGrp1Enter(Sender: TObject);
begin
  AGFbtnGrp1.Caption := PSelectedGroup^.id;
  SGroup1 := PSelectedGroup;
end;

procedure TfrmMain.AGFbtnSqPatEnter(Sender: TObject);
begin
  if (PSelectedGroup <> nil) then
    SqPatrl(PSelectedGroup)
  else
    ShowMessage('no Group selected');
end;

procedure TfrmMain.AGFbtnGrp2Enter(Sender: TObject);
begin
  AGFbtnGrp2.Caption := PSelectedGroup^.id;
  SGroup2 := PSelectedGroup;
end;

procedure TfrmMain.AGFbtnMeetEnter(Sender: TObject);
var
  PPathEvent, PNextPathEvent: PHCHarpoonEvent;
  PMessageEvent: PHCMessageEvent;
  PJoinGroupEvent: PHCJoinGroupEvent;
  num_joins, joins_done: short;
  PUnit: PHCUnit;
  PThisSpeedAltEvent: PHCSpeedAltEvent;
begin    // should test for bases and stationary surface groups too
  //should test that group has air units
  if ((SGroup1^.Units^.Air^.Throttle = 0) or (SGroup2^.Units^.Air^.Throttle = 0)) then
    ShowMessage('a group is loitering')
  else
  begin
    DelPath(SGroup1);
    DelPath(SGroup2);
    SGroup1^.track^.pathEvents := nil;
    SGroup2^.track^.pathEvents := nil;
    SGroup1^.track^.DestLat := (SGroup1^.sLat div
      (SGroup1^.kts + SGroup2^.kts) * SGroup2^.kts + SGroup2^.sLat div
      (SGroup1^.kts + SGroup2^.kts) * SGroup1^.kts);
    SGroup2^.track^.DestLat := SGroup1^.track^.DestLat;
    SGroup1^.track^.DestLon := (SGroup1^.sLon div
      (SGroup1^.kts + SGroup2^.kts) * SGroup2^.kts + SGroup2^.sLon div
      (SGroup1^.kts + SGroup2^.kts) * SGroup1^.kts);
    // might need latfactor? ddt
    SGroup2^.track^.DestLon := SGroup1^.track^.DestLon;
    //testing start
    PPathEvent := NewPtr(SizeOf(THCHarpoonEvent));
    PMessageEvent := NewPtr(SizeOf(THCMessageEvent));
    PMessageEvent^.Message := PAnsiChar('Awaiting Orders');
    PPathEvent^.DataPtr := PMessageEvent;
    PPathEvent^.flag := MESSAGE_EVENT or TimeDelta;
    PPathEvent^.Time := 0;
    PPathEvent^.ourGroup := SGroup1;
    PPathEvent^.UnitID := SGroup1^.Units^.id;
    PPathEvent^.in_scroll_flag := 001;

    // ALE 20130831 Join the groups.  Tanker should be in Group 2, tankees in Group 1
    num_joins := CountUnits(SGroup1);
    PNextPathEvent := NewPtr(SizeOf(THCHarpoonEvent));
    PNextPathEvent^.flag := JOIN_EVENT or TimeDelta;
    PNextPathEvent^.DataPtr := NewPtr(SizeOf(short) + num_joins * SizeOf(THCJoinGroupEvent));
    short(PNextPathEvent^.DataPtr^) := num_joins;  // ALE 20130831 presumably we're joining all units to the tanker group
    PJoinGroupEvent := PHCJoinGroupEvent(NativeUInt(PNextPathEvent^.DataPtr) + SizeOf(num_joins));
    PUnit := SGroup1^.Units;
    for joins_done := 0 to num_joins - 1 do
    begin
      // ALE 20130828 We are joining SGroup1 to SGroup2
      PJoinGroupEvent^.JoinGroup := SGroup2^.id;
      PJoinGroupEvent^.SplitGroup := SGroup1^.id;
      PJoinGroupEvent^.UnitID := PUnit^.id;
      PJoinGroupEvent^.Number := PUnit^.Air^.PlatNow;

      // ALE 20130831 set up to join the next unit in SGroup1 to SGroup2
      PUnit := PUnit^.next;
      PJoinGroupEvent := PHCJoinGroupEvent(NativeUInt(PJoinGroupEvent) + SizeOf(THCJoinGroupEvent));
    end;


    PNextPathEvent^.Time := 0;
    PNextPathEvent^.ourGroup := SGroup1;
    PNextPathEvent^.UnitID := SGroup1^.Units^.id;
    PNextPathEvent^.in_scroll_flag := 001;
    PNextPathEvent^.Next := nil;

    PPathEvent^.Next := PNextPathEvent;

    SGroup1^.track^.pathEvents := PPathEvent;
    //testing end

  end;
end;

procedure TfrmMain.AGFbtnSrchEnter(Sender: TObject);
begin
  AGFgrpSrch();
end;

procedure TfrmMain.AGFlbxGroupSelectionChange(Sender: TObject; User: boolean);
var
  strSel: string;
  PHCGThis: PHCGroup;
  PHCUThis: PHCUnit;
begin
  PSelectedUnit := nil;
  strSel := copy(AGFlbxGroup.Items[AGFlbxGroup.ItemIndex], 1, 2);
  PHCGThis := PStartGroup;
  while (PHCGThis <> nil) do
  begin
    if (strSel = copy(PHCGThis^.id, 1, 2)) then
      break;
    PHCGThis := PHCGThis^.toSouth;
  end;
  if (PHCGThis = nil) then //the selected group is no longer valid
  begin                 //and list must be reset
    AGFgrpSrch();
    PHCGThis := PStartGroup;
  end;                                //list reset
  PSelectedGroup := PHCGThis;
  AGFlbxUnit.Items.Clear;
  AGFlbxUnit.Sorted := True;
  PHCUThis := PHCGThis^.Units;
  while (PHCUThis <> nil) do
  begin
    if (PHCUThis^.Air <> nil) then
      AGFlbxUnit.Items.Add(Format(
        '%0.3d: %d-%s AltBnd %2.2d, BWR: %.0n AA, %.0n ASu, %.1n AS',
        [PHCUThis^.id, PHCAirRec(PHCUThis^.Air)^.PlatNow,
        PHCUThis^.Name, PHCUThis^.AltBand, (PHCUThis^.BestAAWRange) / 10,
        (PHCUThis^.BestASuWRange) / 10, (PHCUThis^.BestAsWRange) / 10]));
    PHCUThis := PHCUThis^.Next;
  end;
end;

procedure TfrmMain.AGFlbxUnitSelectionChange(Sender: TObject; User: boolean);
var
  strSel: string;
  PHCUThis: PHCUnit;
begin
  strSel := AGFlbxUnit.Items[AGFlbxUnit.ItemIndex];
  PHCUThis := PSelectedGroup^.Units;
  while (PHCUThis <> nil) do
  begin
    if (Pos(Format('%0.3d', [PHCUThis^.id]), strSel) = 1) then
    begin
      break;
    end;
    PHCUThis := PHCUThis^.Next;
  end;
  PSelectedUnit := PHCUThis;
  AGFspnbxUnitAlt.ClearSelection;
  AGFspnbxUnitAlt.Value := PSelectedUnit^.AltBand;
  dblLatFact := Cos((90 - (PSelectedUnit^.sLat / sliver1)) / 57.2957);
  AGFspnbxLon.Value := (PselectedUnit^.sLon - PSelectedGroup^.sLon) /
    uHCDefines.NM1 * dblLatFact;
  AGFspnbxLat.Value := (PSelectedUnit^.sLat - PSelectedGroup^.sLat) / uHCDefines.NM1;
end;

procedure TfrmMain.AGFUnitUpdate();
begin
  AGFlbxUnit.Items.Strings[AGFlbxUnit.ItemIndex] :=
    Format('%0.3d: %d-%s AltBnd %2.2d BWR: %.0n AA, %.0n ASu, %.1n AS',
    [PSelectedUnit^.id, PHCAirRec(PSelectedUnit^.Air)^.PlatNow,
    PSelectedUnit^.Name, PSelectedUnit^.AltBand, (PSelectedUnit^.BestAAWRange) /
    10, (PSelectedUnit^.BestASuWRange) / 10, (PSelectedUnit^.BestAsWRange) / 10]);
end;

procedure TfrmMain.Button1Click(Sender: TObject);
var
  strSel: string;
  PEvent: PHCHarpoonEvent;
  PHCGThis: PHCGroup;
  PHCUThis: PHCUnit;
  //  PTrackLeg: PHCPathRec;
  PEventMessage: PHCMessageEvent;
  PJoinGroupEvent: PHCJoinGroupEvent;
  numjoins: short;
  intC: integer;
begin
  //  PTrackLeg:= SGroup1^.track;
  PEvent := SGroup1^.track^.pathEvents;
  Memo1.Clear;

  while (PEvent <> nil) do
  begin
    Memo1.Lines.Add(Format('Pointer to Event: 0x%p', [PEvent]));
    Memo1.Lines.Add(Format('Pointer to next Event: 0x%p', [PEvent^.Next]));
    Memo1.Lines.Add(Format('Event Type: %s',
      [HCEventType2String(PEvent^.flag and CommandMask)]));
    Memo1.Lines.Add(Format('Pointer to Group the Event applies to (if any): 0x%p',
      [PEvent^.ourGroup]));
    Memo1.Lines.Add(Format('UnitID the Event applies to (if any): %3.3d',
      [PEvent^.UnitID]));
    Memo1.Lines.Add(Format('Pointer to DataPtr: 0x%p', [PEvent^.DataPtr]));
    Memo1.Lines.Add(Format('Event Time: %8.8d', [PEvent^.Time]));
    Memo1.Lines.Add(Format('Event Time Type: %s',
      [HCEventTimeType2String(PEvent^.flag and TimeMask)]));
    Memo1.Lines.Add(Format('Event in_scroll_flag: %d', [PEvent^.in_scroll_flag]));
    if (PEvent^.flag and CommandMask = MESSAGE_EVENT) then
    begin
      PEventMessage := PEvent^.DataPtr;
      Memo1.Lines.Add(Format('Message: %s', [PEventMessage^.Message]));
    end
    else if (PEvent^.flag and CommandMask = JOIN_EVENT) then
    begin
      numjoins := short(PEvent^.DataPtr^);
      PJoinGroupEvent := PEvent^.DataPtr + sizeof(numjoins);
      for intC := 0 to numjoins - 1 do
      begin
        Memo1.Lines.Add(Format('Joining: %s unit=%d qty=%d to %s',
          [PJoinGroupEvent^.SplitGroup, PJoinGroupEvent^.UnitID,
          PJoinGroupEvent^.Number, PJoinGroupEvent^.JoinGroup]));
        PJoinGroupEvent := PHCJoinGroupEvent(NativeUInt(PJoinGroupEvent) +
          sizeof(THCJoinGroupEvent)); // ALE 20130828 move to next join
      end;
    end;
    PEvent := PEvent^.Next;
  end;
end;


procedure TfrmMain.AGFspnbxLonEditingDone(Sender: TObject);
begin
  if (AGFlblE.Caption = 'E') then
    PselectedUnit^.sLon := PSelectedGroup^.sLon + Round(
      uHCDefines.NM1 * (AGFspnbxLon.Value) / dblLatFact)
  else
    PselectedUnit^.sLon := PSelectedGroup^.sLon - Round(
      uHCDefines.NM1 * (AGFspnbxLon.Value) / dblLatFact);
  if (AGFlblS.Caption = 'S') then
    PSelectedUnit^.sLat := PSelectedGroup^.sLat + uHCDefines.NM1 * (AGFspnbxLat.Value)
  else
    PSelectedUnit^.sLat := PSelectedGroup^.sLat - uHCDefines.NM1 * (AGFspnbxLat.Value);
end;

procedure TfrmMain.AGFspnbxUnitAltEditingDone(Sender: TObject);
begin
  PSelectedUnit^.AltBand := AGFspnbxUnitAlt.Value;
  AGFUnitUpdate();
end;

procedure TfrmMain.DelPath(SGroup: PHCGroup);
var
  PPathLeg, PPathLegPrev: PHCPathRec;
begin
  // ALE 20130411 Delete the existing path leg by leg.
  PPathLeg := SGroup^.track;
  if ((PPathLeg <> nil) and (PPathLeg^.Next <> nil)) then
  begin
    PPathLegPrev := nil;
    while (PPathLeg^.Next <> nil) do
    begin
      PPathLegPrev := PPathLeg;
      PPathLeg := PPathLeg^.Next;
    end;
  end;
  // ALE 20130411 PPathLeg should now point to the last
  //  leg of the group's path, work backwards freeing them.
  if (PPathLeg <> nil) then
  begin
    while (PPathLeg <> SGroup^.track) do
    begin
      PPathLegPrev := PPathLeg^.prev;
      //Freemem(PPathLeg);
      del_HarpoonEvent_list(PPathLeg^.pathEvents);
      DisposPtr(PPathLeg);
      PPathLeg := PPathLegPrev;
    end;
    PPathleg^.Next := nil;
  end;
end;

procedure TfrmMain.SqPatrl(SGroup: PHCGroup);
var
  PPathLeg, PPathLegNext: PHCPathRec;
  fctr, intLegCnt: integer;
  cLat, cLon: longint;
  LatFactor: double;
begin
  if (SGroup^.kts = 0) then
    ShowMessage('group stationary');
  if (SGroup^.Units^.Air <> nil) then
    if (SGroup^.Units^.Air^.Throttle = 0) then
      ShowMessage('AirGroup loitering');
  DelPath(SGroup);              //strips pointers out of any multi point path
  fctr := AGFspnbxPatrl.Value;      //approx NM scaling of box size
  LatFactor := LatFctGrp(SGroup);   // corrects sLon for 'high' latitudes
  PPathLeg := NewPtr(SizeOf(THCPathRec));
  if (SGroup^.track <> nil) then
  begin
    cLat := SGroup^.track^.DestLat;
    cLon := SGroup^.track^.DestLon;
    //add patrol legs around  end of current path leg
  end
  else
  begin
    cLat := SGroup^.sLat;
    cLon := SGroup^.sLon;
  end;
  // now setup first patrol leg
  PPathLeg^.DestLat := cLat - (NM1 * fctr);
  PPathLeg^.DestLon := cLon;
  PPathLeg^.pathEvents := nil;
  SGroup^.track := PPathLeg;
  //now do remaining patrol legs
  for intLegCnt := 1 to 14 do
    // add to pattern by increasing intLegCnt and adding
  begin
    PPathLegNext := NewPtr(SizeOf(THCPathRec));          // appropriate 'case' items
    PPathLeg^.Next := PPathLegNext;
    PPathLegNext^.prev := PPathLeg;
    PPathLeg := PPathLegNext;

    case intLegCnt of
      1, 12:
      begin
        PPathLeg^.DestLat := cLat;
        PPathLeg^.DestLon := cLon - Round(NM1 * fctr / LatFactor);
      end;
      7, 14:
      begin
        PPathLeg^.DestLat := cLat - Round(NM1 * fctr * 0.8);
        PPathLeg^.DestLon := cLon + Round(NM1 * fctr * 0.8 / LatFactor);
      end;
      2, 5:
      begin
        PPathLeg^.DestLat := cLat + (NM1 * fctr);
        PPathLeg^.DestLon := cLon;
      end;
      6, 10:
      begin
        PPathLeg^.DestLat := cLat - Round(NM1 * fctr * 0.8);
        PPathLeg^.DestLon := cLon - Round(NM1 * fctr * 0.8 / LatFactor);
      end;
      3, 11:
      begin
        PPathLeg^.DestLat := cLat;
        PPathLeg^.DestLon := cLon + Round(NM1 * fctr / LatFactor);
      end;
      4:
      begin
        PPathLeg^.DestLat := cLat - (NM1 * fctr);
        PPathLeg^.DestLon := cLon;
      end;
      8:
      begin
        PPathLeg^.DestLat := cLat + Round(NM1 * fctr * 0.8);
        PPathLeg^.DestLon := cLon - Round(NM1 * fctr * 0.8 / LatFactor);
      end;
      9, 13:
      begin
        PPathLeg^.DestLat := cLat + Round(NM1 * fctr * 0.8);
        PPathLeg^.DestLon := cLon + Round(NM1 * fctr * 0.8 / LatFactor);
      end;
    end;
    PPathLeg^.pathEvents := nil;
  end;
  PPathLeg^.Next := nil;
end;


function TfrmMain.CountUnits(PGroup: PHCGroup): NativeUInt;
var
  intUnits: NativeUInt;
  PUnit: PHCUnit;
begin
  intUnits := 0;
  if (PGroup <> nil) then
  begin
    PUnit := PGroup^.Units;
    while (PUnit <> nil) do
    begin
      Inc(intUnits);
      PUnit := PUnit^.Next;
    end;
  end;
  Result := intUnits;
end;

end.
