unit mainform;

{$mode objfpc}{$H+}

interface

uses
  Classes, SysUtils, FileUtil, Forms, Controls, Graphics, RTTICtrls, Dialogs,
  StdCtrls, ComCtrls, Spin, ExtCtrls, uHCTypeDefs, uHCDefines, uHCEventHelpers,
  uHCAnnexDefs, uHCAnnexHelpers
  ;

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;
    AGFbtnASWPat: TButton;
    AGFbtnStrip: TButton;
    AGFbtnAEWPat: TButton;
    AtkBtnSrch: TButton;
    AtkLbxFGroup: TListBox;
    AtkLbxEGroup: TListBox;
    AtkLbxEUnit: TListBox;
    AtkBtnDefet: TButton;
    AtkBtnCapt: TButton;
    AGFBtnSplit: TButton;
    AGFBtnPause: TButton;
    AtkBtnCover: TButton;
    AtkBtnTarg: TButton;
    AtkBtnPause: TButton;
    AGFBtnRepr: TButton;
    AtkLbxFUnit: TListBox;
    AtkLblBeta: TLabel;
    RflLblRecipT: TLabel;
    RflLblSupT: TLabel;
    SVLabelDetail: TLabel;
    SVListBoxUnitDetail: TListBox;
    SVLabelUnits: TLabel;
    SVLabelGroup: TLabel;
    SVListBoxGroups: TListBox;
    SVListBoxUnits: TListBox;
    RflBtnQueue: TButton;
    RflChkBTnkGrpSrch: TCheckBox;
    RflBtnTrans: TButton;
    RflLblGameTime: TLabel;
    RflChkBFind: TCheckBox;
    RflChkBResume: TCheckBox;
    RflChkBPause: TCheckBox;
    RflLbxUnits: TListBox;
    RflLbxGroups: TListBox;
    RflLbxDetail: TListBox;
    SETmemHelp: TMemo;
    Attack: TTabSheet;
    AGFSpnBxSplt: TSpinEdit;
    RefuelSheet: TTabSheet;
    RflSpinEditTank: TSpinEdit;
    RflSpinEditQuant: TSpinEdit;
    RflSpinEditRecip: TSpinEdit;
    SeaVessel: TTabSheet;
    TedbxPatrol: TEdit;
    SETlblMLcount: TLabel;
    MLtmemLog: TMemo;
    PageControl1: TPageControl;
    MessgLog: TTabSheet;
    Settings: TTabSheet;
    SETspnMLC: TSpinEdit;
    AGFspnbxPatrl: TSpinEdit;
    procedure AGFbtnAEWPatEnter(Sender: TObject);
    procedure AGFbtnASWPatEnter(Sender: TObject);
    procedure AGFbtnGrp1Enter(Sender: TObject);
    procedure AGFbtnGrp2Enter(Sender: TObject);
    procedure AGFbtnMeetEnter(Sender: TObject);
    procedure AGFBtnPauseClick(Sender: TObject);
    procedure AGFBtnReprClick(Sender: TObject);
    procedure AGFBtnSplitClick(Sender: TObject);
    procedure AGFbtnSrchEnter(Sender: TObject);
    procedure AGFbtnStripClick(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 AtkBtnCaptClick(Sender: TObject);
    procedure AtkBtnDefetClick(Sender: TObject);
    procedure AtkBtnCoverClick(Sender: TObject);
    procedure AtkBtnSrchEnter(Sender: TObject);
    procedure AtkBtnTargClick(Sender: TObject);
    procedure AtkGrpSrch();
    procedure SeaVesselShow(Sender: TObject);
    procedure SVListBoxGroupsSelectionChange(Sender: TObject; User: boolean);
    procedure SVListBoxUnitsSelectionChange(Sender: TObject; User: boolean);
    procedure SVSearchGroup();
    procedure AtkLbxEGroupSelectionChange(Sender: TObject; User: boolean);
    procedure AtkLbxEUnitSelectionChange(Sender: TObject; User: boolean);
    procedure AtkLbxFGroupSelectionChange(Sender: TObject; User: boolean);
    procedure AtkLbxFUnitSelectionChange(Sender: TObject; User: boolean);
    procedure DelPath(SGroup:PHCGroup);
    procedure RflBtnQueueClick(Sender: TObject);
    procedure RflBtnTnkrGrpSrchClick(Sender: TObject);
    procedure RflBtnTransClick(Sender: TObject);
    procedure RflChkBFindChange(Sender: TObject);
    procedure RflChkBResumeChange(Sender: TObject);
    procedure RflLbxGroupsSelectionChange(Sender: TObject; User: boolean);
    procedure SqPatrl(SGroup:PHCGroup; PatType:Integer);
    procedure RflLbxUnitFill(PRefuelGroup: PHCGroup);
    procedure RflLbxDetailFill(PRefuelEvent: PHCHarpoonEvent);
    function LatFctGrp(SGroup:PHCGroup):double;
    function isTanker(PTankAirUnit: PHCUnit): Boolean;
    procedure SVLabelGroupClick(Sender: TObject);
  private
    { private declarations }
  public
    { public declarations }
    PStartGroup, PSelectedGroup, PselectedEGroup, SGroup1, SGroup2: PHCGroup;
    PSelectedUnit, PSelectedEUnit: PHCUnit;
    intGameTime, intGameTimeLeft: long;
    strMySide:string;
    MySide, EnmySide: uchar;
    dblLatFact: Double;
    intPageActive, PatType: Integer;
    HCQueueHarpoonEvent: THCQueueHarpoonEvent;
    NewPtr: THCNewPtr;
    DisposPtr: THCDisposPtr;
    HCdllCheckLatLonDepth: THCdllCheckLatLonDepth;
    del_HarpoonEvent_list: THCdel_HarpoonEvent_list;
    PHCEventHead, PCurrentEvent: PHCHarpoonEvent;
    PAnnexData: PHCAnnexes_disk;
    HCannex_id_to_disk_id: THCannex_id_to_disk_id;
    HCdisk_id_to_annex_id: THCdisk_id_to_annex_id;
    dt_scen_start: PHCDateTimeRec;
    PThisPlaneDisk: PHCAnnexB1_disk;
    function CountUnits(PGroup: PHCGroup): NativeUInt;

  end;

var
  frmMain: TfrmMain;
  intMLC: integer;

implementation

uses uLazGUIGlobals;

{$R *.lfm}

{ TfrmMain }

function TfrmMain.LatFctGrp(SGroup:PHCGroup):double;
begin
    result:= Cos((90-(SGroup^.sLat/sliver1))/57.2957);
end;

procedure TfrmMain.SVLabelGroupClick(Sender: TObject);
begin
  SVSearchGroup();
end;

procedure TfrmMain.SVSearchGroup();
var
  PHCGThis: PHCGroup;
begin
  PSelectedUnit:= nil;
  SVListBoxUnits.Items.Clear;
  SVListBoxGroups.Items.Clear;
  SVListBoxGroups.Sorted:=true;
  PHCGThis := PStartGroup;

  while (PHCGThis <> nil) do
    begin
      If (format('%d',[PHCGThis^.side]) = strMySide)
        then
        if  (PHCGThis^.gtype <> 'A')                       // (PHCGThis^.gtype = 'S') or (PHCGThis^.gtype = 'U')
          then
          SVListBoxGroups.Items.Add(PHCGThis^.id + format('-%d kts, %3.3d deg',[PHCGThis^.kts, PHCGThis^.course]));
      PHCGThis := PHCGThis^.toSouth;
    end;
end;

procedure TfrmMain.AGFgrpSrch();
var
  PHCGThis: PHCGroup;
begin
  PSelectedUnit:= nil;
  AGFspnbxUnitAlt.Clear;
  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.AGFbtnASWPatEnter(Sender: TObject);
begin
  If (PSelectedGroup <> nil) then
     begin
      PatType:= 0 ;
      SqPatrl(PSelectedGroup, PatType);
     end
     else showmessage('no Group selected');
end;


procedure TfrmMain.AGFbtnAEWPatEnter(Sender: TObject);
begin
  If (PSelectedGroup <> nil) then
       begin
        PatType:= 1 ;
        SqPatrl(PSelectedGroup, PatType);
       end
       else showmessage('no Group selected');
end;

procedure TfrmMain.AGFbtnGrp2Enter(Sender: TObject);
begin
  AGFbtnGrp2.Caption:=PSelectedGroup^.id;
  SGroup2:=PSelectedGroup
end;

procedure TfrmMain.AGFbtnMeetEnter(Sender: TObject);
// to join group2 to group1 - group1 remaining
var
   PPathEvent, PNextPathEvent: PHCHarpoonEvent;
   PMessageEvent: PHCMessageEvent;
   PJoinGroupEvent: PHCJoinGroupEvent;
   PFinalPathLeg: PHCPathRec;
   num_joins, joins_done: short;
   PUnit: PHCUnit;
   MessageText: String;

begin      //should test that groups are selected and air type
  if (SGroup1 <> nil) and (SGroup2 <> nil) then
begin
  if (SGroup1^.Units^.Air^.Throttle=0)
     then showmessage('group1 mustn''t loiter')
     else
  begin

  if SGroup1^.track <> nil then
   begin
    DelPath(SGroup1);
    PFinalPathLeg:= NewPtr(SizeOf(THCPathRec));
    PFinalPathLeg^:= THCPathRec(SGroup1^.track^);
    PFinalPathLeg^.pathEvents:= nil;   // applies desired destination to combined group
   end
  else
  begin
  SGroup1^.track:= NewPtr(SizeOf(THCPathRec));
  SGroup1^.track^.prev:= nil;
  SGroup1^.track^.next:= nil;
  end;
// group1 track preped and requires destination and events
  MessageText:= 'Groups Joined and Require Orders';

if SGroup2^.Units^.Air^.Throttle <> 0 then //if at loiter leave it alone;
//prep group2 path
begin
      if SGroup2^.track <> nil then
       DelPath(SGroup2)        //prunes path to single leg
    else
      begin
       SGroup2^.track:= NewPtr(SizeOf(THCPathRec));
       SGroup2^.track^.prev:= nil;
       SGroup2^.track^.next:= nil;
      end;
  SGroup2^.track^.pathEvents:= nil;
// group2 path preped now add destination to both groups
  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);
  SGroup2^.track^.DestLon:=  SGroup1^.track^.DestLon;
end
else         //group2 is loitering  = group1 proceeds to group2 current position
begin
  SGroup1^.track^.DestLat:=  SGroup2^.sLat;
  SGroup1^.track^.DestLon:=  SGroup2^.sLon;
end;
//tst
if PFinalPathLeg <> nil then
   if SGroup2^.track <> nil then
   SGroup2^.track^.next:= PFinalPathLeg
   else SGroup2^.track:= PFinalPathLeg;
//end tst
//now set up path events for group1
  PPathEvent := NewPtr(SizeOf(THCHarpoonEvent));
  PPathEvent^.Time:= 0;
  PPathEvent^.in_scroll_flag:= 001;
  PPathEvent^.ourGroup:= SGroup1;
  PPathEvent^.UnitID:= SGroup1^.Units^.id;
  // all group1 units will migrate to group2
  num_joins := CountUnits(SGroup1);
  PPathEvent^.flag := JOIN_EVENT or TimeDelta;
  PPathEvent^.DataPtr := NewPtr(SizeOf(short) + num_joins * SizeOf(THCJoinGroupEvent));

  short(PPathEvent^.DataPtr^) := num_joins;
  PJoinGroupEvent := PHCJoinGroupEvent(NativeUInt(PPathEvent^.DataPtr) + SizeOf(num_joins));
  PUnit := SGroup1^.Units;
  for joins_done := 0 to num_joins - 1 do
    begin
 //     PJoinGroupEvent^.JoinGroup := SGroup2^.id;    / bandaid fix awaiting GE mods
 //     PJoinGroupEvent^.SplitGroup := SGroup1^.id;
        PJoinGroupEvent^.JoinGroup[0] := SGroup2^.id[0];
        PJoinGroupEvent^.JoinGroup[1] := SGroup2^.id[1];
        PJoinGroupEvent^.JoinGroup[2] := SGroup2^.id[2];
        PJoinGroupEvent^.JoinGroup[3] := SGroup2^.id[3];
        PJoinGroupEvent^.SplitGroup[0] := SGroup1^.id[0];
        PJoinGroupEvent^.SplitGroup[1] := SGroup1^.id[1];
        PJoinGroupEvent^.SplitGroup[2] := SGroup1^.id[2];
        PJoinGroupEvent^.SplitGroup[3] := SGroup1^.id[3];
 //

      PJoinGroupEvent^.UnitID := PUnit^.id;
      PJoinGroupEvent^.Number := PUnit^.Air^.PlatNow;
      PUnit := PUnit^.next;
      PJoinGroupEvent := PHCJoinGroupEvent(NativeUInt(PJoinGroupEvent) + SizeOf(THCJoinGroupEvent));
    end;
  //join event completed  now message event
  PMessageEvent:= NewPtr(SizeOf(THCMessageEvent));
  PMessageEvent^.Message := PAnsiChar(MessageText);

  PNextPathEvent:= NewPtr(SizeOf(THCHarpoonEvent));
  PNextPathEvent^.DataPtr:= PMessageEvent;
  PNextPathEvent^.flag:= MESSAGE_EVENT or TimeDelta;
  PNextPathEvent^.Time:= 5;
  PNextPathEvent^.ourGroup:= SGroup2;              //messsage must be applied to Group2 (surviving group) but queued in group1 path
  PNextPathEvent^.UnitID:= SGroup2^.Units^.id;
  PNextPathEvent^.in_scroll_flag:= 001;
  PNextPathEvent^.next:= nil;
  PPathEvent^.next:= PNextPathEvent;
  SGroup1^.track^.pathEvents:= PPathEvent;
  end;
end;

end;

procedure TfrmMain.AGFBtnPauseClick(Sender: TObject);
begin
  If AGFBtnPause.Caption = 'Pause' then
  begin
  Byte(PDLLBusy^) := CTRUE;
  AGFBtnPause.Caption:= 'Resume';
  AtkBtnPause.Caption:= 'Resume';
  AGFgrpSrch();
  end
  else
  begin
    Byte(PDLLBusy^) := CFALSE;
    AGFBtnPause.Caption:= 'Pause';
    AtkBtnPause.Caption:= 'Pause';
  end;
end;

procedure TfrmMain.AGFBtnReprClick(Sender: TObject);
var
  SGroup1DP: short;
  SG1Unit: PHCUnit;
begin                       // need some proximity test
  SG1Unit:= SGroup1^.Units;
  SGroup1DP:= SG1Unit^.OrigDp;
  while SG1Unit^.next <> nil do
  begin
       SG1Unit:= SG1Unit^.next;
       SGroup1DP:= SGroup1DP + SG1Unit^.OrigDp;
  end;
  if SGroup1^.Units^.Air <>nil then
  SGroup1DP := SGroup1DP * 100;   //air groups treated separate
  SGroup2^.Units^.CurrDp:= (SGroup2^.Units^.CurrDp) - (SGroup1DP div 2);
  if SGroup2^.Units^.CurrDp < 1 then
  begin
       SGroup2^.Units^.CurrDp:= 0;
       ShowMessage('Main Unit Repaired');
  end;
 // this has reparied only unit 00
end;

procedure TfrmMain.AGFBtnSplitClick(Sender: TObject);
var
  PSplitUnitEvent: PHCSplitGroupEvent;
  PThisHCEvent: PHCHarpoonEvent;

begin
  If AGFlbxUnit.ItemIndex <> -1 then
      begin

      PThisHCEvent:= NewPtr(SizeOf(THCHarpoonEvent));

      PThisHCEvent^:= THCHarpoonEvent(PHCEventHead^);  //PHCThisHCEvent content 'old' event-head content
      PHCEventHead^.next:= PThisHCEvent;               //now put new event data into event-head content
      PHCEventHead^.DataPtr:= NewPtr(SizeOf(THCSplitGroupEvent) + SizeOf(uchar));
      uchar(PHCEventHead^.DataPtr^):= 1;

       PSplitUnitEvent:= PHCEventHead^.DataPtr + SizeOf(uchar);
       PSplitUnitEvent^.UnitID[0][0]:= PSelectedUnit^.id;
       PSplitUnitEvent^.UnitID[0][1]:= AGFSpnBxSplt.Value;

      PHCEventHead^.flag:= SPLIT_EVENT or TimeAbs;
      PHCEventHead^.ourGroup:= PSelectedGroup;
      PHCEventHead^.UnitID:= PSelectedUnit^.id;
      PHCEventHead^.in_scroll_flag:= 000;
      PHCEventHead^.Time:= intGameTime;

       end
  else ShowMessage('No Unit Selected');
end;


procedure TfrmMain.AGFbtnSrchEnter(Sender: TObject);
begin
  AGFgrpSrch();
end;

procedure TfrmMain.AGFbtnStripClick(Sender: TObject);
begin
  if PSelectedGroup <> nil then
  begin
  DelPath(PSelectedGroup);
  end
  else
  ShowMessage('First Select a Group');
end;

procedure TfrmMain.AGFlbxGroupSelectionChange(Sender: TObject; User: boolean);
var
  strSel: String;
  PHCGThis: PHCGroup;
  PHCUThis: PHCUnit;
begin
  PSelectedUnit:=nil;
  AGFspnbxUnitAlt.Clear;
  AGFspnbxLon.Clear;
  AGFspnbxLat.Clear;
  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;
  PHCUThis := PHCGThis^.Units;
  while (PHCUThis <> nil) do
  begin
    if (PHCUThis^.Air<>nil) then
    begin
     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]));
     if (PHCUThis^.air^.FuelToOffload > 0) then
     AGFlbxUnit.Items.Add(Format('%0.3d unit Tanker(s) with %d refuel available',[PHCUThis^.id, PHCUThis^.air^.FuelToOffload]));
    end;
    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.Value:=11;
  AGFspnbxUnitAlt.Value:=PSelectedUnit^.AltBand;
  dblLatFact:= Cos((90-(PSelectedUnit^.sLat/sliver1))/57.2957);

  if (PselectedUnit^.sLon > PSelectedGroup^.sLon) then
  begin
  AGFspnbxLon.Value:=(PselectedUnit^.sLon - PSelectedGroup^.sLon)/ uHCDefines.NM1*dblLatFact;
  AGFlblE.Caption:= 'E';
  end
  else
  begin
  AGFspnbxLon.Value:=(PSelectedGroup^.sLon - PselectedUnit^.sLon)/ uHCDefines.NM1*dblLatFact;
  AGFlblE.Caption:= 'W';
  end;

  if (PselectedUnit^.sLat > PSelectedGroup^.sLat) then
  begin
   AGFspnbxLat.Value:=(PselectedUnit^.sLat - PSelectedGroup^.sLat)/ uHCDefines.NM1;
   AGFlblS.Caption:= 'S';
  end
  else
  begin
  AGFspnbxLat.Value:=(PSelectedGroup^.sLat - PselectedUnit^.sLat)/ uHCDefines.NM1;
  AGFlblS.Caption:= 'N';
  end;

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.AtkBtnCaptClick(Sender: TObject);
var
valDef, valAtk: real;
intC: integer;
PThisEUnit, PThisFUnit: PHCUnit;
PMnt: PHCMntRec;
PMissDisk: PHCAnnexD1_disk;
PAToGDisk: PHCAnnexG1_disk;
PGunDisk:  PHCAnnexC1_disk;
PMultiMntDisk: PHCAnnexZ2_disk;
AtckGType: AnsiChar;
begin
  //going to calculate on basis of damage points until we have access to database
  If (PSelectedEGroup = nil) or (PSelectedGroup = Nil) then
  ShowMessage('Friendly and Enemy groups must be selected')
  else
    begin
     valDef:= 0;
     PThisFUnit:= PSelectedGroup^.Units;
     PThisEUnit:= PSelectedEGroup^.Units;
     AtckGType:= PSelectedGroup^.gtype;

while (PThisEUnit <> nil) do
     begin
     valDef:= valDef + PThisEUnit^.OrigDp - PThisEUnit^.CurrDp;
//     if AtckGType = 'A' then       //defend against air attack
//     begin
       if PThisEUnit^.Mnts <> nil then
       begin
          PMnt:= PThisEUnit^.Mnts;
          while PMnt <> nil do
               begin
                  case HCAnnexHelper.HCAnnexShift(PMnt^.WepId) of
                  MISSILE_FLAG:
                      begin
                      PMissDisk:= HCAnnexHelper.HCGetAnnexD1(PMnt^.WepId);
                      if AtckGType = 'A' then
                        valDef := valDef + (PMissDisk^.AirPh * PMissDisk^.Dp * PMnt^.Tubes * PMnt^.n_Mounts)
                        else valDef := valDef + (PMissDisk^.SurfPh * PMissDisk^.Dp * PMnt^.Tubes * PMnt^.n_Mounts);
                      end;
                  GUN_FLAG:
                      begin
                        PGunDisk:= HCAnnexHelper.HCGetAnnexC1(PMnt^.WepId);
                        if AtckGType = 'A' then
                          valDef:= valDef + (PGunDisk^.Dp * PGunDisk^.AirPh * PMnt^.n_Mounts * PMnt^.Tubes)
                          else valDef:= valDef + (PGunDisk^.Dp * PGunDisk^.SurfFarPh * PMnt^.n_Mounts * PMnt^.Tubes);
                      end;
                  end;
                  PMnt:= PMnt^.next;
               end;
       end; // finished look at mounts this unit
ShowMessage(FloatToStr(valDef));
//     end;
     PThisEUnit:= PThisEUnit^.next;
     end;   // looked at all defending units
     valAtk:= 0;
      if PSelectedGroup^.gtype = 'A' then          //calc valatk for air groups
      begin
      while (PThisFUnit <> nil) do
      begin
//     valAtk:= valAtk + ((PThisFUnit^.Air^.Endurance div 1000) * PThisFUnit^.Air^.PlatNow);
      if PThisFUnit^.Mnts <> nil then
      begin
        PMnt:= PThisFUnit^.Mnts;
        while PMnt <> nil do
        begin
          case HCAnnexHelper.HCAnnexShift(PMnt^.WepId) of
          MISSILE_FLAG:
              begin
              PMissDisk:= HCAnnexHelper.HCGetAnnexD1(PMnt^.WepId);
              valAtk:= valAtk + (PMissDisk^.Dp * PMissDisk^.SurfPh * PMnt^.Ammo_c[0]);
              end;
          ATOG_FLAG:
              begin
              PAToGDisk:= HCAnnexHelper.HCGetAnnexG1(PMnt^.WepId);
              valAtk:= valAtk + (PAToGDisk^.Dp * PAToGDisk^.Ph * PMnt^.Ammo_c[0]);
              end;
          end;
ShowMessage('Mnt: ' + FloatToStr(valAtk));
          PMnt:= PMnt^.next;
        end;
      end;     //looked at all mounts this air unit
ShowMessage('Unit: ' + FloatToStr(valAtk));
      PThisFUnit:= PThisFUnit^.next;
      end;      // looked at all units this air group
      valAtk:= valAtk / (1 + (sqr(( abs(PSelectedGroup^.sLat - PSelectedEGroup^.sLat) +
               abs(PSelectedGroup^.sLon - PSelectedEGroup^.sLon)) / uHCDefines.NM1)));
      end     // end if air group

//      if PSelectedGroup^.gtype = 'S' then
      else       // not an air group attack = surface or mobile
      begin
      while (PThisFUnit <> nil) do
      begin
//            valAtk:= valAtk + PThisFUnit^.OrigDp - PThisFUnit^.CurrDp;
      if PThisFUnit^.Mnts <> nil then
      begin
        PMnt:= PThisFUnit^.Mnts;
        while PMnt <> nil do
        begin
          case HCAnnexHelper.HCAnnexShift(PMnt^.WepId) of
          MISSILE_FLAG:
              begin
              PMissDisk:= HCAnnexHelper.HCGetAnnexD1(PMnt^.WepId);
              valAtk:= valAtk + (PMissDisk^.Dp * PMissDisk^.SurfPh * PMnt^.Ammo_c[0]);
              end;
          ATOG_FLAG:          // prob never
              begin
              PAToGDisk:= HCAnnexHelper.HCGetAnnexG1(PMnt^.WepId);
              valAtk:= valAtk + (PAToGDisk^.Dp * PAToGDisk^.Ph * PMnt^.Ammo_c[0]);
              end;
          GUN_FLAG:
              begin
              PGunDisk:= HCAnnexHelper.HCGetAnnexC1(PMnt^.WepId);
              valAtk:= valAtk + (PGunDisk^.SurfFarPh * PGunDisk^.Dp * PMnt^.Ammo_c[0]);
              end;
          MULTI_FLAG:
              begin
                PMultiMntDisk:= HCAnnexHelper.HCGetAnnexZ2(PMnt^.WepId);
                intC:= 0;
                while (PMultiMntDisk^.WepQty[intC] > 0) do
                begin
                  case (HCAnnexHelper.HCAnnexShift(PMultiMntDisk^.WepId[intC])) of
                  GUN_FLAG:
                    begin
                    PGunDisk:= HCAnnexHelper.HCGetAnnexC1(PMultiMntDisk^.WepId[intC]);
                    valAtk:= valAtk + (PGunDisk^.SurfFarPh * PGunDisk^.Dp * PMultiMntDisk^.WepQty[intC]);
                    end;
                  MISSILE_FLAG:
                    begin
                    PMissDisk:= HCAnnexHelper.HCGetAnnexD1(PMultiMntDisk^.WepId[intC]);
                    valAtk:= valAtk + (PMissDisk^.SurfPh * PMissDisk^.Dp * PMultiMntDisk^.WepQty[intC]);
                    end;
                  end; // end case this weapon
ShowMessage('Mlt-MntWep: ' + FloatToStr(valAtk));
                intC:= intC + 1;
                end; //end multi mount
              end;// end case = multi-mount
          end;
ShowMessage('Mnt: ' + FloatToStr(valAtk));
          PMnt:= PMnt^.next;
        end;
      end;     //looked at all mounts this unit
            PThisFUnit := PThisFUnit^.next;
      end;  // looked at all units
      valAtk := valAtk / (1 + (( abs(PSelectedGroup^.sLat - PSelectedEGroup^.sLat) +
             abs(PSelectedGroup^.sLon - PSelectedEGroup^.sLon)) / uHCDefines.NM10));
      end;  // end if gtype S
          //will need code for mobile units and cross check legit targets all atk group types
    if valAtk < 0 then
     valAtk := 0;
    If random < valAtk / (valAtk + valDef) then
     begin
      PSelectedEGroup^.side:= PSelectedGroup^.side;
      ShowMessage('Attack succeeded');
     end
    else
      begin
      PSelectedEGroup^.side:= EnmySide;
      If random < valDef / (valAtk + valDef) then
        begin
         PSelectedGroup^.side:= PSelectedEGroup^.side;
//       stop platform and strip track -join enemy group
         ShowMessage('Enemy counter attack succeeded');
        end
      else ShowMessage('You''ve created a new enemy');
      end
    end;
end;

procedure TfrmMain.AtkBtnDefetClick(Sender: TObject);
begin
  if PSelectedEGroup <> Nil then
  case PSelectedEGroup^.side of
  0,1: PSelectedEGroup^.side:= uchar(2);
  2: PSelectedEGroup^.side:= EnmySide;
  end
  else ShowMessage('Select Enemy Group');
end;

procedure TfrmMain.AtkBtnCoverClick(Sender: TObject);
var
PNewPathLeg: PHCPathRec;

begin
  if (PSelectedGroup <> nil) and (PSelectedEUnit <> nil)  then
  begin
  showmessage('Beta feature: send attacker to EUnit');
  if (PSelectedEUnit^.showing[0] <> 0) then
  begin
    if PSelectedEUnit^.UncertaintyRegion[MySide] = nil then
    begin
     if PSelectedgroup^.track = nil then
     begin
      PNewPathLeg := NewPtr(SizeOf(THCPathRec));
      PSelectedGroup^.track:= PNewPathLeg;
      PSelectedGroup^.track^.next:= nil;
      PSelectedGroup^.track^.prev:= nil;
      showmessage('new path');
     end
     else DelPath(PSelectedGroup);
      PSelectedGroup^.track^.DestLat:= PSelectedEUnit^.sLat;
      PSelectedGroup^.track^.DestLon:= PSelectedEUnit^.sLon;
      PSelectedGroup^.track^.pathEvents:= nil;
      if PSelectedGroup^.Units^.Air <> nil then     //if air group check for loiter/hover
        begin
          if PSelectedGroup^.Units^.Air^.Throttle = 0 then
          PSelectedGroup^.Units^.Air^.Throttle:= 1;
          if PSelectedGroup^.kts = 0 then
          PSelectedGroup^.kts:= 50;
        end
    end
    else ShowMessage('exact position of enemy unit unknown');
  end
  else ShowMessage('lost contact');
  end
  else ShowMessage('Friendly Group and Enemy Unit must be selected');
end;

procedure TfrmMain.AtkBtnSrchEnter(Sender: TObject);
begin
  AtkGrpSrch();
end;

procedure TfrmMain.AtkBtnTargClick(Sender: TObject);

begin
  if (PSelectedUnit <>nil) and (PSelectedEUnit <> nil) then
  if (PSelectedUnit^.Missile <> nil) then
  begin
   PSelectedUnit^.Missile^.targetUnit:= PSelectedEUnit;
   PSelectedUnit^.Missile^.tLat:=PSelectedEUnit^.sLat;
   PSelectedUnit^.Missile^.tLon:=PSelectedEUnit^.sLon;
   AtkBtnTarg.Caption:= format('%s - %0.3d', [PHCGroup(PHCUnit(PSelectedUnit^.Missile^.targetUnit)^.Group)^.id,
    PHCUnit(PSelectedUnit^.Missile^.targetUnit)^.id]);
  end;
end;

procedure TfrmMain.AtkGrpSrch();
var
  PHCGThis: PHCGroup;
begin
  PSelectedUnit:= nil;
  SGroup1:= nil;
  SGroup2:= nil;
  AtkLbxEUnit.Items.Clear;
  AtkLbxEGroup.Items.Clear;
  AtkLbxFGroup.Items.Clear;
  AtkLbxFGroup.Sorted:=true;
  AtkLbxEGroup.Sorted:=true;
  PHCGThis := PStartGroup;

  while (PHCGThis <> nil) do
    begin
      If (format('%d',[PHCGThis^.side]) = strMySide)  then
         AtkLbxFGroup.Items.Add(PHCGThis^.id + format('-%d kts',[PHCGThis^.kts]));
      PHCGThis := PHCGThis^.toSouth;
    end;
  PHCGThis := PStartGroup;
while (PHCGThis <> nil) do
    begin
      If (format('%d',[PHCGThis^.side]) <> strMySide) and (PHCGThis^.showing <> 0)
      then
         AtkLbxEGroup.Items.Add(PHCGThis^.id + format('-%d kts',[PHCGThis^.kts]));
      PHCGThis := PHCGThis^.toSouth;
    end;
end;

procedure TfrmMain.SeaVesselShow(Sender: TObject);
begin
  SVSearchGroup();
end;

procedure TfrmMain.SVListBoxGroupsSelectionChange(Sender: TObject; User: boolean);
var
  strSel: String;
  PHCGThis: PHCGroup;
  PHCUThis: PHCUnit;
begin
  PSelectedUnit:=nil;
  strSel := copy(SVListBoxGroups.Items[SVListBoxGroups.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
     SVSearchGroup();
     PHCGThis :=PStartGroup;
     end;                                //list reset
  PSelectedGroup:=PHCGThis;
  SVListBoxUnitDetail.Items.Clear;
  SVListBoxUnits.Items.Clear;
  PHCUThis := PHCGThis^.Units;
  while (PHCUThis <> nil) do
  begin
  SVListBoxUnits.Items.Add(Format('%0.3d - %s', [PHCUThis^.id, PHCUThis^.name]));
  PHCUThis := PHCUThis^.next;
  end;
end;

procedure TfrmMain.SVListBoxUnitsSelectionChange(Sender: TObject; User: boolean);
var
  strSel, strDepthAlt, strSubDepth, strWepName, strText: String;
  ThisWepID: ID;
  isSub, isBase: Boolean;
  PHCUThis: PHCUnit;
  RLat, RLon, WepRnge: Double;
  intDepthAlt, intSubDepth, MinsToGo, intC: integer;
  PmyPlanes: PHCPlaneRec;
  PThisMnt: PHCMntRec;

  PThisMount: PHCAnnexZ1_disk;
  PThisMulti: PHCAnnexZ2_disk;
  PThisLoadout: PHCAnnexY1_disk;

  PThisMissileDisk: PHCAnnexD1_disk;

  PHCAnnexA1: PHCAnnexA1_disk;
  PHCAnnexA2: PHCAnnexA2_disk;
  PHCAnnexB1: PHCAnnexB1_disk;
  PHCAnnexP1: PHCAnnexP1_disk;
  PHCAnnexC1: PHCAnnexC1_disk;
  PHCAnnexD1: PHCAnnexD1_disk;
  PHCAnnexE1: PHCAnnexE1_disk;
  PHCAnnexF1: PHCAnnexF1_disk;
  PHCAnnexG1: PHCAnnexG1_disk;
  PHCAnnexG2: PHCAnnexG2_disk;


begin
  strSel := SVListBoxUnits.Items[SVListBoxUnits.ItemIndex];
  PHCUThis := PSelectedGroup^.Units;
  while (PHCUThis <> nil) do
  begin
    if (Pos(Format('%0.3d', [PHCUThis^.id]), strSel) = 1) then        // need to get right group
    begin
      break;
    end;
    PHCUThis := PHCUThis^.next;
  end;
  PSelectedUnit := PHCUThis;

  if (PSelectedUnit <> nil) then     // ddt add unit information
  begin
  isSub:= False;      // False until
  isBase:= False;     // shown otherwise
    SVListBoxUnitDetail.Items.Clear;
    // first look up unit disk index for class name
    case (HCAnnexHelper.HCAnnexShift(PSelectedUnit^.Annex)) of
          SHIP_FLAG:
          begin
            PHCAnnexA1 := HCAnnexHelper.HCGetAnnexA1(PSelectedUnit^.Annex);
            strText := 'Ship Class Name: ' + PHCAnnexA1^.ClassName;
          end;
          SUB_FLAG:
          begin
            isSub:= True;
            PHCAnnexA2 := HCAnnexHelper.HCGetAnnexA2(PSelectedUnit^.Annex);
            strText := 'Submarine Class Name: ' + PHCAnnexA2^.ClassName;
          end;
          PLANE_FLAG:
          begin
            PHCAnnexB1 := HCAnnexHelper.HCGetAnnexB1(PSelectedUnit^.Annex);
            strText := 'Aircraft Class Name: ' + PHCAnnexB1^.ClassName;
          end;
          BASE_FLAG:
          begin
            PHCAnnexP1 := HCAnnexHelper.HCGetAnnexP1(PSelectedUnit^.Annex);
            isBase:= True;
            strText := 'Base Name: ' + PHCAnnexP1^.Name;
          end;
          else
            strText := '';
        end;
    //
    SVListBoxUnitDetail.Items.Add(strText);
    SVListBoxUnitDetail.Items.Add(Format('%2.2s%0.3d %s',[PHCGroup(PSelectedUnit^.Group)^.id, PSelectedUnit^.id, PSelectedUnit^.name]));
    SVListBoxUnitDetail.Items.Add(format('best weapon range: AAW- %d, ASUW- %d, ASW- %0.1n',[PSelectedUnit^.BestAAWRange div 10, PSelectedUnit^.BestASuWRange div 10, PSelectedUnit^.BestASWRange / 10]));
    // test for base => omit next line
    if not isBase then
    SVListBoxUnitDetail.Items.Add(format('course: %3.3d, kts: %d, MaxKts: %d',[PSelectedUnit^.course, PSelectedUnit^.kts, PSelectedUnit^.maxspeed]));
    SVListBoxUnitDetail.Items.Add(format('Damage Points Now/Orig: %d/ %d',[PSelectedUnit^.CurrDp, PSelectedUnit^.OrigDp]));
// needs to run thru mnts to show weapons
  if (PSelectedUnit^.Mnts <> nil) then
  begin
    SVListBoxUnitDetail.Items.Add(' ');
    SVListBoxUnitDetail.Items.Add('* Mounts *');
    PThisMnt:= PSelectedUnit^.Mnts;
    while (PThisMnt <> nil) do
      begin

    if (HCAnnexHelper.HCAnnexShift(PThisMnt^.MntId) = MOUNT_FLAG)  then
  begin
    PThisMount:= HCAnnexHelper.HCGetAnnexZ1(PThisMnt^.MntId);
    SVListBoxUnitDetail.Items.Add(format('%s: %d mounts; %d tubes, %d targets',[PThisMount^.ClassName, PThisMount^.NumMounts, PThisMount^.NumTubes, PThisMount^.NumTargets]));
    ThisWepID:= PThisMount^.WepId;
  end
    else // assume its a weapon
    ThisWepID:= PThisMnt^.WepId;


    case (HCAnnexHelper.HCAnnexShift(ThisWepId)) of
      MULTI_FLAG:
    begin
          PThisMulti:= HCAnnexHelper.HCGetAnnexZ2(ThisWepId);
          intC:= 0;
          while (PThisMulti^.WepQty[intC] > 0) do
          begin
           //
           WepRnge:= 0;
           case (HCAnnexHelper.HCAnnexShift(PThisMulti^.WepId[intC])) of
    GUN_FLAG:
      begin
      PHCAnnexC1:= HCAnnexHelper.HCGetAnnexC1(PThisMulti^.WepId[intC]);
      strWepName:= PHCAnnexC1^.ClassName;
      if  PHCAnnexC1^.AirRange  >  PHCAnnexC1^.SurfRange
        then   WepRnge:= PHCAnnexC1^.AirRange
        else WepRnge:= PHCAnnexC1^.SurfRange;
      end;
    MISSILE_FLAG:
      begin
      PHCAnnexD1:= HCAnnexHelper.HCGetAnnexD1(PThisMulti^.WepId[intC]);
      if PHCAnnexD1^.AirRange > PHCAnnexD1^.SurfRange
        then  WepRnge:= PHCAnnexD1^.AirRange
        else  WepRnge:= PHCAnnexD1^.SurfRange;
      strWepName:= PHCAnnexD1^.ClassName;
      end;
    ASW_FLAG:
    begin
    PHCAnnexE1:= HCAnnexHelper.HCGetAnnexE1(PThisMulti^.WepId[intC]);
    WepRnge:= PHCAnnexE1^.MaxRange;
    strWepName:= PHCAnnexE1^.ClassName;
    end;
    TORP_FLAG:
    begin
    PHCAnnexF1:= HCAnnexHelper.HCGetAnnexF1(PThisMulti^.WepId[intC]);
    if PHCAnnexF1^.MaxRange[0] > PHCAnnexF1^.MaxRange[1]
      then  WepRnge:= PHCAnnexF1^.MaxRange[0]
      else  WepRnge := PHCAnnexF1^.MaxRange[1];
    strWepName:= PHCAnnexF1^.ClassName;
    end;
    ATOG_FLAG:
    begin
    PHCAnnexG1:= HCAnnexHelper.HCGetAnnexG1(PThisMulti^.WepId[intC]);
    strWepName:= PHCAnnexG1^.ClassName;
    end;
    ECM_FLAG:
    begin
    PHCAnnexG2:= HCAnnexHelper.HCGetAnnexG2(PThisMulti^.WepId[intC]);
    strWepName:= PHCAnnexG2^.ClassName;
    end;
    else
      strWepName:= format('misc - WepID %d',[ThisWepId]);
    end;

            SVListBoxUnitDetail.Items.Add(format('   %d of %s: Range: %0.1n',[PThisMulti^.WepQty[intC], strWepName, WepRnge/10]));
            intC:= intC + 1;
          end;     //  end one weapon of multi mount
    end;      // end case of multi mount

    GUN_FLAG:
      begin
      PHCAnnexC1:= HCAnnexHelper.HCGetAnnexC1(ThisWepId);
      if  PHCAnnexC1^.AirRange  >  PHCAnnexC1^.SurfRange
        then   WepRnge:= PHCAnnexC1^.AirRange
        else WepRnge:= PHCAnnexC1^.SurfRange;
      SVListBoxUnitDetail.Items.Add('  Load: ' + PHCAnnexC1^.ClassName + '- Range: ' + FloatToStr(WepRnge/10));
      end;
    MISSILE_FLAG:
      begin
      PHCAnnexD1:= HCAnnexHelper.HCGetAnnexD1(ThisWepId);
      if PHCAnnexD1^.AirRange > PHCAnnexD1^.SurfRange
        then  WepRnge:= PHCAnnexD1^.AirRange
        else  WepRnge:= PHCAnnexD1^.SurfRange;
      SVListBoxUnitDetail.Items.Add('  Load: ' + PHCAnnexD1^.ClassName + '- Range: ' + FloatToStr(WepRnge/10));
      end;
    ASW_FLAG:
    begin
    PHCAnnexE1:= HCAnnexHelper.HCGetAnnexE1(ThisWepId);
    WepRnge:= PHCAnnexE1^.MaxRange;
    SVListBoxUnitDetail.Items.Add('  Load: ' + PHCAnnexE1^.ClassName + '- Range: ' + FloatToStr(WepRnge/10));
    end;
    TORP_FLAG:
    begin
    PHCAnnexF1:= HCAnnexHelper.HCGetAnnexF1(ThisWepId);
    if PHCAnnexF1^.MaxRange[0] > PHCAnnexF1^.MaxRange[1]
      then  WepRnge:= PHCAnnexF1^.MaxRange[0]
      else  WepRnge := PHCAnnexF1^.MaxRange[1];
    SVListBoxUnitDetail.Items.Add('  Load: ' + PHCAnnexF1^.ClassName + '- Range: ' + FloatToStr(WepRnge/10));
    end;
    ATOG_FLAG:
    begin
    PHCAnnexG1:= HCAnnexHelper.HCGetAnnexG1(ThisWepId);
    SVListBoxUnitDetail.Items.Add('  Load:' + PHCAnnexG1^.ClassName);
    end;
    ECM_FLAG:
    begin
    PHCAnnexG2:= HCAnnexHelper.HCGetAnnexG2(ThisWepId);
    SVListBoxUnitDetail.Items.Add('  load: ' + PHCAnnexG2^.ClassName);
    end;
    else
      SVListBoxUnitDetail.Items.Add(format('  misc - WepID %d',[ThisWepId]));
    end;
    // end case weapon type

    PThisMnt:= PThisMnt^.next;
    end;       // end  each mount

  end; //end if mnts
//  test for landed air units
  if (PSelectedUnit^.myPlanes <> nil) then
  begin
    PmyPlanes:= PSelectedUnit^.myPlanes;
    SVListBoxUnitDetail.Items.Add('aircraft landed');
    while (PmyPlanes <> nil) do
    begin
      if PmyPlanes^.Time = 0 then MinsToGo:= 0 else
      MinstoGo:= (PmyPlanes^.Time - intGameTime) div 60;
      SVListBoxUnitDetail.Items.Add(format('%d-%s; ready time: %d',[PmyPlanes^.PlatNow, PmyPlanes^.name, MinsToGo]));
      PmyPlanes:= PmyPlanes^.next;  // need to get loadouts
    end;
  end;
// end test for landed air units

  SVListBoxUnitDetail.Items.Add('general');
  RLat:= 90-(PSelectedUnit^.sLat/sliver1);       // have to convert game slat/slon to real lat/lon
  RLon:= PSelectedUnit^.sLon/sliver1;
  SVListBoxUnitDetail.Items.Add(format('lat: %3.2f; lon: %3.2f',[RLat, RLon]));
  intDepthAlt:= HCdllCheckLatLonDepth(RLat, RLon);     // water depth/land altitude at geographic point of unit
  case intDepthAlt of
  0: strDepthAlt := 'Water depth: Extreme';
  1: strDepthAlt := 'Water depth: Vdeep';
  2: strDepthAlt := 'Water depth: Deep';
  3: strDepthAlt := 'Water depth: Intermediate';
  4: strDepthAlt := 'Water depth: Shallow';
  5: strDepthAlt := 'Water depth: Snorkel';
  6: strDepthAlt := 'WARNING Water depth: SeaLevel';
  7: strDepthAlt := 'ScopeAlt';
  8: strDepthAlt := 'SmallShip';
  9: strDepthAlt := 'MedShip';
  10: strDepthAlt := 'LrgShip';
  11: strDepthAlt := 'Ground Level';             // normally vlow
  12: strDepthAlt := 'Low';
  13: strDepthAlt := 'Medium';
  14: strDepthAlt := 'High';
  15: strDepthAlt := 'Vhigh';
  16: strDepthAlt := 'SubOrbit';
  end;
  //
  if isSub then
  begin
  intSubDepth:= PSelectedUnit^.AltBand;
  case intSubDepth of
  0: strSubDepth := 'Too Deep';
  1: strSubDepth := 'Vdeep';
  2: strSubDepth := 'Deep';
  3: strSubDepth := 'Intermediate';
  4: strSubDepth := 'Shallow';
  5: strSubDepth := 'Snorkel';
  6: strSubDepth := 'Surfaced';
  end;
  SVListBoxUnitDetail.Items.Add('Submarine depth: ' + strSubDepth)
  end;
  SVListBoxUnitDetail.Items.Add(strDepthAlt);
  if PSelectedUnit^.AgroundFlag = 1 then
  SVListBoxUnitDetail.Items.Add('Vessel is aground');

  end
  else
  SVListBoxUnitDetail.Items.Add('bad unit selection?'); // something strange went on
end;


procedure TfrmMain.AtkLbxEGroupSelectionChange(Sender: TObject; User: boolean);
var
  strSel: String;
  PHCGThis: PHCGroup;
  PHCUThis: PHCUnit;
begin
  PSelectedUnit:=nil;
  strSel := copy(AtkLbxEGroup.Items[AtkLbxEGroup.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
     AtkGrpSrch();
     PHCGThis :=PStartGroup;
     end;                                //list reset
  PSelectedEGroup:=PHCGThis;
  AtkLbxEUnit.Items.Clear;
  AtkLbxEUnit.Sorted := True;
  PHCUThis := PHCGThis^.Units;
  while (PHCUThis <> nil) do
  begin
    If (PHCUThis^.showing[0] <> 0) then         //need to test for showing
    AtkLbxEUnit.Items.Add(Format('%0.3d: %s DP: %d - %d',
       [PHCUThis^.id, PHCUThis^.name, PHCUThis^.OrigDp, PHCUThis^.CurrDp]));
    PHCUThis := PHCUThis^.next;
  end;
end;

procedure TfrmMain.AtkLbxEUnitSelectionChange(Sender: TObject; User: boolean);
begin
  PSelectedEUnit:= PSelectedEGroup^.Units;
  while
   copy(AtkLbxEUnit.Items[AtkLbxEUnit.ItemIndex],1,3) <> Format('%0.3d',[PSelectedEUnit^.id])
   do
   PSelectedEUnit:= PSelectedEUnit^.next;
end;

procedure TfrmMain.AtkLbxFGroupSelectionChange(Sender: TObject; User: boolean);
var
  strSel: String;
  PHCGThis: PHCGroup;
  PHCUThis: PHCUnit;
begin
  strSel := copy(AtklbxFGroup.Items[AtklbxFGroup.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
     AtkgrpSrch();
     PHCGThis :=PStartGroup;
     end;                                //list reset
  PSelectedGroup:=PHCGThis;
//
  AtkLbxFUnit.Items.Clear;
  AtkLbxFUnit.Sorted := True;
  PHCUThis := PHCGThis^.Units;
  while (PHCUThis <> nil) do
  begin
    If (PHCUThis^.Air<>nil) then
    begin
     AtkLbxFUnit.Items.Add(Format('%0.3d: %d-%s',
        [PHCUThis^.id, PHCAirRec(PHCUThis^.Air)^.PlatNow, PHCUThis^.name]));
    end
    else
      if (PHCUThis^.Missile <>nil) then
      AtkLbxFUnit.Items.Add(Format('%0.3d: %d-%s',
         [PHCUThis^.id, PHCUThis^.Missile^.PlatNow, PHCUThis^.name]))
      else
            AtkLbxFUnit.Items.Add(Format('%0.3d: %s',[PHCUThis^.id, PHCUThis^.name]));
      PHCUThis := PHCUThis^.next;
  end;
  if PSelectedGroup^.Units^.Missile <> nil then
  if PSelectedGroup^.Units^.Missile^.targetUnit <> nil then
    AtkBtnTarg.Caption:= format('%s', [PHCGroup(PHCUnit(PSelectedGroup^.Units^.Missile^.targetUnit)^.Group)^.id])
  else
    AtkBtnTarg.Caption:= 'No Target';

end;

procedure TfrmMain.AtkLbxFUnitSelectionChange(Sender: TObject; User: boolean);
var
   strSel: String;
   PHCUThis: PHCUnit;
begin
  strSel := AtkLbxFUnit.Items[AtkLbxFUnit.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;
  if PSelectedUnit^.Missile <> nil then
    if PSelectedUnit^.Missile^.targetUnit <> nil then
      AtkBtnTarg.Caption:= format('%s - %0.3d', [PHCGroup(PHCUnit(PSelectedUnit^.Missile^.targetUnit)^.Group)^.id,
    PHCUnit(PSelectedUnit^.Missile^.targetUnit)^.id]);
end;


procedure TfrmMain.AGFspnbxLonEditingDone(Sender: TObject);
begin
  if PSelectedUnit <> nil then
  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
  else ShowMessage('No Unit selected');
end;

procedure TfrmMain.AGFspnbxUnitAltEditingDone(Sender: TObject);
var
   intMaxAltBand, intThrot, intOldThrot, intKts: integer;
   PThisUnit: PHCUnit;
begin
  If PselectedUnit <> nil then
  begin
  //  check max alt of aircraft           ddt 20150901
    PThisPlaneDisk:= HCAnnexHelper.HCGetAnnexB1(PSelectedUnit^.Annex);
    intMaxAltBand:= PThisPlaneDisk^.MaxAlt;
    if intMaxAltBand < AGFspnbxUnitAlt.Value then
    AGFspnbxUnitAlt.Value:= intMaxAltBand;
    PSelectedUnit^.AltBand:=AGFspnbxUnitAlt.Value;
  //   check if speed must reduce ddt 20150902 first reset throttle
    intOldThrot:= PSelectedUnit^.Air^.Throttle;
    case intOldThrot of
    0: intThrot:= 0;
    1..3:  case  PSelectedUnit^.AltBand of
          11..12: intThrot:= 1;
          13:     intThrot:= 2;
          14..15: intThrot:= 3;
          end;
    4..6:  case  PSelectedUnit^.AltBand of
          11..12: intThrot:= 4;
          13:     intThrot:= 5;
          14..15: intThrot:= 6;
          end;
    7..9:  case  PSelectedUnit^.AltBand of
          11..12: intThrot:= 7;
          13:     intThrot:= 8;
          14..15: intThrot:= 9;
          end;
    end;
                                  //check if plane needs to slow for new alt
    if PSelectedUnit^.kts > PThisPlaneDisk^.SpeedLevels[intThrot] then
    begin
    intKts:= PThisPlaneDisk^.SpeedLevels[intThrot];
    PSelectedUnit^.kts:= intKts;
// check group kts - unit may be in formation about surface unit or in an air group
    if PHCGroup(PSelectedUnit^.Group)^.kts > PSelectedUnit^.kts then
      PHCGroup(PSelectedUnit^.Group)^.kts:= PSelectedUnit^.kts;
//should check if unit can reduce throttle but maintain kts
    end;       // group should be now correct kts
  //
  AGFUnitUpdate();
  end
  else ShowMessage('No unit selected');
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;
      del_HarpoonEvent_list(PPathLeg^.pathEvents);
      DisposPtr(PPathLeg);
      PPathLeg := PPathLegPrev;
    end;
    PPathleg^.next:= nil;
    end;
end;

procedure TfrmMain.RflBtnQueueClick(Sender: TObject);
var
  PNewRefuelEvent: PHCAirRefuelEvent;
  PNewEvent: PHCHarpoonEvent;
  PURefuelTank: PHCUnit;
begin
  PURefuelTank:= PSelectedGroup^.Units;
  while (PURefuelTank <> nil) do
  begin
    if (PURefuelTank^.id = RflSpinEditTank.Value) and (PURefuelTank^.Air^.FuelToOffload > 0)
      then break;
    PURefuelTank:= PURefuelTank^.next;
  end;      // should have tank units
  if (PURefuelTank = nil) then //an error
  RflLbxDetail.Items.Add('error - no suitable supply tanker unit selected')
  else
  begin
    RflLbxDetail.Items.Add(Format('refuel queued unit %3.3d as tanker',[PURefuelTank^.id]));
    PNewRefuelEvent:= NewPtr(SizeOf(THCAirREfuelEvent));
    PNewEvent:= NewPtr(SizeOf(THCHarpoonEvent));
    PNewRefuelEvent^.fuelingUnit:= PURefuelTank^.id;
    PNewRefuelEvent^.now_refueling:= CFalse;
    PNewRefuelEvent^.fuel_range:= PUREfuelTank^.Air^.FuelToOffload * PURefuelTank^.Air^.PlatNow;
    PNewRefuelEvent^.num_tankers:= PURefuelTank^.Air^.PlatNow;
    PNewRefuelEvent^.side:= MySide;        // group tanker type and recipient units not required
    PNewEvent^.DataPtr:= PNewRefuelEvent;
    PNewEvent^.next:= nil;
    PNewEvent^.flag:= AIR_REFUEL_EVENT or TimeDelta;
    PNewEvent^.in_scroll_flag:= CFALSE;
    PNewEvent^.ourGroup:= PSelectedGroup;
    PNewEvent^.Time:= 30;
    PNewEvent^.UnitID:= PURefuelTank^.id;
    HCQueueHarpoonEvent(PNewEvent);       // dllbusy was called earlier
  end;
end;

procedure TfrmMain.RflBtnTnkrGrpSrchClick(Sender: TObject);
var
  PThisGroup: PHCGroup;
  PThisUnit: PHCUnit;

begin
  if (RflChkBTnkGrpSrch.Checked = True) then
  begin;
  Byte(PDLLBusy^) := CTRUE;
  RflChkBFind.Checked:= False;
  RflChkBResume.Checked:= False;
  RflLbxUnits.Items.Clear;
  RflLbxGroups.Items.Clear;
  RflLbxDetail.Items.Clear;
  RflLbxGroups.Sorted:=true;
  PThisGroup := PStartGroup;
  while (PThisGroup <> nil) do
    begin
      If (format('%d',[PThisGroup^.side]) = strMySide) and
        (PThisGroup^.Units^.Air <> nil)
        then
        begin           // search units of group for fuel to offload
         PThisUnit:= PThisGroup^.Units;
         while (PThisUnit <> nil) do
         begin
         If  (PThisUnit^.Air^.FuelToOffload > 0)  // or   isTanker(PThisUnit)
             then break
             else if isTanker(PThisUnit)
                  then break;
         PThisUnit := PThisUnit^.next;
         end;
         if (PThisUnit <> nil) then     // we got a tanker
           RflLbxGroups.Items.Add(PThisGroup^.id);
        end;
      PThisGroup := PThisGroup^.toSouth;
    end;
  end;
end;

procedure TfrmMain.RflBtnTransClick(Sender: TObject);
var
  PTankUnit, PRecipUnit: PHCUnit;
  intFuelTrans: integer;
  PNewRefuelEvent: PHCAirRefuelEvent;
  PNewEvent: PHCHarpoonEvent;

begin
  if (PSelectedGroup = nil)
  then  RflLbxUnits.Items.Add('Select a group')
  else
  begin
    PTankUnit:= PSelectedGroup^.Units;
    while (PTankUnit <> nil) do
    begin
      if (PTankUnit^.id = RflSpinEditTank.Value) then
      break
      else PTankUnit:= PTankUnit^.next;
    end;         //  PTankUnit should now be selected supply tank unit
    if (PTankUnit = nil)
    then RflLbxDetail.Items.Add('bad supply tanker unit selection')
      else
      begin
      if not isTanker(PTankUnit)
      then
      RflLbxDetail.Items.Add('supply unit has insufficient fuel or not a tanker')
      else
      if (RflSpinEditTank.Value = RflSpinEditRecip.Value) then       // internal tranfer from refuel
      begin
        intFuelTrans:= RflSpinEditQuant.Value;
        if (intFuelTrans > PTankUnit^.Air^.FuelToOffload)
        then intFuelTrans:= PTankUnit^.Air^.FuelToOffload;
        PTankUnit^.Air^.FuelToOffload:= PTankUnit^.Air^.FuelToOffload - intFuelTrans;
        PTankUnit^.Air^.Endurance:= PTankUnit^.Air^.Endurance + (4 * 3600 * intFuelTrans div PTankUnit^.kts);
        RflLbxDetail.Items.Add(Format('added %d mins endurance from refuel',[60 * intFuelTrans div PTankUnit^.kts]));
        RflSpinEditQuant.Value:= 0;
      end
      else
        if RflSpinEditRecip.Value = -1 then // internal transfer to refuel
        begin
        intFuelTrans:= RflSpinEditQuant.Value;                           // restrict to 2/3 current main tank load
        if ((4 * 3600 * intFuelTrans div PTankUnit^.kts) >= 2*(PTankUnit^.Air^.Endurance) div 3) then
        intFuelTrans:=  ((PTankUnit^.Air^.Endurance * PTankUnit^.kts) div (6 * 3600));
        PTankUnit^.Air^.FuelToOffload:= PTankUnit^.Air^.FuelToOffload + intFuelTrans;
        PTankUnit^.Air^.Endurance:= PTankUnit^.Air^.Endurance - (4 * 3600 * intFuelTrans div PTankUnit^.kts);
        RflLbxDetail.Items.Add(Format('added %d n.m. to refuel',[intFuelTrans]));
        RflSpinEditQuant.Value:= 0;
        RflSpinEditRecip.Value:= 0;
      end
      else
         // transfer to another unit, first find recip unit
      begin
            PRecipUnit:= PSelectedGroup^.Units;
            while (PRecipUnit <> nil) do
            begin
              if (PRecipUnit^.id = RflSpinEditRecip.Value) then
              break
              else PRecipUnit:= PRecipUnit^.next;
            end;         //  PRecipUnit now is selected recipient unit
            if (PRecipUnit = nil)
              then RflLbxDetail.Items.Add('bad recipient unit selection')
              else
              if (PrecipUnit^.Air^.FuelToOffload > 0) or isTanker(PRecipUnit) then
              begin                        // second unit legit tanker
            // fill in a new event for refuel specific (other tanker) unit
  // need to test for not over commit fuel
    if  (RflSpinEditQuant.Value * PRecipUnit^.Air^.PlatNow)
      > (PTankUnit^.Air^.FuelToOffload * PTankUnit^.Air^.PlatNow)
    then RflSpinEditQuant.Value:=
    (PTankUnit^.Air^.FuelToOffload * PTankUnit^.Air^.PlatNow div PRecipUnit^.Air^.PlatNow);
               RflLbxDetail.Items.Add(Format('queued to refuel from tanker unit %d to %d',[RflSpinEditTank.Value, RflSpinEditRecip.Value]));
               PNewRefuelEvent:= NewPtr(SizeOf(THCAirREfuelEvent));
               PNewEvent:= NewPtr(SizeOf(THCHarpoonEvent));
               PNewRefuelEvent^.fuelingUnit:= PTankUnit^.id;
               PNewRefuelEvent^.now_refueling:= CTRUE;
               PNewRefuelEvent^.fuel_range:= PTankUnit^.Air^.FuelToOffload * PTankUnit^.Air^.PlatNow;
               PNewRefuelEvent^.num_tankers:= PTankUnit^.Air^.PlatNow;
               PNewRefuelEvent^.planestofuel[0]:= PRecipUnit^.id;
               PNewRefuelEvent^.planestofuel[1]:= 255;
               PNewRefuelEvent^.fuel_per_plane[0]:= RflSpinEditQuant.Value;
               PNewRefuelEvent^.fuel_per_plane[1]:= 0;
               PNewRefuelEvent^.side:= MySide;        // group and tanker type not required
               PNewEvent^.DataPtr:= PNewRefuelEvent;
               PNewEvent^.next:= nil;
               PNewEvent^.flag:= AIR_REFUEL_EVENT or TimeDelta;
               PNewEvent^.in_scroll_flag:= CFALSE;
               PNewEvent^.ourGroup:= PSelectedGroup;
               PNewEvent^.Time:= 60;
               PNewEvent^.UnitID:= PTankUnit^.id;
               HCQueueHarpoonEvent(PNewEvent);
            end;   // we had a second tanker and have queued refuel
       end;
    end;  // finshed fuel transfer,
  end;
end;

function TfrmMain.isTanker(PTankAirUnit: PHCUnit): Boolean;
var
  PThisMount: PHCMntRec;
  PAtogDisk: PHCAnnexG1_Disk;
  ThisWepID: ID;
begin
  result:= False;     // unless made true
  if PTankAirUnit^.Air <> nil then
    if PTankAirUnit^.Mnts <> nil then
      begin
      PThisMount:= PTankAirUnit^.Mnts;
      while PThisMount <> nil do
        begin
        ThisWepID:= PThisMount^.WepId;
        if (HCAnnexHelper.HCAnnexShift(ThisWepID) = ATOG_FLAG) then
          begin
          PAtogDisk:= HCAnnexHelper.HCGetAnnexG1(ThisWepID);
          if PAtogDisk^.ClassName= 'Refuel store' then
            begin
            result:= True;
            break
            end;
          end;
        PThisMount:= PThisMount^.next;
        end;       // looked at all mounts
      end; // there were mounts
end;

procedure TfrmMain.RflChkBFindChange(Sender: TObject);
var
  PEvent: PHCHarpoonEvent;
begin
  if (RflChkBFind.Checked = True) then
  begin
    RflChkBResume.Checked:= False;
    RflChkBTnkGrpSrch.Checked:= False;
    RflLbxUnits.Clear;
    RflLbxDetail.Clear;
    Byte(PDLLBusy^):= CTRUE;
    RflLbxGroups.Clear;
    PEvent:= PHCEventHead;
    while (PEvent <> nil) do
    begin
      if (((PEvent^.flag and CommandMask) = AIR_REFUEL_EVENT) and (PHCGroup(PEvent^.ourGroup)^.side = MySide)) then
      RflLbxGroups.Items.Add(Format('%3.3s, %d',[PHCGroup(PEvent^.ourGroup)^.id, PEvent^.Time]));
      PEvent:= PEvent^.next;
    end;
  end;
end;

procedure TfrmMain.RflChkBResumeChange(Sender: TObject);
begin
  if (RflChkBResume.Checked = True) then
  begin
      if RflChkBPause.Checked = True then
      begin
      RflChkBFind.Checked:= False;
      RflChkBTnkGrpSrch.Checked:= False;
      RflLbxGroups.Clear;
      RflLbxUnits.Clear;
      RflLbxDetail.Clear;
      RflSpinEditTank.Value:=0;
      RflSpinEditRecip.Value:=0;
      RflSpinEditQuant.Value:=0;
      PCurrentEvent:= nil;
      PSelectedGroup:= nil;
      PageControl1.ActivePageIndex:= intPageActive;
      end;
    Byte(PDLLBusy^):= CFALSE;
  end;
end;

procedure TfrmMain.RflLbxGroupsSelectionChange(Sender: TObject; User: boolean);
var
  strGroupID: string;
  PThisEvent: PHCHarpoonEvent;

begin
  if (RflChkBFind.Checked = True) then
  begin
    strGroupID:= copy(RflLbxGroups.Items[RflLbxGroups.ItemIndex],1,2);
    PThisEvent:= PHCEventHead;
    while (PThisEvent <> nil) do
    begin
      if (PThisEvent^.ourGroup <> nil) then
        if (strGroupID = copy(PHCGroup(PThisEvent^.ourGroup)^.id,1,2))  then
          if ((PThisEvent^.flag and CommandMask) = AIR_REFUEL_EVENT)
          then
          break;
     PThisEvent:= PThisEvent^.next;
    end;
                          // ddt we should have the group
  if (PThisEvent <> nil) then
  begin
   RflLbxUnitFill(PThisEvent^.ourGroup);
   RflLbxDetailFill(PthisEvent);
  end
  else    RflLbxUnits.Items.Add('error?');

  end
  else      // not a refuel event search
  begin
   PSelectedGroup:= PStartGroup;
   strGroupID:= copy(RflLbxGroups.Items[RflLbxGroups.ItemIndex],1,2);
   while (PSelectedGroup <> nil) do
   begin
     if (strGroupID = copy(PSelectedGroup^.id,1,2)) then
     break;
     PSelectedGroup:= PSelectedGroup^.toSouth;
   end;
   if (PSelectedGroup <> nil) then
     RflLbxUnitFill(PSelectedGroup);
  end;
end;

procedure TfrmMain.SqPatrl(SGroup:PHCGroup;PatType:Integer);
var
   PPathLeg, PPathLegNext: PHCPathRec;
   fctr, intLegCnt, PatAltBand: integer;
   cLat, cLon: LongInt;
   LatFactor: double;
   PPathEvent1, PPathEvent2: PHCHarpoonEvent;
   PSpeedAltEventLoiter, PSpeedAltEventReset: PHCSpeedAltEvent;
   PEndMessage: PHCMessageEvent;
   PRadarSet: PHCIntermittentSensorsEvent;
   intSortieKts: short;
   SortieThrottle: uchar;

  begin
     if (SGroup^.Units^.Air = nil) then
  ShowMessage('Air Groups Only')
     else
    begin
     if (SGroup^.Units^.Air^.Throttle=0) then
     ShowMessage('Set AirGroup to Cruise')
     else
    begin
     intSortieKts:= SGroup^.kts;      //used to reset cruise speed and throttle
     SortieThrottle:= SGroup^.Units^.Air^.Throttle;

     case PatType of
     0:     PatAltBand:= 12;
     1:     PatAltBand:= SGroup^.Units^.AltBand;       // consider setting to highest alt of group
     end;

     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;

     PPathEvent1:= NewPtr(SizeOf(THCHarpoonEvent));
     PPathEvent2:= NewPtr(SizeOf(THCHarpoonEvent));

     PSpeedAltEventReset:= NewPtr(SizeOf(THCSpeedAltEvent));
     PSpeedAltEventReset^.newAltBand:= PatAltBand;
     PSpeedAltEventReset^.newSpeed:= intSortieKts;
     PSpeedAltEventReset^.newThrottle:= SortieThrottle;
     Case PatType of
     0: begin
     PSpeedAltEventLoiter:= NewPtr(SizeOf(THCSpeedAltEvent));
     PSpeedAltEventLoiter^.newAltBand:= PatAltBand;
     PSpeedAltEventLoiter^.newThrottle:= 0;
     PSpeedAltEventLoiter^.newSpeed:= 2 * intSortieKts div 3;
        end;
     1: begin
     PRadarSet:= NewPtr(SizeOf(THCIntermittentSensorsEvent));
     PRadarSet^.Duration:=60;
     PRadarSet^.gtype:=000;
     PRadarSet^.Zones[0]:=1;         //set AS on main zone
     PRadarSet^.Zones[1]:=1;         //set SS  on main zone
     PRadarSet^.Interval:=10;
     PRadarSet^.Variance:=0;
     PRadarSet^.Toggle:=000;         //start on
        end;
     end;
     PPathEvent2^.next:= nil;
     PPathEvent2^.ourGroup:= SGroup;
     PPathEvent2^.UnitID:= NoUnit;
     PPathEvent2^.Time:= 60;
     PPathEvent2^.flag:= ALT_SPEED_EVENT or TimeDelta;
     PPathEvent2^.in_scroll_flag:= 001;
     PPathEvent2^.DataPtr:=PSpeedAltEventReset;
     case patType of
     0: begin
     PPathEvent1^.DataPtr:= PSpeedAltEventLoiter;
     PPathEvent1^.flag:= ALT_SPEED_EVENT or TimeDelta;
        end;
     1: begin
     PPathEvent1^.DataPtr:=PRadarSet;
     PPathEvent1^.flag:= INTERMITTENT_EVENT or TimeDelta;
        end;
     end;
     PPathEvent1^.UnitID:= NoUnit;
     PPathEvent1^.in_scroll_flag:= 001;
     PPathEvent1^.ourGroup:= SGroup;
     PPathEvent1^.Time:= 0;
     PPathEvent1^.next:= PPathEvent2;

     PPathLeg^.pathEvents:= PPathEvent1;

     SGroup^.track:=PPathLeg;
                                                     //now do remaining patrol legs
     for intLegCnt := 1 to 16   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,9: begin
     PPathLeg^.DestLat:=cLat - Round(NM1*fctr*0.8);
     PPathLeg^.DestLon:=cLon + Round(NM1*fctr*0.8 / LatFactor);
        end;
     2,10: begin
     PPathLeg^.DestLat:=cLat;
     PPathLeg^.DestLon:=cLon + Round(NM1*fctr / LatFactor);
        end;
     3,11: begin
     PPathLeg^.DestLat:=cLat + Round(NM1*fctr*0.8);
     PPathLeg^.DestLon:=cLon + Round(NM1*fctr*0.8 / LatFactor);
        end;
     4,12: begin
     PPathLeg^.DestLat:=cLat + (NM1*fctr);
     PPathLeg^.DestLon:=cLon;
        end;
     5,13: begin
     PPathLeg^.DestLat:=cLat + Round(NM1*fctr*0.8);
     PPathLeg^.DestLon:=cLon - Round(NM1*fctr*0.8 / LatFactor);
        end;
     6,14: begin
     PPathLeg^.DestLat:=cLat;
     PPathLeg^.DestLon:=cLon - Round(NM1*fctr / LatFactor);
        end;
     7,15: begin
     PPathLeg^.DestLat:=cLat - Round(NM1*fctr*0.8);
     PPathLeg^.DestLon:=cLon - Round(NM1*fctr*0.8 / LatFactor);
        end;
     8: begin
     fctr:= 2 * fctr div 3;
     PPathLeg^.DestLat:=cLat - (NM1*fctr);
     PPathLeg^.DestLon:=cLon;
        end;
     16: begin
     PPathLeg^.DestLat:= cLat;
     PPathLeg^.DestLon:= clon;
         end;
     end;

    PPathEvent1:= NewPtr(SizeOf(THCHarpoonEvent));
    PPathEvent2:= NewPtr(SizeOf(THCHarpoonEvent));
    PSpeedAltEventLoiter:= NewPtr(SizeOf(THCSpeedAltEvent));
    PSpeedAltEventReset:= NewPtr(SizeOf(THCSpeedAltEvent));
    PSpeedAltEventReset^.newAltBand:= SGroup^.Units^.AltBand;
    PSpeedAltEventReset^.newSpeed:= intSortieKts;
    PSpeedAltEventReset^.newThrottle:= SortieThrottle;
    PSpeedAltEventLoiter^.newAltBand:= PatAltBand;
    PSpeedAltEventLoiter^.newThrottle:= 0;
    PSpeedAltEventLoiter^.newSpeed:= 2 * intSortieKts div 3;

    PPathEvent2^.next:= nil;
    PPathEvent2^.ourGroup:= SGroup;
    PPathEvent2^.UnitID:= NoUnit;
    PPathEvent2^.Time:= 60;
    PPathEvent2^.flag:= ALT_SPEED_EVENT or TimeDelta;
    PPathEvent2^.in_scroll_flag:= 001;
    PPathEvent2^.DataPtr:=PSpeedAltEventReset;

    PPathEvent1^.DataPtr:= PSpeedAltEventLoiter;
    PPathEvent1^.flag:= ALT_SPEED_EVENT or TimeDelta;
    PPathEvent1^.in_scroll_flag:= 001;
    PPathEvent1^.ourGroup:= SGroup;
    PPathEvent1^.Time:= 0;
    PPathEvent1^.UnitID:= NoUnit;
    PPathEvent1^.next:= PPathEvent2;

    PPathLeg^.pathEvents:= PPathEvent1;
     end;
     PPathLeg^.next:=nil;
     PEndMessage:= NewPtr(SizeOf(THCMessageEvent));
     PEndMessage^.Message:= PAnsiChar('Patrol complete, require orders');
     PPathEvent2^.DataPtr:= PEndMessage;
     PPathEvent2^.flag:= MESSAGE_EVENT or TimeDelta;
     PPathEvent2^.in_scroll_flag:= 001;
     PPathEvent2^.ourGroup:= SGroup;
     PPathEvent2^.Time:= 0;
     PPathEvent2^.UnitID:= SGroup^.Units^.id;
     end;
    end;
end;

procedure TfrmMain.RflLbxUnitFill(PRefuelGroup: PHCGroup);
var
   PUnit: PHCUnit;
   intEndMins, intEndNM: integer;
begin
   RflLbxUnits.Clear;
   PUnit:= PRefuelGroup^.Units;
   RflLbxUnits.Items.Add(Format('%d kts %d deg',[PUnit^.kts, PRefuelGroup^.course]));

   while (PUnit <> nil) do
   begin
    intEndMins:= (PUnit^.Air^.Endurance) div (4 * 60);
    intEndNM:= (intEndMins * PUnit^.kts) div 60;
    RflLbxUnits.Items.Add(Format('%0.3d: %d-%s',[PUnit^.id, PUnit^.Air^.PlatNow, PUnit^.name]));
    RflLbxUnits.Items.Add(Format('endurance: %d mins; %d n.m.',[intEndMins, intEndNM]));
    if (PUnit^.Air^.FuelToOffload >0)
    then RflLbxUnits.Items.Append(Format('refuel available: %d nm per tank',[PUnit^.Air^.FuelToOffload]));
    PUnit:= PUnit^.next;
   end; // unit box complete,

end;

procedure TfrmMain.RflLbxDetailFill(PRefuelEvent: PHCHarpoonEvent);
var
   PRefuelEventDetail: PHCAirRefuelEvent;
   strUnitID, strFuelling: string;
   intTotFuel, N: integer;
begin
  RflLbxDetail.Clear;
  PRefuelEventDetail:= PRefuelEvent^.DataPtr;
 //
  strUnitID:= Format('%3.3d',[PRefuelEventDetail^.fuelingUnit]);
  PSelectedUnit:= PHCGroup(PRefuelEvent^.ourGroup)^.Units;
  while (PSelectedUnit <> nil) do
  begin
    if (strUnitID = Format('%3.3d',[PSelectedUnit^.id])) then
    break;
    PSelectedUnit:= PSelectedUnit^.next;
  end;
  if (PSelectedUnit <> nil) then    // need to test for nil pselectedunit for if tanker split off
  begin
  intTotFuel:= (PSelectedUnit^.Air^.FuelToOffload) * strToInt(Format('%d',[PRefuelEventDetail^.num_tankers]));
  if (PRefuelEventDetail^.now_refueling = 0) then
    strFuelling:= 'No'
    else strFuelling := 'Yes';
  //
  RflLbxDetail.Items.Add(Format('Fueling Unit: %3.3d', [PRefuelEventDetail^.fuelingUnit]));
  RflLbxDetail.Items.Add(Format('Number of Tankers: %2.2d - %s', [PRefuelEventDetail^.num_tankers, PSelectedUnit^.name]));
  RflLbxDetail.Items.Add('Fueling: ' + strFuelling);
  RflLbxDetail.Items.Add(Format('Original Refuel Range: %4.4d', [PRefuelEventDetail^.fuel_range]));
  RflLbxDetail.Items.Add(Format('Available Refuel Range %4.4d',[intTotFuel]));
  RflLbxDetail.Items.Add('. . . . . . . .');
  // now do the queue
  N := 0;
  repeat
  begin
  RflLbxDetail.Items.Add(Format('# %d: unit %3.3d', [N, PRefuelEventDetail^.planestofuel[N]]));
  If PRefuelEventDetail^.planestofuel[N] = 255 then
    RflLbxDetail.Items.Add('Refuel cycle finished')
    else
    if
      PRefuelEventDetail^.planestofuel[N] = 205 then
      RflLbxDetail.Items.Add('Awaiting start of refuel?')
      else
      RflLbxDetail.Items.Add(Format('Per Plane Fuel: %4.4d', [PRefuelEventDetail^.fuel_per_plane[N]]));
  N := N+1;
  end;
  until  PRefuelEventDetail^.planestofuel[N-1] > 200;
  PCurrentEvent:= PRefuelEvent;
  end
  else RflLbxDetail.Items.Add('refuel tanker has been split off?');
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.

