unit MainForm;

{$mode objfpc}{$H+}

interface

uses
  Classes, SysUtils, FileUtil, Forms, Controls, Graphics, Dialogs, StdCtrls,
  ComCtrls, Spin, ExtCtrls,  uHCTypeDefs, uHCAnnexDefs, uHCAnnexHelpers,
  uLazGUIGlobals ,CheckLst, XMLPropStorage, Menus, uHCUtils;

type

  { TfrmMain }

  TfrmMain = class(TForm)
    SGbtnURep: TButton;
    GTbtnCheck: TButton;
    GTbtnRepair: TButton;
    GTSpnEdSctr: TSpinEdit;
    GTSpnEdRng: TSpinEdit;
    GTlblRng: TLabel;
    GTlblSctr: TLabel;
    StrExpAirBlTally: TMemo;
    StrExpAirRdTally: TMemo;
    StoreExpRedLst: TMemo;
    StoreExpAccumLst: TMemo;
    SLTLblPrvID: TLabel;
    SLTLblThsID: TLabel;
    SLTLblThisUUID: TLabel;
    SLTLblLastUUID: TLabel;
    StoreExpLst: TMemo;
    SGlblDetQual: TLabel;
    SGlblUnitKts: TLabel;
    SGChkBxRTU: TCheckBox;
    SGTButKing: TButton;
//    SGTButUUID: TButton;
    SGTButDrone: TButton;
    SGBtnGrpRem: TButton;
    SGBtnUntRem: TButton;
    SGlblUnitShowing: TLabel;
    SGButtonSD: TButton;
    tshStoreExpend: TTabSheet;
 //   SGSpnEdAIChance: TSpinEdit;     240301
    UNedtLabel: TLabel;
    UNedtLines: TSpinEdit;
    UNckbEnabled: TCheckBox;
    UNmemMsgLog: TMemo;
    SGButtonResume: TButton;
    tshUN: TTabSheet;
    VClstbxItems: TListBox;
    SGlblClassName: TLabel;
    SGButtonRefresh: TButton;
    GTcklbAutoP: TCheckListBox;
    GTckbAutoP: TCheckBox;
    GTbtnResume: TButton;
    GTckbPause: TCheckBox;
    GTlblGameTime: TLabel;
    GTHEEventList: TListBox;
    GEHEmemDetail: TMemo;
    GTlblGameTimeLeft: TLabel;
    SGListBoxUnitInfo: TListBox;
    tshGT: TTabSheet;
    tshVC: TTabSheet;
    VCckbEnabled: TCheckBox;
    VCedtLines: TSpinEdit;
    VClblLines: TLabel;
    SGlblUnitIDName: TLabel;
    SGpnlUnit: TPanel;
    SGlblGroups: TLabel;
    SGlblUnits: TLabel;
    SGlbxGroups: TListBox;
    SGlbxUnits: TListBox;
    SGpnl1: TPanel;
    SGlblDesc: TLabel;
    memDLLInit: TMemo;
    SLmemMsgLog: TMemo;
    tshStartGroup: TTabSheet;
    tshDLLInit: TTabSheet;
    tshScenLoad: TTabSheet;
    tshTimeFactor: TTabSheet;
    tshUnitShowing: TTabSheet;
    TFckbEnabled: TCheckBox;
    TFedtLines: TSpinEdit;
    TFlblLines: TLabel;
    TFmemMsgLog: TMemo;
    MLckbEnabled: TCheckBox;
    USckbEnabled: TCheckBox;
    MLlblLines: TLabel;
    USedtLines: TSpinEdit;
    MLmemMsgLog: TMemo;
    USlblLines: TLabel;
    PageControl1: TPageControl;
    MLedtLines: TSpinEdit;
    tshMsgLog: TTabSheet;
    USmemMsgLog: TMemo;
    SessionSze: TXMLPropStorage;
//    procedure DUbtnUpdateClick(Sender: TObject);
    procedure GTbtnCheckClick(Sender: TObject);
    procedure GTbtnRepairClick(Sender: TObject);
    procedure GTSpnEdRngEditingDone(Sender: TObject);
    procedure GTSpnEdSctrEditingDone(Sender: TObject);
    procedure SGbtnURepClick(Sender: TObject);
    procedure SGlblUnitKtsFill();
    procedure GTbtnResumeClick(Sender: TObject);
    procedure GTHEEventListSelectionChange(Sender: TObject; User: boolean);
    procedure SessionSzeRestoreProperties(Sender: TObject);
    procedure SessionSzeSavingProperties(Sender: TObject);
    procedure SGBtnGrpRemClick(Sender: TObject);
    procedure SGBtnUntRemClick(Sender: TObject);
//    procedure HHQueueEventMessageClick(Sender: TObject);
    procedure SGButtonRefreshClick(Sender: TObject);
    procedure SGButtonResumeClick(Sender: TObject);
    procedure SGButtonSDClick(Sender: TObject);
    procedure SGlbxGroupsDblClick(Sender: TObject);
//    procedure SGCurntSpdSpnEdEditingDone(Sender: TObject);
//    procedure SGesmSpnEdChange(Sender: TObject);
//    procedure SGlblSpnEdAIChanceClick(Sender: TObject);
    procedure SGlbxGroupsSelectionChange(Sender: TObject; User: boolean);
    procedure SGlbxUnitsDblClick(Sender: TObject);
    procedure SGlbxUnitsSelectionChange();  //  240613  (Sender: TObject; User: boolean);
    procedure EventmemDetailWrite;
 //   procedure SGMaxKtsSpnEdChange; //(Sender: TObject);
 //   procedure SGNewUUIDRadBtnClick(Sender: TObject);
 //   procedure SGRCSSpnEdChange(Sender: TObject);
    procedure SGSizeSpnEdChange(Sender: TObject);
    procedure SGTButKingClick(Sender: TObject);
    procedure UNbtnResumeClick(Sender: TObject);
    function HCMT2String(const ValHCMT: HCMT): string;
    function Side2String(const ThisSide: uchar): string;
    function Showing2String(const intShow: integer): String;
    function DetQuality2String(const intQual: integer): String;

  private
    { private declarations }
  public
    { public declarations }
    // ALE 20130812 Big bad pointers that hold most of the game data
    PStartGroup, PSelectedGroup, PHCSGThis: PHCGroup;
    PHarpEventHead, PEvent: PHCHarpoonEvent; 
//  250624  pFormationEvent: PHCFormationEvent;     //  250622
  PAirPatrolEvent: PHCAirPatrolEvent;     //  250624
    PSonobuoyRegionHead: PHCSonobuoyRegion;
    PAllDeadUnitsPtr: PHCDeadListItem;
    PWeatherGroups: PHCGroup;
    PVCList: PHCVictoryConditions;
    // ALE 20130812 transient pointers
    PSelectedUnit, PEventUnit, gPHCtopUnit: PHCUnit;
    // ALE 20130812 misc globals
    //blnInvestigatingStructs: Boolean;
    // ALE 20130908 pointers to functions exported by the GE
    HCQueueHarpoonEvent: THCQueueHarpoonEvent;
    NewPtr: THCNewPtr;
    HCdllCheckLatLonDepth: THCdllCheckLatLonDepth;
    HCannex_id_to_disk_id: THCannex_id_to_disk_id;
    HCdisk_id_to_annex_id: THCdisk_id_to_annex_id;
    HCChangeActGroupPtr: THCChangeActGroupPtr;
    HCChangeActUnitPtr: THCChangeActUnitPtr;

//    PHCAnnexA2: PHCAnnexA2_disk;     // sub moved to public for spin box
//    PHCAnnexA1: PHCAnnexA1_disk;     // ship ditto   240411 moved back
    intPSide, intESide: uchar;
    strSide, strOSide: string;
    HCfree_Unit: THCfree_Unit;
    HCfree_Group: THCfree_Group;
    intGTNow: LongInt;
  end;

var
  frmMain: TfrmMain;


implementation

uses uHCDefines, uHCEventHelpers;

{$R *.lfm}

{ TfrmMain }

function TfrmMain.Side2String(const ThisSide: uchar): string;

  begin
    case ThisSide of
    0: result:= 'Blue';
    1: result:= 'Red';
    2: result:= 'Green';
    else result:= 'Unknown';
    end;
  end;

function TfrmMain.HCMT2String(const ValHCMT: HCMT): string;
var
  strHCMT: string;

begin
  case ValHCMT  of
       MT_AC:         result := 'Plane';
       MT_HELO:       result := 'Helo';
       MT_CARRIER:    result := 'Carrier';
       MT_SHIP:       result := 'Surface Vessel';
       MT_SUB:        result := 'Submarine';
       MT_AIRFIELD:   result := 'Airfield';
       MT_PORT:       result := 'Port';
       MT_BASE:       result := 'Port & Airfield';
       MT_CITY:       result := 'City';
       MT_MISSILE:    result := 'Missile';
       MT_TORP:       result := 'Torpedo';
       MT_SAMFIXED:   result := 'AD Fixed';
       MT_SAMMOBILE:  result := 'AD Mobile';
       MT_CONVOY:     result := 'Convoy';
       MT_ARMORED:    result := 'Armored';
      else         result := 'Unkn ' + IntToStr(ValHCMT);

  end;
end;

procedure TfrmMain.SGlbxGroupsSelectionChange(Sender: TObject; User: boolean);
var
  strSel: String;
  PHCUThis: PHCUnit;
begin
   // ALE 20130317 User selected a group, pull up unit list.
  strSel := SGlbxGroups.Items[SGlbxGroups.ItemIndex];
  PHCSGThis := PStartGroup;
  while (PHCSGThis <> nil) do
  begin
    if (strSel = PHCSGThis^.id) then
       break;
    PHCSGThis := PHCSGThis^.toSouth;
  end;
  // ALE 20130317 in theory we have the group from which
  //  we can enumerate the units.
  SGlblUnitIDName.Caption:= '';
  SGlblClassName.Caption:=  '';
  SGlblUnitShowing.Caption:= '';          //  20230503  clear all boxs until a unit is selected
  SGlblUnitKts.Caption:= '';
  SGlblDetQual.Caption:= '';
  SGListBoxUnitInfo.Items.Clear;
  SGlbxUnits.Items.Clear;
  if PHCSGThis <> nil then
  begin
  PSelectedGroup:= PHCSGThis;
//  PSelectedUnit:= nil;      // 250801  240907  reset public selected unit
  SGlbxUnits.Sorted := True;
  PHCUThis := PHCSGThis^.Units;        //  list units
  while (PHCUThis <> nil) do
  begin
  if PHCUThis^.Missile = nil           //  240907 add missile test and platNo
  then
  SGlbxUnits.Items.Add(Format('%2.2s%0.3d %s', [PHCSGThis^.id, PHCUThis^.id, PHCUThis^.name]))
  else
  SGlbxUnits.Items.Add(Format('%2.2s%0.3d %d %s',
    [PHCSGThis^.id, PHCUThis^.id, PHCUThis^.Missile^.PlatNow, PHCUThis^.name]));
    PHCUThis := PHCUThis^.next;
  end;
  SGBtnGrpRem.Enabled := True;                          //  20230427  for group free (remove) function
    //  20230603 add group info to the unit detail section
  SGlblUnitIDName.Caption:= Format('%s %s',[Side2String(PHCSGThis^.side), HCMT2String(PHCSGThis^.gtype)]);
  SGlblClassName.Caption:= Format('Grp Speed: %d/%d kts, course: %d degs',
      [PHCSGThis^.kts, PHCSGThis^.maxspeed, PHCSGThis^.course]);
  SGListBoxUnitInfo.Items.Add('UUID: ' + PHCSGThis^.UUID); //  250405
  SGListBoxUnitInfo.Items.Add(Format('Grp RCS: %d, VCS: %d, ECM: %d',[PHCSGThis^.RCS, PHCSGThis^.VCS,
      PHCSGThis^.ecm]));
  SGlblUnitShowing.Caption:= Format('Group''s opponent %s',[Showing2String(PHCSGThis^.showing)]);
  if PHCSGThis^.Units^.Missile <> nil
    then
    begin
      if PHCSGThis^.Units^.Missile^.targetUnit <> nil
      then strSel:= Format('%2.2s',[PHCGroup(PHCUnit(PHCSGThis^.Units^.Missile^.targetUnit)^.Group)^.id])
      else strSel:= 'Nil';
      SGlblDetQual.Caption:= Format('Target = %s, Shooter = %2.2s',
        [strSel, PHCGroup(PHCUnit(PHCSGThis^.Units^.Missile^.sourceUnit)^.Group)^.id]);  // 240905 + 240908
    end;

//  SGListBoxUnitInfo.Items.Add(Format('AI Risk Taking: %d',[PHCSGThis^.AIChance]));
//  SGCurntSpdSpnEd.Value:= PHCSGThis^.kts;     //  240315 set up to edit group speed
//  SGCurntSpdSpnEd.MaxValue:= PHCSGThis^.maxspeed;
  end
  else
  SGlbxUnits.Items.Add('Selected Group no longer valid'); // test for valid group
end;

procedure TfrmMain.SGlbxUnitsDblClick(Sender: TObject);
begin
 // HCChangeActUnitPtr(PSelectedUnit);
end;

procedure TfrmMain.GTbtnResumeClick(Sender: TObject);
begin
  GTHEEventList.Clear;      // want to leave so as to have last event of interest visible
  GEHEmemDetail.Clear;         //  20230406  changed mind, want cleared
//  GTckbPause.Checked:=False;    testtonight 231117
  GTbtnResume.Enabled:=False;
  SGButtonResume.Enabled:=False;
  GTckbPause.Enabled:=True;
  Byte(PDLLBusy^) := CFALSE; // ALE 20130816
  WindowState:= wsMinimized;      // ddt 20230406  hide window after resume from event info showing
end;

procedure TfrmMain.GTHEEventListSelectionChange(Sender: TObject; User: boolean);
var
  strSel: String;
  intIndx: Integer;
  PHCGThis: PHCGroup;
  PHCUThis: PHCUnit;

begin
  // ALE 20130317 User selected a group, pull up unit list.
  intIndx:= Pos('uuid:', GTHEEventList.Items[GTHEEventList.ItemIndex]);
  strSel := Copy(GTHEEventList.Items[GTHEEventList.ItemIndex], intIndx + 5, HCUUID_LEN - 1);
  PEvent := PHarpEventHead;
  while (PEvent <> nil) do
  begin
    if (Trim(strSel) = PEvent^.uuid) then
       break;
    PEvent := PEvent^.next;
  end;
  If (PEvent = nil)
    then   GEHEmemDetail.Lines.Add('error, no event')
    else
//    if (PEvent^.ourGroup <> nil) -250827
//      then
      EventmemDetailWrite ;
//      else
//      begin
//      GEHEmemDetail.Lines.Clear;
//      GEHEmemDetail.Lines.Add('error, no event group');
//      end;        // 20230416
end;

procedure TfrmMain.SessionSzeRestoreProperties(Sender: TObject); // set location  20230409
begin
  //Top:= StrToInt(SessionSze.StoredValue['frmMain_Top']);               //  do I need to test for file exists?
  //Width:= StrToInt(SessionSze.StoredValue['frmMain_Width']);           // if FileExists(Application.Location + LAZGExtn.xml) then ..
  //Height:= StrToInt(SessionSze.StoredValue['frmMain_Height']);
  //Left:= StrToInt(SessionSze.StoredValue['frmMain_Left']);
end;

procedure TfrmMain.SessionSzeSavingProperties(Sender: TObject);  // store location 20230409
begin
  SessionSze.StoredValue['frmMain_Top']:= IntToStr(Top);        // move to boundary changed  too
  SessionSze.StoredValue['frmMain_Width']:= IntToStr(Width);
  SessionSze.StoredValue['frmMain_Height']:= IntToStr(Height);
  SessionSze.StoredValue['frmMain_Left']:= IntToStr(Left);
end;

procedure TfrmMain.SGBtnGrpRemClick(Sender: TObject);
begin                                                //  20230426 added
  if (PSelectedGroup <> nil) then                    // maybe need to freeze GE ?
  begin
    HCfree_Group(PSelectedGroup);
    PSelectedGroup := nil;
    SGBtnGrpRem.Enabled := False;
    SGbuttonRefresh.Click;
  end;
end;

procedure TfrmMain.SGBtnUntRemClick(Sender: TObject);
begin                                                //  20230426 added
   if (PSelectedUnit <> nil) then
  begin                                    // maybe need to freeze processing in GE?
    HCfree_Unit(PSelectedUnit);
    PSelectedUnit := nil;
    PSelectedGroup:= nil;
    SGBtnUntRem.Enabled := False;
    SGbuttonRefresh.Click;
  end;
end;

procedure TfrmMain.EventmemDetailWrite;
var
//  PEvent is made public
  PEventMessage: PHCMessageEvent;
  PRefuelEvent: PHCAirRefuelEvent;
  PInterceptEvent: PHCInterceptEvent;
 // 250624 made public PAirPatrolEvent: PHCAirPatrolEvent;
  pFormationEvent: PHCFormationEvent;
  PAirAttackEvent: PHCAirAttackEvent;
  PLaunchLandEvent: PHCLaunchLandEvent;
  PLaunchAirEvent: PHCLaunchAirEvent;
  PLandAirEvent:  PHCLandAirEvent;
  PSplitGroupEvent : PHCSplitGroupEvent;
  PJoinGroupEvent: PHCJoinGroupEvent;
  PEnemyAIEvent: PHCEnemyAIEvent;
  PSpeedAltEvent: PHCSpeedAltEvent;
  PAltCourseEvent: PHCAltCourseEvent;
  PAttackEvent: PHCAttackEvent;
  PReadyEvent: PHCReadyAirEvent;
  pReloadEvent: PHCReloadEvent;
  pNukeFreeEvent: PHCNukeFreeEvent;
  PThisLoadout: PHCAnnexY1_disk;
  PThisAC: PHCAnnexB1_disk;
  lPThisGroup: PHCGroup;
  lPThisUnit: PHCUnit;
  lPThisPlane: PHCPlaneRec;

  N, intEType, intTotFuel: Integer;
  strEType, strAltBand, strNewSpeed, strNewThrottle, strAirType,
    strLoadOutName, strAirPatStat : string;
  strUnitID: uchar;

begin
GEHEmemDetail.Clear;
GTSpnEdSctr.Clear;
GTSpnEdRng.Clear;

GEHEmemDetail.Lines.Add(Format('Event Type: %s, ex-T %3d', [HCEventType2String(PEvent^.event_type),
             PEvent^.Time]));    //  240908 added time
if PEvent^.unit_uuid <> '' then        //  250709  then there's an evebt unit
 begin
   PEventUnit:= HCuuid2UnitPtr(PEvent^.unit_uuid, pStartGroup, PEvent^.ourGroup);
   if  HCIsValidUnit(PEventUnit, gPHCtopUnit) = CTRUE
     then
     begin
     strUnitID:= PEventUnit^.id;      // vers update  / flag AND CommandMask
     GEHEmemDetail.Lines.Add(Format('Group:unit %2.2s:%0.3d - %s',
    [PHCGroup(PEventUnit^.Group)^.id, strUnitID, PEventUnit^.name]));    //  250711  use proper unit's group ID
     GEHEmemDetail.Lines.Add(Format('Unit uuid: %s',[ PEvent^.unit_uuid]));
     end
     else GEHEmemDetail.Lines.Add(Format('Invalid unit UUID: %s',
          [PEvent^.unit_uuid]));       //  250710 tracking for invalid air patrol Uuuids
 end
else
 begin        // there is no event unit
  if PEvent^.ourGroup <> nil            //  250710
    then GEHEmemDetail.Lines.Add(Format('Group: %3.3s',
        [PHCGroup(PEvent^.ourGroup)^.id]))
    else  GEHEmemDetail.Lines.Add('No Event Group');  //  250827
 end;
GEHEmemDetail.Lines.Add('................');
//ddt now, event specific additional information
// message event
if (PEvent^.DataPtr <> nil)
  then
begin
if (PEvent^.event_type = MESSAGE_EVENT) then        //  vers update /flag AND CommandMask
  begin
    PEventMessage := PEvent^.DataPtr;
    GEHEmemDetail.Lines.Add(Format('Message: %s', [PEventMessage^.Message]));
  end;
//LandAirEvent
if (PEvent^.event_type =  LANDING_EVENT) then
  begin
       PLandAirEvent := PEvent^.DataPtr;
       case  PLandAirEvent^.planeUnit of
         0: strEType:= 'AirPatrol Landing';
         1: strEType:= 'AirGroup Landing';
       end;
       GEHEmemDetail.Lines.Add(Format('Landon: %2.2s: %3.3d, %s',
         [PLandAirEvent^.landGroup, PLandAirEvent^.landUnit, strEType]));  //  240330   strEType 250729
  end;
//  Group Join  240330             need to allow for the numjoins = short(Event.dtrP)
if (PEvent^.event_type =  JOIN_EVENT) then
  begin
       N:= short(PEvent^.DataPtr^);           //  gives number of joins
       repeat
       begin
          PJoinGroupEvent:= PEvent^.DataPtr + sizeof(short) + (N-1)*(sizeof(THCJoinGroupEvent));
 //         GEHEmemDetail.Lines.Add(IntToStr(N));
          GEHEmemDetail.Lines.Add(Format('SourceGroup: %s, JoinGroup: %s, Unit: %d, Number: %d',
             [PJoinGroupEvent^.SplitGroup, PJoinGroupEvent^.JoinGroup, PJoinGroupEvent^.UnitID,
             PJoinGroupEvent^.Number]));
          N := N-1;
       end;
       until N = 0;

  end;
// Group Split  240330             need to allow for the numsplit =   uchar(event.dtrP)
if (PEvent^.event_type =  SPLIT_EVENT) then
  begin
    N:= uchar(PEvent^.DataPtr^);  // should be number of moves
    Repeat
    begin
       PSplitGroupEvent:= PEvent^.DataPtr + sizeof(uchar) + (N-1)*(sizeof(THCSplitGroupEvent));
       GEHEmemDetail.Lines.Add(Format('Unit: %d, Quantity: %d',
           [PSplitGroupEvent^.UnitID[0][0], PSplitGroupEvent^.UnitID[0] [1]]));
       N := N-1;
    end;
    until N = 0;
  end;
//REadyAirEvent
if (PEvent^.event_type = READY_EVENT)then
  begin
    PReadyEvent:= PEvent^.DataPtr;
    PThisLoadout:= HCAnnexHelper.HCGetAnnexY1(PReadyEvent^.LoadID);
 //   strLoadOutName:= PThisLoadout^.MissionID;    need helper to tourn to string
    PThisAC:= HCAnnexHelper.HCGetAnnexB1(PReadyEvent^.whichPlaneType);
    strAirType:= PThisAC^.ClassName;
    GEHEmemDetail.Lines.Add(Format('ReadyAirEvent: PlaneClass %s; number: %d; loadout: %d',
      [strAirType, PReadyEvent^.numberAircraft, PThisLoadout^.MissionID]));
//   teseting ReadyAirEvent data
  end;
// refuel event ddt 20150625
if (PEvent^.event_type = AIR_REFUEL_EVENT) then    //  vers update / flag and CommandMask
begin
  PRefuelEvent:= PEvent^.DataPtr;
  //strUnitID:= Format('%3.3d',[PRefuelEvent^.fuelingUnit]);
  //PSelectedUnit:= PHCGroup(PEvent^.ourGroup)^.Units;
  //while (PSelectedUnit <> nil) do
  //begin
  //  if (strUnitID = Format('%3.3d',[PSelectedUnit^.id])) then
  //  break;
  //  PSelectedUnit:= PSelectedUnit^.next;
  //end;
  intTotFuel:= (PEventUnit^.Air^.FuelToOffload) * strToInt(Format('%2.2d',[PRefuelEvent^.num_tankers]));
  GEHEmemDetail.Lines.Add(Format('Fueling Unit: %3.3d', [PRefuelEvent^.fuelingUnit]));
  GEHEmemDetail.Lines.Add(Format('Tanker Type: %3.3d',[PRefuelEvent^.which_plane_type]));
  GEHEmemDetail.Lines.Add(Format('Number of Tankers: %2.2d', [PRefuelEvent^.num_tankers]));
  GEHEmemDetail.Lines.Add(Format('Fueling: %3.3d', [PRefuelEvent^.now_refueling]));   // 0=No, 1= yes
  GEHEmemDetail.Lines.Add(Format('Original Refuel Range: %4.4d', [PRefuelEvent^.fuel_range]));
  GEHEmemDetail.Lines.Add(Format('Remaining Refuel Range %4.4d',[intTotFuel]));
  GEHEmemDetail.Lines.Add('. . . . . . . .');
  // now do each unit refuel info
  N := 0;
  repeat
  begin
  GEHEmemDetail.Lines.Add(Format('# %d: unit %3.3d', [N, PRefuelEvent^.planestofuel[N]]));
  If PRefuelEvent^.planestofuel[N] = 255 then
    GEHEmemDetail.Lines.Add('Refuel cycle finished')
    else
    if
      PRefuelEvent^.planestofuel[N] = 205 then
      GEHEmemDetail.Lines.Add('Awaiting start of refuel?')
      else
      GEHEmemDetail.Lines.Add(Format('Per Plane Fuel: %4.4d', [PRefuelEvent^.fuel_per_plane[N]]));
  N := N+1;
  end;
  until  PRefuelEvent^.planestofuel[N-1] > 200;
end;
//  intercept event
if (PEvent^.event_type = INTERCEPT_EVENT) then    //  flag and CommandMask
  begin
    PInterceptEvent:= PEvent^.DataPtr;
    GEHEmemDetail.Lines.Add(Format('Target: %2.2s%0.3d',
        [PInterceptEvent^.targetGroup, PInterceptEvent^.targetUnit]));
    GEHEmemDetail.Lines.Add(Format('Close to: %d',[PInterceptEvent^.Irange]));
//    GEHEmemDetail.Lines.Add(Format('action: %d',[PInterceptEvent^.action]));
//    GEHEmemDetail.Lines.Add(Format('flag: %d, data: %d',[PInterceptEvent^.flag, PInterceptEvent^.data]));
//    GEHEmemDetail.Lines.Add(Format('dummies: %d, %d',[PInterceptEvent^.Dummy1, PInterceptEvent^.Dummy2]));
  end;
// group formation event       250622
if (PEvent^.event_type = FORMATION_EVENT) then
  begin
  pFormationEvent:= PEvent^.DataPtr;
  GEHEmemDetail.Lines.Add(Format('Rings: %d',[pFormationEvent^.rings]));
    GEHEmemDetail.Lines.Add('centre outwards = 1,2,4,8,16,32 occupy multi rings add values');
//      GTSpnEdRng.Value:= pFormationEvent^.rings;
  GEHEmemDetail.Lines.Add(Format('Sectors: %d',[pFormationEvent^.sectors]));
    GEHEmemDetail.Lines.Add('from 1am clockwise = 1,2,4,8,16,32,64,128 add for multi sectors');
//      GTSpnEdSctr.Value:= pFormationEvent^.sectors;
  GEHEmemDetail.Lines.Add(Format('offset x/x: %d/ %d',
    [pFormationEvent^.Xoffset, pFormationEvent^.Yoffset]));
  GEHEmemDetail.Lines.Add(Format('oldspeed: %d',[pFormationEvent^.oldSpeed]));

  end;
// air patrol event
if (PEvent^.event_type = AIR_PATROL_EVENT) then     // flag and CommandMask
  begin
    PAirPatrolEvent:= PEvent^.DataPtr;
    intEType:= PAirPatrolEvent^.PatrolType;
    case intEType of
    1: strEType:= 'ASUW';
    2: strEType:= 'ASW';
    3: strEType:= 'AAW';
    4: strEType:= 'AEW/radar/ESM';
    5: strEType:= 'EW';
    else strEType:= 'unknown';
    end;
    PThisAC:= HCAnnexHelper.HCGetAnnexB1(PAirPatrolEvent^.PlaneID);   //  240803 replace annexID by class name
    GEHEmemDetail.Lines.Add(Format('%s, %s patrol',[PThisAC^.ClassName, strEType]));
    GEHEmemDetail.Lines.Add(Format('Loadout IDX#: %d',[PAirPatrolEvent^.LoadoutIDX]));
    case PAirPatrolEvent^.Status of           //  250710
    0: strAirPatStat:= 'Readying';
    3: strAirPatStat:= 'Taking Off';
    4: strAirPatStat:= 'In Air RTB';
    5: strAirPatStat:= 'Patrolling';
    else strAirPatStat:= IntToStr(PAirPatrolEvent^.Status);
    end;
    GEHEmemDetail.Lines.Add(Format('Number Patrolling: %2.2d, Status: %s',
      [PAirPatrolEvent^.Numer, strAirPatStat]));
    GEHEmemDetail.Lines.Add(Format('Rings: %d',[PAirPatrolEvent^.Rings]));
      GTSpnEdRng.Value:= PAirPatrolEvent^.Rings;
      GEHEmemDetail.Lines.Add('centre outwards = 1,2,4,8,16,32 occupy multi rings add values');   //  20230409
    GEHEmemDetail.Lines.Add(Format('Sectors: %d',[PAirPatrolEvent^.Sectors]));
      GTSpnEdSctr.Value:= PAirPatrolEvent^.Sectors;
      GEHEmemDetail.Lines.Add('from 1am clockwise = 1,2,4,8,16,32,64,128 add for multi sectors');  //  20230409
  end;
// AIR ATTACK EVENT
if (PEvent^.event_type = AIR_ATTACK_EVENT) then  // flag and CommandMask
  begin
    PAirAttackEvent:= PEvent^.DataPtr;
    GEHEmemDetail.Lines.Add(Format('Target: %3.3s',[PAirAttackEvent^.targetGroup]));
    GEHEmemDetail.Lines.Add(Format('eventType:%3.3d',[PAirAttackEvent^.eventType]));
    GEHEmemDetail.Lines.Add(Format('Range of Weapon: %4d',[PAirAttackEvent^.attackRange]));              //  20230409
//    GEHEmemDetail.Lines.Add(Format('Flag %2.2d, data:%2.2d, dipping: %1.1d',[PAirAttackEvent^.flag, PAirAttackEvent^.data, PAirAttackEvent^.dipping]));
  end;
// Launch Land Event
if (PEvent^.event_type = LAUNCHLAND_EVENT) then  // flag and CommandMask
  begin
    PLaunchLandEvent:= PEvent^.DataPtr;
    lPThisPlane:= PLaunchLandEvent^.planes;
    intEType:= PLaunchLandEvent^.launch;
    case intEType of
    0: strEType:= 'landing';
    1: strEtype:= 'launching';
    end;
    GEHEmemDetail.Lines.Add(Format('base: %2.2s%0.3d',[PLaunchLandEvent^.baseGroup,
    PLaunchLandEvent^.baseUnit]));   // +target 250729
    GEHEmemDetail.Lines.Add('aircraft is ' + strEType);    //  landing\launching
    if lPThisPlane^.LoadID <> INVALID_ID
    then
    begin
      PThisLoadout:= HCAnnexHelper.HCGetAnnexY1(lPThisPlane^.LoadID);
      case HCMissionMap[PThisLoadout^.MissionID] of
          Ord(THCMissionType.MISSION_TANKER): strEType:= 'Tanker';
          Ord(THCMissionType.MISSION_FERRY):  strEType:= 'Ferry';
          Ord(THCMissionType.MISSION_CARGO): strEType:= 'Cargo';
          Ord(THCMissionType.MISSION_ASSAULT):  strEType:= 'Assault';
          Ord(THCMissionType.MISSION_PATROL):  strEType:= 'Patrol';
          Ord(THCMissionType.MISSION_NUCLEAR):  strEType:= 'Nuclear';
          Ord(THCMissionType.MISSION_STANDOFF):  strEType:= 'Standoff';
          Ord(THCMissionType.MISSION_SEAD):  strEType:= 'Sead';
          Ord(THCMissionType.MISSION_GUIDED):  strEType:= 'Guided';
          Ord(THCMissionType.MISSION_UNGUIDED):  strEType:= 'Unguided';
          Ord(THCMissionType.MISSION_ANTIRUN):  strEType:= 'Anti Runway';
          Ord(THCMissionType.MISSION_ATA):  strEType:= 'Intercept';
          Ord(THCMissionType.MISSION_ASW):  strEType:= 'ASW';
          Ord(THCMissionType.MISSION_AARMR):  strEType:= 'Anti Armour';
          Ord(THCMissionType.MISSION_DECOY):  strEType:= 'Decoy';
      end;
    end
    else strEType:= 'Unknown';
    //
    GEHEmemDetail.Lines.Add(Format('%d %s, Mission: %s',
      [lPThisPlane^.PlatNow, lPThisPlane^.name, strEType]));

  end;

//  Launch Air Event
If (PEvent^.event_type = LAUNCH_EVENT) then     // flag and CommandMask
  begin
    PLaunchAirEvent:= PEvent^.DataPtr;
    GEHEmemDetail.Lines.Add(Format('number to launch: %0.3d, unitID: %3.3d, Target: %3.3s ',
      [PLaunchAirEvent^.numberAircraft, PLaunchAirEvent^.UnitID, PLaunchAirEvent^.targetGroup]));
  end;
// Enemy AI event
if (PEvent^.event_type = ENEMY_AI_EVENT) then
  begin
    PEnemyAIEvent:= PEvent^.DataPtr;
    GEHEmemDetail.Lines.Add(Format('Target Group: %3.3s',[PEnemyAIEvent^.targetGroup]));
//    GEHEmemDetail.Lines.Add(Format('Effect subtype/ range/ data: %2.2d/ %4.4d/ %3.3d',[PEnemyAIEvent^.etype, PEnemyAIEvent^.range, PEnemyAIEvent^.data]));   //  20230409
  end;
//  Speed Altitude change
if  (PEvent^.event_type = ALT_SPEED_EVENT) then
  begin
    PSpeedAltEvent:= PEvent^.DataPtr;
    if PSpeedAltEvent^.newAltBand = -1
      then strAltBand := 'No Change'
      else strAltBand:=  HCAltBand2String(PSpeedAltEvent^.newAltBand);
    if PSpeedAltEvent^.newSpeed = -1
      then strNewSpeed:= 'No Change'
      else strNewSpeed := Format('%0.4d',[PSpeedAltEvent^.newSpeed]);
    if PSpeedAltEvent^.newThrottle = -1
      then  strNewThrottle:= 'No Change'
      else  strNewThrottle:= Format('%0.4d',[PSpeedAltEvent^.newThrottle]) ;
    GEHEmemDetail.Lines.Add(Format('New: speed: %s, AltBand: %s, Throttle %s',
      [strNewSpeed, strAltBand, strNewThrottle]));    //  20230409

  end;
//  Alter Course
if  (PEvent^.event_type = ALT_COURSE_EVENT) then
  begin
    PAltCourseEvent:= PEvent^.DataPtr;
    GEHEmemDetail.Lines.Add(Format('course - new: %3.3d, old: %3.3d',[PAltCourseEvent^.newcourse, PAltCourseEvent^.oldcourse]));
  end;

//  NukesFree Event
if (PEvent^.event_type = NUKE_FREE_EVENT) then
  begin
    PNukeFreeEvent:= PEvent^.DataPtr;

    GEHEmemDetail.Lines.Add('Nukes Free for side ' +
        AnnexData^.PBSStrings^.SideName[PNukeFreeEvent^.Side]);
  end;
// Attack Event
if (PEvent^.event_type = ATTACK_EVENT) then
  begin
    PAttackEvent:= PEvent^.DataPtr;
    GEHEmemDetail.Lines.Add(Format('Target: %3.3s:%0.3d',[PAttackEvent^.TargetGroup, PAttackEvent^.TargetID]));
    GEHEmemDetail.Lines.Add(Format('Attacking unit: %2.2s:%0.3d, (Mnt:ID/Anx %d/%d) %d Rounds, %d secs delay to fire',
       [PHCGroup(PEvent^.ourGroup)^.id, PAttackEvent^.UnitID, PAttackEvent^.ourMount, PAttackEvent^.mountIndex,
         PAttackEvent^.numberRounds, PAttackEvent^.deltaTime]));     //  240908 interrogate mount info
  end;     //  should put in weapon type  240428
if (PEvent^.event_type = RELOAD_EVENT) then
  begin
  pReloadEvent:= PEvent^.DataPtr;
  if pReloadEvent <> nil then        //  240803 seems to be empty data
  GEHEmemDetail.Lines.Add(Format('%d reloaded mnt %d',
    [pReloadEvent^.numberRounds, pReloadEvent^.ourMountAnnex]));
  end;
//  do RepairEvent
end      //  pevent data <> nil
else GEHEmemDetail.Lines.Add('Oops, no event data');
end;

//procedure TfrmMain.SGNewUUIDRadBtnClick(Sender: TObject);
//begin                                     //  240401  trying to fix duplicate uuid after unit split
//   if SGlbxUnits.ItemIndex <> -1
//     then PSelectedUnit^.UUID := GenerateUUID;
//end;


procedure TfrmMain.SGSizeSpnEdChange(Sender: TObject);
var
  PSGThisUnit: PHCUnit;
  PSGThisMnt: PHCMntRec;
  IntSGSubSpeed: Integer;
  PSGAnnexA1: PHCAnnexA1_disk;
  PSGAnnexA2: PHCAnnexA2_disk;

begin
if PHCSGThis <> nil then
  begin            //  should check that a group is selected       240403 redo whole procedure
  //  first do Group settings
  PHCSGThis^.VCS:= 10;
  PHCSGThis^.RCS:= 1;
  PHCSGThis^.maxspeed:= 45;
  // now do unit's  stuff all units
  PSGThisUnit:= PHCSGThis^.Units;
  while PSGThisUnit <> nil do
  begin
 // set VCS RCS and etc
  PSGThisUnit^.VCS := 1;
  PSGThisUnit^.RCS := 1;
  PSGThisUnit^.BaseSourceLevel:= 10;
  // now annex stuff
  if PSGThisUnit^.utype = MT_SHIP
    then begin
         if PSGThisUnit^.maxspeed < 55 then PSGThisUnit^.maxspeed:= 55; //  240405 moved from general
         PSGAnnexA1 := HCAnnexHelper.HCGetAnnexA1(PSGThisUnit^.Annex);
         PSGAnnexA1^.RCS:= 1;
         PSGAnnexA1^.BaseSourceLevel:= 1;
         PSGAnnexA1^.DetectSize:= 1;
         PSGAnnexA1^.MaxSpeed:= 55;
         PSGAnnexA1^.SearchHeight:= ScopeAlt;    // 240319 assume setting mast height for a drone
         end
    else
  if PSGThisUnit^.utype = MT_SUB
    then begin
         PSGThisUnit^.maxspeed:= 50;
         PSGThisUnit^.AltBand:= Snorkel;
         PSGThisMnt:= PSGThisUnit^.Mnts;
         while  (PSGThisMnt <> nil) do     //  240405
           begin
           PSGThisUnit^.maxspeed:= (PSGThisUnit^.maxspeed - 5);      //  240405 reduce maxKts for larger payloads
           PSGThisMnt^.MntId:= 60722;  // 240404 wild stab in dark    19011
           PSGThisMnt^.WepId:= 19011;
           PSGThisMnt^.Ammo_c[0]:= 1;
           PSGThisMnt:= PSGThisMnt^.next;
           end;
         if PSGThisUnit^.maxspeed < PHCSGThis^.maxspeed
           then PHCSGThis^.maxspeed:= PSGThisUnit^.maxspeed;
         PSGAnnexA2 := HCAnnexHelper.HCGetAnnexA2(PSGThisUnit^.Annex);
         PSGAnnexA2^.RCS:= 1;
         PSGAnnexA2^.BaseSourceLevel:= 1;
         PSGAnnexA2^.DetectSize:= 1;
         PSGAnnexA2^.CreepSpeed:= 15;
         PSGAnnexA2^.MaxSurfSpeed:= PSGThisUnit^.maxspeed;
         PSGAnnexA2^.MaxSubSpeed:= PSGThisUnit^.maxspeed;
         PSGAnnexA2^.MaxDepth:= Snorkel;
         end;
      PSGThisUnit:= PSGThisUnit^.next;
  end;
  end;  //  end check that group existed
end;

procedure TfrmMain.SGTButKingClick(Sender: TObject);
var
   PTheMultiAnnex: PHCAnnexZ2_disk;
   PTheMntAnnex: PHCAnnexZ1_disk;
   PTheAsrocAnnex: PHCAnnexE1_disk;
   PTheWepAnnex: PHCAnnexF1_disk;
   PShipMnt: PHCMntRec;
   PShipMag: PHCMagRec;
//   PShipWepID: ID;
   intC: integer;

begin
   PShipMnt:= PSelectedUnit^.Mnts;
   Repeat       // 1/ look for fwd p&s torp tubes from f100
   begin
        if (PShipMnt^.MntId = 59844) or (PShipMnt^.MntId = 59845) then
          begin  
            PShipMnt^.WepId := 22569;    //  stallion asroc   wep 26624
            PTheAsrocAnnex:= HCAnnexHelper.HCGetAnnexE1(22569);
            PTheAsrocAnnex^.ClassName:= 'KingFisherShell';
            PTheAsrocAnnex^.RCS:= 50;
            PTheAsrocAnnex^.Speed:= 2100;
            //PTheAsrocAnnex^.Dp:= 26652;   mod asroc seems not to get played
            PTheWepAnnex:= HCAnnexHelper.HCGetAnnexF1(26624);
            PTheWepAnnex^.SubDp:= 2;
            PTheWepAnnex^.Ph:= 75;
            PTheWepAnnex^.ClassName:= 'KingfisherSmartDC';
          end;  // swapped a torp tube wep
          if PShipMnt^.MntId = 59485    //  127 mm turret
          then PShipMnt^.WepId:= 14390;       //  swapped for vulcano A
        PShipMnt:= PShipMnt^.next;
   end;
   until PShipMnt = nil;     //  end torp tubes swap to kingfisherASW  and vulcano

   //  fix magazine
   PShipMag:=  PSelectedUnit^.Mags;
   repeat
      begin
           if PShipMag^.WepId = 26628 then
           begin
           PShipMag^.WepId:= 22569;     //  stallion asroc
           PShipMag^.Count:= 150;
           PShipMag^.ReloadTime:= 5;
           end;
           if PShipMag^.WepId = 14425
           then PShipMag^.WepId:=14390;
           PShipMag:= PShipMag^.next;
      end;
   until PShipMag = nil;       // mags swapped torp & asroc

end;  // end proceedure KingClick

procedure TfrmMain.UNbtnResumeClick(Sender: TObject);
begin
  Byte(PDLLBusy^) := CFALSE;
end;


procedure TfrmMain.SGButtonRefreshClick(Sender: TObject);
var
  PTGroup: PHCGroup;
begin
  SGlbxGroups.Items.Clear;
  SGlbxUnits.Items.Clear;
  SGListBoxUnitInfo.Clear;
  SGlblUnitIDName.Caption:= '';           //  clear all boxs until a unit is selected  20230503
  SGlblClassName.Caption:= '';
  SGlblUnitShowing.Caption:= '';
  SGlblDetQual.Caption:= '';
  SGlblUnitKts.Caption:= '';
  PTGroup := PStartGroup;
  while (PTGroup <> nil) do
    begin
    SGlbxGroups.Items.Add(PTGroup^.id);           //  HCMT would be usful
    PTGroup := PTGroup^.toSouth;
    end;
end;

procedure TfrmMain.SGButtonResumeClick(Sender: TObject);
begin
  GTbtnResume.Enabled:=False;
  SGButtonResume.Enabled:=False;
  Byte(PDLLBusy^) := CFALSE; // ALE 20130816

end;

procedure TfrmMain.SGButtonSDClick(Sender: TObject);
begin
  if SGlbxUnits.SelCount <> 0
    then PSelectedUnit^.CurrDp:= PSelectedUnit^.OrigDp
    else ShowMessage('Select a Unit to SelfDestruct');
end;

procedure TfrmMain.SGlbxGroupsDblClick(Sender: TObject);
begin
  HCChangeActGroupPtr(PSelectedGroup);
end;



//procedure TfrmMain.SGesmSpnEdChange(Sender: TObject);
//begin
//  PSelectedUnit^.esm_d_val:= SGesmSpnEd.Value;
//  PSelectedUnit^.ir_d_val:= SGesmSpnEd.Value;
//end;

//procedure TfrmMain.SGlblSpnEdAIChanceClick(Sender: TObject);
//begin
//  if PHCSGThis <> Nil
//    then PHCSGThis^.AIChance:= SGSpnEdAIChance.Value;
//end;
//            remove feature 240301

//procedure TfrmMain.DUbtnUpdateClick(Sender: TObject);
//var
//  PThisDeadUnit: PHCDeadListItem;
//  strClass, strUname: String;
//  intCarrierKill, intShipKill, intSubKill,
//  intPlaneKill, intHeloKill,
//  intBaseKill,
//  intFixKill, intMobKill,
//  intArmKill, intUnarmKill,
//  intS: integer;
//  ThisA1_disk: PHCAnnexA1_disk;
//begin
//  DUmemDetail.Lines.Clear;
//  for intS := 0 to 2 do
//  begin
//  case intS of
//  0: DUmemDetail.Lines.Add('Blue Side');
//  1: DUmemDetail.Lines.Add('Red Side');
//  2: DUmemDetail.Lines.Add('Neutral Side');
//  end;
//  PThisDeadUnit:= PAllDeadUnitsPtr;
//  intCarrierKill:= 0;
//  intShipKill:= 0;
//  intSubKill:= 0;
//  intPlaneKill:= 0;
//  intHeloKill:=0;
//  intBaseKill:= 0;
//  intFixKill:= 0;
//  intMobKill:= 0;
//  intArmKill:= 0;
//  intUnarmKill:= 0;
//  while PThisDeadUnit <> nil do
//  begin
//  if PThisDeadUnit^.side = intS then
//    begin
//    case PThisDeadUnit^.typeindex of
//    0: begin
//        strClass:= 'Carrier';
//        intCarrierKill:= intCarrierKill + PThisDeadUnit^.count;
//       end;
//    1: begin
//        strClass:= 'Ship';
//        intShipKill:= intShipKill + PThisDeadUnit^.count;
//        end;
//    2: begin
//        strClass:= 'Submarine';
//        intSubKill:= intSubKill + PThisDeadUnit^.count;
//       end;
//    3: begin
//        strClass:= 'Plane';
//        intPlaneKill:= intPlaneKill + PThisDeadUnit^.count;
//       end;
//    4: begin
//        strClass:= 'Helicopter';
//        intHeloKill:= intHeloKill + PThisDeadUnit^.count;
//       end;
//    5: begin
//        strClass:= 'Base';
//        intBaseKill:= intBaseKill + PThisDeadUnit^.count;
//       end;
//    6: begin
//        strClass:= 'AD Fixed';
//        intFixKill:= intFixKill + PThisDeadUnit^.count;
//       end;
//    7: begin
//        strClass:= 'AD Mobile';
//        intMobKill:= intMobKill + PThisDeadUnit^.count;
//       end;
//    8: begin
//        strClass:= 'Armoured';
//        intArmKill:= intArmKill + PThisDeadUnit^.count;
//       end;
//    9: begin
//        strClass:= 'Unarmoured';
//        intUnarmKill:= intUnarmKill + PThisDeadUnit^.count;
//       end;
//    end;
//  // insert look for class details
//  case HCAnnexShift(PThisDeadUnit^.Annex) of
//  SHIP_FLAG:
//    begin
//    ThisA1_disk:= HCAnnexHelper.HCGetAnnexA1(PThisDeadUnit^.Annex);
//    strUname:= ThisA1_disk^.ClassName;
//    end;
//  PLANE_FLAG:
//    begin
//    strUname:= PHCAnnexB1_disk(HCAnnexHelper.HCGetAnnexB1(PThisDeadUnit^.Annex))^.ClassName;
//    end;
//  BASE_FLAG:
//    strUname:= PHCAnnexP1_disk(HCAnnexHelper.HCGetAnnexP1(PThisDeadUnit^.Annex))^.Name;
//  SUB_FLAG:
//    strUname:= PHCAnnexA2_disk(HCAnnexHelper.HCGetAnnexA2(PThisDeadUnit^.Annex))^.ClassName;
//  else strUname:= 'unknown';
//  end;
//  // end look for class detail
//  DUmemDetail.Lines.Add(format('%s (%s): %d destroyed',[strUname, strClass, PThisDeadUnit^.count]));
//  end;
//  PThisDeadUnit:= PThisDeadUnit^.next;
//  end;   // end loop deadunitptr
//  DUmemDetail.Lines.Add('Summary');
//if intCarrierKill > 0 then  DUmemDetail.Lines.Add('Carriers: ' + intTostr(intCarrierKill));
//if intShipKill > 0 then  DUmemDetail.Lines.Add('Ships: ' + intTostr(intShipKill));
//if intSubKill > 0 then  DUmemDetail.Lines.Add('Submarines: ' + intTostr(intSubKill));
//if intPlaneKill > 0 then  DUmemDetail.Lines.Add('Planes: ' + intTostr(intPlaneKill));
//if intHeloKill > 0 then  DUmemDetail.Lines.Add('Helicopters: ' + intTostr(intHeloKill));
//if intMobKill > 0 then  DUmemDetail.Lines.Add('AD mobile: ' + intTostr(intMobKill));
//if intFixKill > 0 then  DUmemDetail.Lines.Add('AD fixed: ' + intTostr(intFixKill));
//if intArmKill > 0 then  DUmemDetail.Lines.Add('Armoured: ' + intTostr(intArmKill));
//if intUnarmKill > 0 then  DUmemDetail.Lines.Add('Unarmoured: ' + intTostr(intUnarmKill));
//  end;  // finished all sides
//end;
          //  want to put this back?

function TfrmMain.DetQuality2String(const intQual: integer): String;
var
  lStrQual: String;

begin
  lStrQual:= ' has no location';
  if ((intQual and cHEIGHT) > 0) then lStrQual := ' has fix including AltBand'
   else if ((intQual and cEXACT) > 0) then lStrQual:= ' has exact fix'
     else if ((intQual and cAREA) > 0 ) then lStrQual := ' has approx location'
          else if ((intQual and cBEARING) > 0) then lStrQual:= ' has bearing only';
  if intQual >= cTYPE then lStrQual := lStrQual + ' - knows broad type';
   if intQual >= cSIZE then lStrQual := lStrQual + ', size';
   if intQual >= cCLASS then lStrQual := lStrQual + ', Class';
   if intQual >= cNAME then lStrQual:= lStrQual + ', Name';
   result := lStrQual;

end;

function TfrmMain.Showing2String(const intShow: integer): String;

begin
    case intShow of
    0: result:= ' has no detection';
    1: result:= ' knows side, not location';
    2: result:= ' has exact fix, not side';
    3: result:= ' has exact fix and side';
    4: result:= ' has approx position, not side';
    5: result:= ' has approx position and knows side';
    else result:= ' detection status = ' + IntToStr(intShow);
    end;
end;

procedure TfrmMain.SGlblUnitKtsFill();

begin
 if SGlbxUnits.ItemIndex <> -1
   then
   begin
  SGlblUnitShowing.Caption:= strSide + ' Unit: ' + strOSide + HCUnitDetection2String(PSelectedUnit);       //  240616
  SGlblDetQual.Caption:= strOSide + DetQuality2String(PSelectedUnit^.Data[0]);
  if (PSelectedUnit^.utype = MT_SHIP) or (PSelectedUnit^.utype = MT_SUB) then       //  240616
    SGlblUnitKts.Caption:= format('%d kts, Noise: %d',[PSelectedUnit^.kts, PSelectedUnit^.SourceLevel])
    else if (PSelectedUnit^.utype = MT_MISSILE) then
    SGlblUnitKts.Caption:= format('%d kts, Target Time %d secs',
      [PSelectedUnit^.kts, PSelectedUnit^.Missile^.tTime]);   //  240907
    end
    else
    if SGlbxGroups.ItemIndex <> -1
      then                           //  240907
      SGlblUnitShowing.Caption:= 'Opponent ' + Showing2String(PSelectedGroup^.showing);
end;

procedure TfrmMain.GTSpnEdSctrEditingDone(Sender: TObject);
begin
 if PEvent^.event_type = AIR_PATROL_EVENT
   then PAirPatrolEvent^.sectors:= GTSpnEdSctr.Value;
end;

procedure TfrmMain.SGbtnURepClick(Sender: TObject);
var
 lIntGrpRepVal: Integer;
 lPThisRepUnit: PHCUnit;
begin
  if (PSelectedGroup<>nil) and (PSelectedUnit<>nil)
  then
  begin
    if (HCMTIsVessel(PSelectedGroup^.gtype) = True)
       and (PSelectedUnit^.CurrDp > 0)
      then
      begin
           lPThisRepUnit:= PSelectedGroup^.Units;
           lIntGrpRepVal:= 0;
        while  lPThisRepUnit<>nil do
        begin
        // add filter so support ships have more repair value
        //  must be coastal
           lIntGrpRepVal:= lIntGrpRepVal + lPThisRepUnit^.OrigDp - lPThisRepUnit^.CurrDp;
           lPThisRepUnit:= lPThisRepUnit^.next;
        end;
        if PSelectedUnit^.CurrDp < lIntGrpRepVal
           then PSelectedUnit^.CurrDp:= 0
           else PSelectedUnit^.CurrDp:= PSelectedUnit^.CurrDp - lIntGrpRepVal;
      end
    else
    if (HCMTIsAir(PSelectedGroup^.gtype) = True)
    and (PSelectedUnit^.CurrDp>0)
    then
    begin
      lPThisRepUnit:= PSelectedGroup^.Units;
      lIntGrpRepVal:= 0;
      while lPThisRepUnit<>nil do
      begin
         lIntGrpRepVal:= lINtGrpRepVal + 10;
         //  change so that only 'cargo' weapons repair and 'use' weapon
         //  require very close
         lPThisRepUnit:= lPThisRepUnit^.next;
      end;
      if PSelectedUnit^.CurrDp < lIntGrpRepVal
           then PSelectedUnit^.CurrDp := 0
           else PSelectedUnit^.CurrDp:= PSelectedUnit^.CurrDp - lIntGrpRepVal

    end;
    //  time to do repairs

  end
  else
  ShowMessage('FGroup and FUnit must be selected');
end;

procedure TfrmMain.GTSpnEdRngEditingDone(Sender: TObject);
begin
if PEvent^.event_type = AIR_PATROL_EVENT
  then PAirPatrolEvent^.Rings:= GTSpnEdRng.Value;
  //else
  //if PEvent^.event_type = FORMATION_EVENT
  //  then PFormationEvent^.Rings:= GTSpnEdRng.Value;
end;

procedure TfrmMain.GTbtnRepairClick(Sender: TObject);
begin                                          //  250711 repair formation and aitPat mismatch groups
PEvent := PHarpEventHead;
while (PEvent <> nil) do
  begin
    if (PEvent^.unit_uuid <> '')
    then     //  there's an event unit
      begin
      PEventUnit:= HCuuid2UnitPtr(PEvent^.unit_uuid, pStartGroup,PEvent^.ourGroup);
      if PEventUnit <> nil
      then     //  there's a valid event unit
        begin
          if (PEvent^.ourGroup <> PEventUnit^.Group)
          then        //  there's a mismatch with event and unit groups
             PEvent^.ourGroup:= PEventUnit^.Group;
        end
      else   //  there is an invalid unit
        begin
          PEvent^.unit_uuid:= '';
        end;
      end;  //  there was an event unit-uuid   otherwise do nothing
  PEvent := PEvent^.next;
  end;
  ///

end;

procedure TfrmMain.GTbtnCheckClick(Sender: TObject);
var
  strEventType, strGroupID: String;
  lBoolClear: Boolean;

begin
    lBoolClear:= True;
    Byte(PDLLBusy^) := CTRUE;
    GTbtnResume.Enabled:=True;
    SGButtonResume.Enabled:= True;   //  240906
    PEvent := PHarpEventHead;
    GTHEEventList.Clear;
    GEHEmemDetail.Clear;
    while (PEvent <> nil) do
    begin
    if (PEvent^.unit_uuid <> '')           // there is an event unit
    then
    if HCuuid2UnitPtr(PEvent^.unit_uuid, pStartGroup,PEvent^.ourGroup) <> nil
    then            // unit is valid, check if wrong group/unit
      begin
      if (PEvent^.ourGroup <> PHCUnit(HCuuid2UnitPtr(PEvent^.unit_uuid, pStartGroup,PEvent^.ourGroup))^.Group)
      then
        begin
        lBoolClear:= False;
        strEventType := HCEventType2String(PEvent^.event_type);
        strGroupID := PHCGroup(PEvent^.ourGroup)^.id;
        GTHEEventList.Items.Add(Format('ExT:%8.5d %s %s uuid:%s',
           [PEvent^.Time, strGroupID,  strEventType, PEvent^.uuid ]));    //  ddt 20230410 trimmed
         end;    //  end mis matched event group and group unit's group
      end      //  unit was valid
    else       //  unit is invalid       250713
      begin
      lBoolClear:= False;
      strEventType := HCEventType2String(PEvent^.event_type);
      strGroupID := PHCGroup(PEvent^.ourGroup)^.id;
      GTHEEventList.Items.Add(Format('ExT:%8.5d %s %s invalid unit, uuid:%s',
         [PEvent^.Time, strGroupID,  strEventType, PEvent^.uuid ]));    //  ddt 20230410 trimmed
       end;
      PEvent := PEvent^.next;
    end;
    if lBoolClear = True
    then
    begin
    Byte(PDLLBusy^) := CFALSE;
    GTbtnResume.Enabled:= False;
    SGButtonResume.Enabled:= False;
    end
    else
    begin
    WindowState:= wsNormal;
    end;
end;

procedure TfrmMain.SGlbxUnitsSelectionChange();  //  240613 - (Sender: TObject; User: boolean);
var
  strSel, strDepthAlt, strShow, strWepName, strPlaneLoadout, strName,
    strMGPhase, strStatus, strUType, strMType: String;      // strSide, strOSide, moved to updateshowing
  PHCUThis: PHCUnit;
  PThisMnt: PHCMntRec;
  PThisMag: PHCMagRec;
  PmyPlanes: PHCPlaneRec;
  RLat, RLon: Double;
  intDepthAlt, intShow, intC: integer;
  isTanker, isSub, isAir: Boolean;    // , isShip, isBase     240319 not using now
  strID: uchar;
  ThisWepID:  ID;
  PThisLoadout: PHCAnnexY1_disk;
  PThisMount: PHCAnnexZ1_disk;
  PThisMulti: PHCAnnexZ2_disk;
  PHCAnnexA1: PHCAnnexA1_disk;      // surface ship
  PHCAnnexA2: PHCAnnexA2_disk;      // subs
  PHCAnnexB1: PHCAnnexB1_disk;      // aircraft
  PHCAnnexP1: PHCAnnexP1_disk;      // installations/bases
  PHCAnnexC1: PHCAnnexC1_disk;      // guns
  PHCAnnexD1: PHCAnnexD1_disk;      // missiles
  PHCAnnexE1: PHCAnnexE1_disk;      // asw dc mortar etc
  PHCAnnexF1: PHCAnnexF1_disk;      // torpedos
  PHCAnnexG1: PHCAnnexG1_disk;      // ATOG
  PHCAnnexG2: PHCAnnexG2_disk;      // ECM
  PHCAnnexM1: PHCAnnexM1_disk;      // Sonar

begin
  // ALE 20130317 User selected a unit, pull up unit info.
  isTanker:= False;  //until proven True
  strSel := SGlbxUnits.Items[SGlbxUnits.ItemIndex];    // need a check for selected unit/group? no longer valid
                                                       //  check group still valid
  if HCIsValidGroup(PSelectedGroup, PStartGroup) = CTRUE then
  begin
  PHCUThis := PSelectedGroup^.Units;
  while (PHCUThis <> nil) do
  begin
    if (Pos(Format('%2.2s%0.3d', [PSelectedGroup^.id, PHCUThis^.id]), strSel) = 1) then
      break;
    PHCUThis := PHCUThis^.next;
  end;                            // ddt add unit information
  if (PHCUThis <> nil) then      // test for valid unit
  begin
    isSub:= False;
    isAir:= False;
//    isShip:= False;
//    isBase:= False;
    PSelectedUnit := PHCUThis;         //  set PSelectedUnit to unit selected in list
    SGListBoxUnitInfo.Items.Clear;
    SGlblUnitShowing.Caption:= '';
    SGlblDetQual.Caption:= '';
    SGlblUnitKts.Caption:= '';
    strUType:= '';
    // now add class name using annex
      case (HCAnnexHelper.HCAnnexShift(PSelectedUnit^.Annex)) of
          SHIP_FLAG:
          begin
//            isShip:= True;
            PHCAnnexA1 := HCAnnexHelper.HCGetAnnexA1(PSelectedUnit^.Annex);
            SGlblClassName.Caption := 'Ship Class Name: ' + PHCAnnexA1^.ClassName;   //  240309 stopped testing detection size
            strUType:= AnnexData^.PBSStrings^.ShipType[PHCAnnexA1^.Subtype];
          end;
          SUB_FLAG:
          begin
            PHCAnnexA2 := HCAnnexHelper.HCGetAnnexA2(PSelectedUnit^.Annex);
            SGlblClassName.Caption := 'Submarine Class Name: ' + PHCAnnexA2^.ClassName;
            strUType:= AnnexData^.PBSStrings^.ShipType[PHCAnnexA2^.Subtype];
            isSub:= True;
            intDepthAlt:= PSelectedUnit^.AltBand;
            case intDepthAlt of
  0: strDepthAlt := 'TooDeep';
  1: strDepthAlt := 'Vdeep';
  2: strDepthAlt := 'Deep';
  3: strDepthAlt := 'Intermed';
  4: strDepthAlt := 'Shallow';
  5: strDepthAlt := 'Snorkel';
  6: strDepthAlt := 'SeaLevel';
  7: strDepthAlt := 'ScopeAlt';
          end;
          end;
          PLANE_FLAG:
          begin
            isAir:= True;
            PHCAnnexB1 := HCAnnexHelper.HCGetAnnexB1(PSelectedUnit^.Annex);
            SGlblClassName.Caption := 'Aircraft Class Name: ' + PHCAnnexB1^.ClassName;  //  240607
            strUType:= AnnexData^.PBSStrings^.ACType[PHCAnnexB1^.Subtype];
          end;
          BASE_FLAG:
          begin
//            isBase:= True;            //  240319
            PHCAnnexP1 := HCAnnexHelper.HCGetAnnexP1(PSelectedUnit^.Annex);
            SGlblClassName.Caption := 'Base Name: ' + PHCAnnexP1^.Name;
          end;
          MISSILE_FLAG:
          begin
            isAir:= True;
            PHCAnnexD1:= HCAnnexHelper.HCGetAnnexD1(PSelectedUnit^.Annex);
            SGlblClassName.Caption := 'Missile Class: ' + PHCAnnexD1^.ClassName;  //  240606   back edit
          end;
          TORP_FLAG:
          begin
            PHCAnnexF1:= HCAnnexHelper.HCGetAnnexF1(PSelectedUnit^.Annex);
            SGlblClassName.Caption := 'Torpedo Class: ' + PHCAnnexF1^.ClassName;   //  240607 back edit
          end;
          ASW_FLAG:
          begin
            PHCAnnexE1:= HCAnnexHelper.HCGetAnnexE1(PSelectedUnit^.Annex);
            SGlblClassName.Caption:= 'Class:  ' + PHCAnnexE1^.ClassName;           //  240607 added ASW
          end;
          else
            SGlblClassName.Caption := 'No Class Name';  // (HCAnnexHelper.HCAnnexShift(PSelectedUnit^.Annex))
        end;
    // finished getting class name from annex
    SGlblUnitIDName.Caption := Format('%2.2s%0.3d %s %s',
      [PSelectedGroup^.id, PSelectedUnit^.id, strUType, PSelectedUnit^.name]);  //  240412 add strUType
 //   SGlblUnitKtsFill();                                                 //  240613 for updating info
       //  add which side and if seen
  if (PHCGroup(PSelectedUnit^.Group)^.side = 2) then
  SGListBoxUnitInfo.Items.Add('side = neutral')
  else
  begin
  if (PHCGroup(PSelectedUnit^.Group)^.side = intESide)    // intESide = enemy side   should move to group selection changed
     then begin strSide:= 'Enemy';
          strOSide:= 'Player';
          end            // strOSide is str Other Side   relative to the event group
     else begin strSide:= 'Player';
          strOSide:= 'Enemy';
          end;
    begin
    //intShow:=  PSelectedUnit^.showing[0];           //  0 index only used, value is for unit's opponent
    //case intShow of
    //0: strShow:= ' has no detection';
    //1: strShow:= ' knows side, no position';
    //2: strShow:= ' has exact fix';
    //3: strShow:= ' has exact fix and side';
    //4: strShow:= ' has approx position only';
    //5: strShow:= ' has approx position and knows side';
    //else strShow:= 'detection status = ' + IntToStr(intShow);    //  20230409  needs update
    //end;
    strShow:= HCUnitDetection2String(PSelectedUnit);     //  20240905 use hcutils function
    SGlblUnitShowing.Caption:= (strSide + ' Unit: ' + strOSide + strShow);
    strShow:= DetQuality2String(PSelectedUnit^.Data[0]);       //  240906
    if strShow <> '' then
       SGlblDetQual.Caption:= strOSide + strShow;
    end;
  end;        // now move onto general unit info
    SGListBoxUnitInfo.Items.Add('UUID: ' + PSelectedUnit^.UUID);  //250405
    SGListBoxUnitInfo.Items.Add(format('course: %3.3d, kts: %d, MaxKts: %d',
      [PSelectedUnit^.course, PSelectedUnit^.kts, PSelectedUnit^.maxspeed]));
    SGListBoxUnitInfo.Items.Add(format('best weapon range: AAW- %0.1n, ASUW- %0.1n, ASW- %0.1n',
      [PSelectedUnit^.BestAAWRange / 10, PSelectedUnit^.BestASuWRange / 10 ,
       PSelectedUnit^.BestASWRange / 10]));                                //  240617 >>
  if (PSelectedUnit^.utype = MT_SHIP) or (PSelectedUnit^.utype = MT_SUB) then
    SGListBoxUnitInfo.Items.Add(format('SelfNoise: %d; TotHydroNoise: %d; BaseHydroNoise(at Creep): %d',
      [PSelectedUnit^.SelfNoise, PSelectedUnit^.SourceLevel, PSelectedUnit^.BaseSourceLevel]));     //  240610
    SGListBoxUnitInfo.Items.Add(Format('RCS: %d, VCS: %d',
        [PSelectedUnit^.RCS, PSelectedUnit^.VCS]));
    SGListBoxUnitInfo.Items.Add(format('Enemy Detection - SonarPD: %d HydPD: %d SurfRadarPD: %d, AirRadarPD %d',
       [PSelectedUnit^.son_pd, PSelectedUnit^.hyd_pd, PSelectedUnit^.srad_pd, PSelectedUnit^.arad_pd]));
  if isSub then
    SGListBoxUnitInfo.Items.Add('Depth: ' + strDepthAlt + ' - MaxDepth: '
      + HCAltBand2String(PHCAnnexA2^.MaxDepth));         //  240607
  if isAir = False
    then SGListBoxUnitInfo.Items.Add(format('Damage Points Now/Orig: %d/ %d',
            [PSelectedUnit^.CurrDp, PSelectedUnit^.OrigDp]));    //  240908 don't want if ac or missile

// test for air or missile units        PROB need to do loadouts and mounts here and delete unit^.air from general mounts and mags
  if (PSelectedUnit^.Air <> nil) then
  begin
    strDepthAlt:= HCAltBand2String(PSelectedUnit^.AltBand);
    SGListBoxUnitInfo.Items.Insert(0, 'Aircraft Unit');
    SGListBoxUnitInfo.Items.Insert(1,'Altitude: ' + strDepthAlt + '  - MaxAlt: '
        + HCAltBand2String(PHCAnnexB1^.MaxAlt));  //240607
    SGListBoxUnitInfo.Items.Add(format('Aircraft number orig/now: %2.2d/ %2.2d',
      [PSelectedUnit^.Air^.PlatStart, PSelectedUnit^.Air^.PlatNow]));
    SGListBoxUnitInfo.Items.Add(format('base: %2.2s-%3.3d, %s',
      [PHCGroup(PHCUnit(PSelectedUnit^.Air^.HomeUnit)^.Group)^.id, PHCUnit(PSelectedUnit^.Air^.HomeUnit)^.id, PHCUnit(PSelectedUnit^.Air^.HomeUnit)^.name]));
     if ((PHCAnnexB1^.UnitFlags AND UF_B1_NOE) > 0) then
       SGListBoxUnitInfo.Items.Add('NapeOfEarth - YES')  else
       SGListBoxUnitInfo.Items.Add('NapOfEarth - NO');
    if ((PHCAnnexB1^.UnitFlags AND UF_B1_refuelcapable) > 0) then
       SGListBoxUnitInfo.Items.Add('InFlightRefuel - YES')  else
       SGListBoxUnitInfo.Items.Add('InFlightRefuel - NO');
  //have annex, get more info
    SGListBoxUnitInfo.Items.Add('Det.Size: ' + IntToStr(PHCAnnexB1^.Size) + '; RCS: '
      + IntToStr(PHCAnnexB1^.RCS));   //  240607
    SGListBoxUnitInfo.Items.Add('Max alt band: ' + HCAltBand2String(PHCAnnexB1^.MaxAlt));  //  240607
    if PSelectedUnit^.Air^.FuelToOffload > 0 then
    SGListBoxUnitInfo.Items.Add(format('tanker fuel remaining: %d per tanker',[PSelectedUnit^.Air^.FuelToOffload]));
 //  now add loadout info   then add mount info separate
  If PSelectedUnit^.Mnts <> nil      //  this is in airunit so is loadout
    then
    begin
    PThisMnt:= PselectedUnit^.Mnts;
    if (HCAnnexShift(PThisMnt^.MntId) = LOADOUT_FLAG)  then
    SGListBoxUnitInfo.Items.Add('WEAPONS:');   //  240617  is this correct place?
  while (PThisMnt <> nil) do
    begin
    if (HCAnnexShift(PThisMnt^.MntId) = LOADOUT_FLAG)  then   //find mission type
    begin
    PThisLoadout:= HCAnnexHelper.HCGetAnnexY1(PThisMnt^.MntId);            //  240616 deletd loadout from print
    strPlaneLoadout:= Format('Air Unit DID %d, Mission #%d: ',
      [PSelectedUnit^.Annex, PThisLoadout^.MissionID]);   // 240616 added loadout #
    case HCMissionMap[PThisLoadout^.MissionID] of
        Ord(THCMissionType.MISSION_TANKER): SGListBoxUnitInfo.Items.Strings[0]:= strPlaneLoadout + 'Tanker';
        Ord(THCMissionType.MISSION_FERRY): SGListBoxUnitInfo.Items.Strings[0]:= strPlaneLoadout + 'Ferry';
        Ord(THCMissionType.MISSION_ASSAULT): SGListBoxUnitInfo.Items.Strings[0]:= strPlaneLoadout + 'Assault';
        Ord(THCMissionType.MISSION_PATROL): SGListBoxUnitInfo.Items.Strings[0]:= strPlaneLoadout + 'Patrol';
        Ord(THCMissionType.MISSION_NUCLEAR): SGListBoxUnitInfo.Items.Strings[0]:= strPlaneLoadout + 'Nuclear';
        Ord(THCMissionType.MISSION_STANDOFF): SGListBoxUnitInfo.Items.Strings[0]:= strPlaneLoadout + 'Standoff';
        Ord(THCMissionType.MISSION_SEAD): SGListBoxUnitInfo.Items.Strings[0]:= strPlaneLoadout + 'Sead';
        Ord(THCMissionType.MISSION_GUIDED): SGListBoxUnitInfo.Items.Strings[0]:= strPlaneLoadout + 'Guided';
        Ord(THCMissionType.MISSION_UNGUIDED): SGListBoxUnitInfo.Items.Strings[0]:= strPlaneLoadout + 'Unguided';
        Ord(THCMissionType.MISSION_ANTIRUN): SGListBoxUnitInfo.Items.Strings[0]:= strPlaneLoadout + 'Anti Runway';
        Ord(THCMissionType.MISSION_ATA): SGListBoxUnitInfo.Items.Strings[0]:= strPlaneLoadout + 'Intercept';
        Ord(THCMissionType.MISSION_ASW): SGListBoxUnitInfo.Items.Strings[0]:= strPlaneLoadout + 'ASW';
        Ord(THCMissionType.MISSION_AARMR): SGListBoxUnitInfo.Items.Strings[0]:= strPlaneLoadout + 'Anti Armour';
        Ord(THCMissionType.MISSION_DECOY): SGListBoxUnitInfo.Items.Strings[0]:= strPlaneLoadout + 'Decoy';
         Ord(THCMissionType.MISSION_CARGO): SGListBoxUnitInfo.Items.Strings[0]:= strPlaneLoadout + 'Cargo';
      end;    //end case of mission type   COULD add loadout weapon count data to print as per landed planes
     end;   // end getting ID mission type
 //  LOADOUT WEAPONS
    if PThisMnt^.WepId <> Invalid_ID      //    PThisMnt: PHCMntRec
    then
    begin
    ThisWepID:= PThisMnt^.WepId;
    begin
    case (HCAnnexShift(ThisWepId)) of
    GUN_FLAG:
      begin
      PHCAnnexC1:= HCAnnexHelper.HCGetAnnexC1(ThisWepId);
      strWepName:=  Format('%s; ARange: %d NM, SRange: %d NM',
              [PHCAnnexC1^.ClassName, (PHCAnnexC1^.AirRange div 10), (PHCAnnexC1^.SurfRange div 10)]);
      end;
    MISSILE_FLAG:
      begin
      PHCAnnexD1:= HCAnnexHelper.HCGetAnnexD1(ThisWepId);
      strWepName:= Format('%s; ARange: %d NM, SRange: %d NM, MinRange: %d NM',
      [PHCAnnexD1^.ClassName, (PHCAnnexD1^.AirRange div 10), (PHCAnnexD1^.SurfRange div 10),
      (PHCAnnexD1^.MinRange div 10)]);
      end;
    ASW_FLAG:
    begin
    PHCAnnexE1:= HCAnnexHelper.HCGetAnnexE1(ThisWepId);
    strWepName:= Format('%s: MaxRange: %d MinRange: %d',
              [PHCAnnexE1^.ClassName, (PHCAnnexE1^.MaxRange div 10), (PHCAnnexE1^.MinRange div 10)]);
    end;
    TORP_FLAG:
    begin
    PHCAnnexF1:= HCAnnexHelper.HCGetAnnexF1(ThisWepId);
    strWepName:= Format('%s; MaxRange: %d/%d MaxDepth %s',
            [PHCAnnexF1^.ClassName, (PHCAnnexF1^.MaxRange[0] div 10), (PHCAnnexF1^.MaxRange[1] div 10),
            HCAltBand2String(PHCAnnexF1^.MaxDepth)]);
    end;
    ATOG_FLAG:
    begin
    PHCAnnexG1:= HCAnnexHelper.HCGetAnnexG1(ThisWepId);
    strWepName:=  PHCAnnexG1^.ClassName + 'ATOG';  // 250730 'weptype ' + intToStr(PHCAnnexG1^.WType) + '/' +
    end;
    ECM_FLAG:
    begin
    PHCAnnexG2:= HCAnnexHelper.HCGetAnnexG2(ThisWepId);
    strWepName:= PHCAnnexG2^.ClassName;
    end;
    SONAR_FLAG:
      begin
      PHCAnnexM1:= HCAnnexHelper.HCGetAnnexM1(ThisWepId);
      strWepName:= PHCAnnexM1^.ClassName;
      end;
    else
      strWepName:= format('misc - WepID %d',[ThisWepId]);
    end;  //  end look at  mount
      SGListBoxUnitInfo.Items.Add('  ' + IntToStr(PThisMnt^.Ammo_c[0]) + ' - ' + strWepName); // 240307
      strWepName:= '';
    end;   // end this weapon of loadout
    end; // valid weaponID
    PThisMnt:= PThisMnt^.next;
    end;  // end this loadout    - should only be one
  end; // look at air unit loadouts
  // listing altband - speed table            240617 moved from before wep list  WHY did I do it like this??
  SGListBoxUnitInfo.Items.Add('AltBand/Throttle/Speed Table:');
  SGListBoxUnitInfo.Items.Add(format('speed level 0=%d',[PHCAnnexB1^.SpeedLevels[0]]));
      for intC := 1 to  (PLANE_SPEED_LEVELS - 1) do
      SGListBoxUnitInfo.Items.Append(format('speed level %d=%d',[intC, PHCAnnexB1^.SpeedLevels[intC]]));
  //  finished altband  - speed table
  end             // end look at air units
  else
//  MISSILE UNITS   !!! INcludes torpedo and asroc but separate annex
  if (PSelectedUnit^.Missile <> nil) then
  begin
    strDepthAlt:= HCAltBand2String(PSelectedUnit^.AltBand);
    SGListBoxUnitInfo.Items.Insert(0, 'Missile Unit - Altitude: ' + strDepthAlt);
    SGListBoxUnitInfo.Items.Add(format('Number Now/Original: %d/ %d',
      [PSelectedUnit^.Missile^.PlatNow, PSelectedUnit^.Missile^.PlatStart]));
    SGListBoxUnitInfo.Items.Add(format('Shooter %2.2s:%3.3d - %s',
      [PHCGroup(PHCUnit(PSelectedUnit^.Missile^.sourceUnit)^.Group)^.id,
      PHCUnit(PSelectedUnit^.Missile^.sourceUnit)^.id, PHCUnit(PSelectedUnit^.Missile^.sourceUnit)^.name]));
    if PSelectedUnit^.Missile^.targetUnit <> nil
    then
    SGListBoxUnitInfo.Items.Add(format('Target: %2.2s-%3.3d - %s',
      [PHCGroup(PHCUnit(PSelectedUnit^.Missile^.targetUnit)^.Group)^.id,
      PHCUnit(PSelectedUnit^.Missile^.targetUnit)^.id, PHCUnit(PSelectedUnit^.Missile^.targetUnit)^.name]));
    SGlblUnitKts.Caption:= format('%d kts, Target Time %d secs',
      [PSelectedUnit^.kts, PSelectedUnit^.Missile^.tTime]);    //240613  & 240907
    case (HCAnnexHelper.HCAnnexShift(PSelectedUnit^.Annex)) of
    MISSILE_FLAG:
                 begin
                   SGListBoxUnitInfo.Items.Add(format('AirPh/SurfPh: %d/ %d; DP: %d',
                     [PHCAnnexD1^.AirPh, PHCAnnexD1^.SurfPh, PHCAnnexD1^.Dp]));   //  240607
                   SGListBoxUnitInfo.Items.Add(format('Operational: %S - %s',
                     [HCAltBand2String(PHCAnnexD1^.MinAlt), HCAltBand2String(PHCAnnexD1^.MaxAlt)]));
                 end;
    ASW_FLAG:
                 begin
                   if PHCAnnexE1^.Dp < 101
                   then
                     SGListBoxUnitInfo.Items.Add(format('Ph: %d; DP: %d',
                       [PHCAnnexE1^.Ph, PHCAnnexE1^.Dp]))
                   else SGListBoxUnitInfo.Items.Add('ASROC');
                   SGListBoxUnitInfo.Items.Add(format('Operational: %d - %d',
                     [((PHCAnnexE1^.MinRange) div 10), ((PHCAnnexE1^.MaxRange) div 10)]));      //  240607
                 end;
    TORP_FLAG:
                 begin
                   SGListBoxUnitInfo.Items.Add(format('Ph: %d; DP Surf/Sub: %d/ %d',
                     [PHCAnnexF1^.Ph, PHCAnnexF1^.SurfDp, PHCAnnexF1^.SubDp]));
                   SGListBoxUnitInfo.Items.Add(format('MaxDepth: %s',
                     [HCAltBand2String(PHCAnnexF1^.MaxDepth)]));    //  240607
                 end;
    end;

    case PSelectedUnit^.Missile^.guid_phase of
    1: strMGPhase:= 'Inertial';
    2: strMGPhase:= 'Terminal';
    4: strMGPhase:= 'SemiActive';
    8: strMGPhase:= 'ARM';
    16: strMGPhase:= 'IR/Laser homing';
    32: strMGPhase:= 'Radar Homing';
    64: strMGPhase:= 'Mid Course Guidance';
    128: strMGPhase:= 'Command Guidance';
    else strMGPhase:= 'unknown';
    end;
    SGListBoxUnitInfo.Items.Add(format('Current Guidance Phase: %d: %s',
        [PSelectedUnit^.Missile^.guid_phase, strMGPhase]));
  end;
// end air missile unit test
//  test for landed air units
  if (PSelectedUnit^.myPlanes <> nil) then
  begin
    PmyPlanes:= PSelectedUnit^.myPlanes;
    SGListBoxUnitInfo.Items.Add('* Aircraft Landed *');
    while (PmyPlanes <> nil) do
    begin
      if  PmyPlanes^.Time > 0
          then intC:=  (PmyPlanes^.Time - intGTNow)  div 60
          else intC:= 0;
      // testing status
      case PmyPlanes^.Status of
      PS_READYING:  strStatus := 'Readying';
      PS_READY:     strStatus := 'Ready';
      PS_LAUNCHING: strStatus := 'Launching';
      PS_LANDING:   strStatus := 'Landing';
      else strStatus := 'Unknown';
      end;
      //
      SGListBoxUnitInfo.Items.Add(format('%d-%s; Status %s, ReadyTime: %d - AC-ID %d',
         [PmyPlanes^.PlatNow, PmyPlanes^.name, strStatus, intC, PmyPlanes^.PlaneID])); // 20231114 PmyPlanes^.Status

  if PmyPlanes^.LoadID <> INVALID_ID then             //  use for calc repair value
  begin
 //     PThisLoadout:= HCAnnexHelper.HCGetAnnexY1(PmyPlanes^.LoadID);     // 20231115 moved line 240616 ad loadID
//
  if PmyPlanes^.LoadID <> INVALID_ID
  then
  begin
    PThisLoadout:= HCAnnexHelper.HCGetAnnexY1(PmyPlanes^.LoadID);
    case HCMissionMap[PThisLoadout^.MissionID] of
        Ord(THCMissionType.MISSION_TANKER): strMType:= 'Tanker';
        Ord(THCMissionType.MISSION_FERRY):  strMType:= 'Ferry';
        Ord(THCMissionType.MISSION_CARGO): strMType:= 'Cargo';
        Ord(THCMissionType.MISSION_ASSAULT):  strMType:= 'Assault';
        Ord(THCMissionType.MISSION_PATROL):  strMType:= 'Patrol';
        Ord(THCMissionType.MISSION_NUCLEAR):  strMType:= 'Nuclear';
        Ord(THCMissionType.MISSION_STANDOFF):  strMType:= 'Standoff';
        Ord(THCMissionType.MISSION_SEAD):  strMType:= 'Sead';
        Ord(THCMissionType.MISSION_GUIDED):  strMType:= 'Guided';
        Ord(THCMissionType.MISSION_UNGUIDED):  strMType:= 'Unguided';
        Ord(THCMissionType.MISSION_ANTIRUN):  strMType:= 'Anti Runway';
        Ord(THCMissionType.MISSION_ATA):  strMType:= 'Intercept';
        Ord(THCMissionType.MISSION_ASW):  strMType:= 'ASW';
        Ord(THCMissionType.MISSION_AARMR):  strMType:= 'Anti Armour';
        Ord(THCMissionType.MISSION_DECOY):  strMType:= 'Decoy';
    end;
  end;
//
      SGListBoxUnitInfo.Items.Add(format('LO-ID: %d, Mission: %s;  Range %d nm',
        [PThisLoadout^.DiskId, strMType, PThisLoadout^.Range])); // 20231115 moved "range"
      intC := 0 ;
      while PThisLoadout^.WepQty[intC] > 0 do
    begin
        //find weapon type then name
     case (HCAnnexShift(PThisLoadout^.WepId[intC])) of
    GUN_FLAG:
      begin
      PHCAnnexC1:= HCAnnexHelper.HCGetAnnexC1(PThisLoadout^.WepId[intC]);
      strWepName:= PHCAnnexC1^.ClassName + ': dp ' + IntToStr(PHCAnnexC1^.Dp);
      end;
    MISSILE_FLAG:
      begin
      PHCAnnexD1:= HCAnnexHelper.HCGetAnnexD1(PThisLoadout^.WepId[intC]);
      strWepName:= PHCAnnexD1^.ClassName + ': dp ' + IntToStr(PHCAnnexD1^.Dp);
      end;
    ASW_FLAG:
      begin
      PHCAnnexE1:= HCAnnexHelper.HCGetAnnexE1(PThisLoadout^.WepId[intC]);
      strWepName:= PHCAnnexE1^.ClassName + ': dp ' + IntToStr(PHCAnnexE1^.Dp);
      end;
    TORP_FLAG:
      begin
      PHCAnnexF1:= HCAnnexHelper.HCGetAnnexF1(PThisLoadout^.WepId[intC]);
      strWepName:= PHCAnnexF1^.ClassName + ': V/S dp '
        + IntToStr(PHCAnnexF1^.SurfDp) + IntToStr(PHCAnnexF1^.SubDp);
      end;
    ATOG_FLAG:
      begin
      PHCAnnexG1:= HCAnnexHelper.HCGetAnnexG1(PThisLoadout^.WepId[intC]);
      strWepName:= PHCAnnexG1^.ClassName + ': dp ' + IntToStr(PHCAnnexG1^.Dp);
      end;
    ECM_FLAG:
      begin
      PHCAnnexG2:= HCAnnexHelper.HCGetAnnexG2(PThisLoadout^.WepId[intC]);
      strWepName:= PHCAnnexG2^.ClassName;
      end;
    //   250801 add sonar
    SONAR_FLAG:
      begin
      PHCAnnexM1:= HCAnnexHelper.HCGetAnnexM1(PThisLoadout^.WepId[intC]);
      strWepName:= PHCAnnexM1^.ClassName;
      end;
    else strWepName:= 'misc';
    end; //case   now have weapon name
        SGListBoxUnitInfo.Items.Add(format('   %d  %s',[PThisLoadout^.WepQty[intC], strWepName]));
        intC:= intC +1;
    end;    // now have weapon No/names listed  for this plane
  end
  else SGListBoxUnitInfo.Items.Add('INVALID Loadout ID');  //  testtonight 231117
      PmyPlanes:= PmyPlanes^.next;
    end;
  end;
// end test for landed air units
// SURFACE UNIT MOUNTS
  if (PselectedUnit^.Mnts <> nil) and (PSelectedUnit^.Air = nil) then       //  240307 re introduce NOT AIR
  begin
  SGListBoxUnitInfo.Items.Add('* Mounts *');
  PThisMnt:= PselectedUnit^.Mnts;
  while (PThisMnt <> nil) do
    begin                             // PThisMnt = ships mount,  PThisMount = Mount annex
    if (HCAnnexShift(PThisMnt^.MntId) = MOUNT_FLAG)  then
  begin    //  Name Mount
    PThisMount:= HCAnnexHelper.HCGetAnnexZ1(PThisMnt^.MntId);
    SGListBoxUnitInfo.Items.Add(format('%s #%d: %d mounts; %d tubes, %d targets, Damage: %d',
           [PThisMount^.ClassName, PThisMount^.DiskId, PThisMount^.NumMounts, PThisMount^.NumTubes,
           PThisMount^.NumTargets, PThisMnt^.Damage]));
 //    if PThisMount^.WepId <> Invalid_ID then  ThisWepID:= PThisMount^.WepId;  240304 moved test
  end;
 //   case (HCAnnexShift(ThisWepId)) of        // 20231117    sepatated multi an single mounts
    if PThisMnt^.WepId <> Invalid_ID
    then    //  have moved valid wepID test and removed weap print if invalid 240304
    begin
    ThisWepID:= PThisMnt^.WepId;
    if (HCAnnexShift(ThisWepId)) =  MULTI_FLAG
    then
//  Multi Mounts
    begin
        PThisMulti:= HCAnnexHelper.HCGetAnnexZ2(ThisWepId);
        intC:= 0;
        while (PThisMulti^.WepQty[intC] > 0) do
        begin
         //
          case (HCAnnexShift(PThisMulti^.WepId[intC])) of
          GUN_FLAG:
            begin
            PHCAnnexC1:= HCAnnexHelper.HCGetAnnexC1(PThisMulti^.WepId[intC]);
            strWepName:= Format('%s; ARange: %d NM, SRange: %d NM',
              [PHCAnnexC1^.ClassName, (PHCAnnexC1^.AirRange div 10), (PHCAnnexC1^.SurfRange div 10)]);
            end;  //   240405 more detail
          MISSILE_FLAG:
            begin
            PHCAnnexD1:= HCAnnexHelper.HCGetAnnexD1(PThisMulti^.WepId[intC]);
            strWepName:= Format('%s; ARange: %d NM, SRange: %d NM, MinRange: %d NM',[PHCAnnexD1^.ClassName,
      (PHCAnnexD1^.AirRange div 10), (PHCAnnexD1^.SurfRange div 10), (PHCAnnexD1^.MinRange div 10)]);
            //  insertnmin range   220230513   22021117
            end;
          ASW_FLAG:
          begin
          PHCAnnexE1:= HCAnnexHelper.HCGetAnnexE1(PThisMulti^.WepId[intC]);
          strWepName:= Format('%s: MaxRange: %d MinRange: %d',
              [PHCAnnexE1^.ClassName, (PHCAnnexE1^.MaxRange div 10), (PHCAnnexE1^.MinRange div 10)]);
          end;
          TORP_FLAG:
          begin
          PHCAnnexF1:= HCAnnexHelper.HCGetAnnexF1(PThisMulti^.WepId[intC]);
          strWepName:= Format('%s; MaxRange: %d/%d MaxDepth %s',
            [PHCAnnexF1^.ClassName, (PHCAnnexF1^.MaxRange[0] div 10), (PHCAnnexF1^.MaxRange[1] div 10),
            HCAltBand2String(PHCAnnexF1^.MaxDepth)]);
          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;
           //    now print out data for multi mount this weapon
            SGListBoxUnitInfo.Items.Add(format('  %d from %d of %s',
            [PThisMnt^.Ammo_c[intC], PThisMulti^.WepQty[intC], strWepName]));   //  20230503 include current load and range could add thiswepid number here
            intC:= intC + 1;

        end;     //  end one weapon of multi mount
        strWepName:= '';  //  needs to be reset to nil so as not to list at end of multi mount

    end   //;      // end case of multi mount


    else  //     must be a single mount   PThisMount = annex PThisMnt = ship mount
          //  need to separate air units and not look up annex-NumRoundsMnt
    begin
    case (HCAnnexShift(ThisWepId)) of
    GUN_FLAG:
      begin
      PHCAnnexC1:= HCAnnexHelper.HCGetAnnexC1(ThisWepId);
      strWepName:=  Format('%d of %s: ARange: %d NM, SRange %d NM',
      [(PThisMount^.NumRoundsMnt * PThisMount^.NumMounts), PHCAnnexC1^.ClassName,
      (PHCAnnexC1^.AirRange div 10), (PHCAnnexC1^.SurfRange div 10)]);    // PThisMnt^.Ammo_c[0]    240303
      end;
    MISSILE_FLAG:
      begin
      PHCAnnexD1:= HCAnnexHelper.HCGetAnnexD1(ThisWepId);
      strWepName:= Format('%d of %s; ARange: %d NM, SRange: %d NM, MinRange: %d NM',[PThisMount^.NumRoundsMnt * PThisMount^.NumMounts,
      PHCAnnexD1^.ClassName, (PHCAnnexD1^.AirRange div 10), (PHCAnnexD1^.SurfRange div 10),
      (PHCAnnexD1^.MinRange div 10)]);
            //  insertnmin range   220230513 insert ARange n SRange 20231117   240306 add intitial mount rounds (and below)
      end;
    ASW_FLAG:
    begin
    PHCAnnexE1:= HCAnnexHelper.HCGetAnnexE1(ThisWepId);
    strWepName:= IntToStr(PThisMount^.NumRoundsMnt * PThisMount^.NumMounts) + ' of ' +  PHCAnnexE1^.ClassName;
    end;
    TORP_FLAG:
    begin
    PHCAnnexF1:= HCAnnexHelper.HCGetAnnexF1(ThisWepId);
    strWepName:= Format('%d of %s: MaxRange: %d/%d, maxDepth: %s',
        [(PThisMount^.NumRoundsMnt * PThisMount^.NumMounts), PHCAnnexF1^.ClassName,
        (PHCAnnexF1^.MaxRange[0] div 10), (PHCAnnexF1^.MaxRange[1] div 10),
            HCAltBand2String(PHCAnnexF1^.MaxDepth)]); //  240405 more detail
    end;
    ATOG_FLAG:
    begin
    PHCAnnexG1:= HCAnnexHelper.HCGetAnnexG1(ThisWepId);
    strWepName:= 'weptype ' + intToStr(PHCAnnexG1^.WType) + '/' + PHCAnnexG1^.ClassName;
    end;
    ECM_FLAG:
    begin
    PHCAnnexG2:= HCAnnexHelper.HCGetAnnexG2(ThisWepId);
    strWepName:= PHCAnnexG2^.ClassName;
    end;
    else
      strWepName:= format('misc - WepID %d',[ThisWepId]);
    end;  //  end look at non multi mount 20231117
    //  now print out non multi mount data   (already printed multi wep data?)
      SGListBoxUnitInfo.Items.Add(Format('   %1.1d from %s #%d',
        [PThisMnt^.Ammo_c[0], strWepName, ThisWepId]));  // 20231117 add space formatting 250802add thiswepid number here
      strWepName:= '';
    end;   // end mount
       end; // valid weaponID     240404 moved valid weap id test
                            //  20231117 moved printout code from here
    PThisMnt:= PThisMnt^.next;
    end;       // end  each mount
  end;       // end if mounts
//
  if (PSelectedUnit^.Mags <> nil) then
  begin
  SGListBoxUnitInfo.Items.Add('* Magazines *');
  //if (isShip = True)                       //  240319 listing mag info
  //then SGListBoxUnitInfo.Items.Add(format('1stmag @ %d, NoOfMags # %d',[PHCAnnexA1^.MagStart, PHCAnnexA1^.MagCount]));
  PThisMag:= PSelectedUnit^.Mags;
  while (PThisMag <> nil) do
    begin
    //  get annex info
    case (HCAnnexShift(PThisMag^.WepId)) of
    GUN_FLAG:
      begin
      PHCAnnexC1:= HCAnnexHelper.HCGetAnnexC1(PThismag^.WepId);
      strWepName:= PHCAnnexC1^.ClassName;
      end;
    MISSILE_FLAG:
      begin
      PHCAnnexD1:= HCAnnexHelper.HCGetAnnexD1(PThismag^.WepId);
      strWepName:= PHCAnnexD1^.ClassName;
      end;
    ASW_FLAG:
    begin
    PHCAnnexE1:= HCAnnexHelper.HCGetAnnexE1(PThismag^.WepId);
    strWepName:= PHCAnnexE1^.ClassName;
    end;
    TORP_FLAG:
    begin
    PHCAnnexF1:= HCAnnexHelper.HCGetAnnexF1(PThismag^.WepId);
    strWepName:= PHCAnnexF1^.ClassName;
    end;
     // moreweapons?
    else
      strWepName:= 'unknown weapon ID: ' + IntToStr(PThisMag^.WepId)
    end;
//  ShowMessage(strWepName + '/' +  IntToStr(PThisMag^.WepId));       // debugging  removed 20240215
    SGListBoxUnitInfo.Items.Add(format('   %s: Count: %d, ReloadTime: %d',[strWepName, PThisMag^.Count, PThisMag^.ReloadTime]));
//  we could amend magazine data here
    PThisMag:= PThisMag^.next;
    end;
  end;   //end mags
  SGListBoxUnitInfo.Items.Add('* General *');
  SGListBoxUnitInfo.Items.Add(' .......... ');
  RLat:= 90-(PSelectedUnit^.sLat/sliver1);       // have to convert game slat/slon to real lat/lon
  RLon:= PSelectedUnit^.sLon/sliver1;
  SGListBoxUnitInfo.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 := 'TooDeep';
  1: strDepthAlt := 'Vdeep';
  2: strDepthAlt := 'Deep';
  3: strDepthAlt := 'Intermed';
  4: strDepthAlt := 'Shallow';
  5: strDepthAlt := 'Snorkel';
  6: strDepthAlt := 'SeaLevel';
  7: strDepthAlt := 'ScopeAlt';
  8: strDepthAlt := 'SmallShip';
  9: strDepthAlt := 'MedShip';
  10: strDepthAlt := 'LrgShip';
  11: strDepthAlt := 'Land';             // normally vlow
  12: strDepthAlt := 'Low';
  13: strDepthAlt := 'Medium';
  14: strDepthAlt := 'High';
  15: strDepthAlt := 'Vhigh';
  16: strDepthAlt := 'SubOrbit';
  end;
  //
  SGListBoxUnitInfo.Items.Add('water depth: ' + strDepthAlt);      //240617
  SGBtnUntRem.Enabled := True;
  end
  else
  begin
    SGListBoxUnitInfo.Items.Clear;
    SGListBoxUnitInfo.Items.Add('bad unit selection?'); // something strange went on
  end;
  end    // end that the group was valid
else begin
         SGlbxGroups.Items.Clear;
         SGlbxGroups.Items.Add('Selected Unit from Invalid Group');
         SGlbxUnits.Items.Clear;
         SGListBoxUnitInfo.Clear;
         SGlblUnitIDName.Caption:= '';           //  clear all boxs until a unit is selected  20230503
         SGlblClassName.Caption:= '';
         SGlblUnitShowing.Caption:= '';
     end;        //  was invalid group


end;

end.

