library ToolBox;

{$mode delphi}{$H+}

{$R *.res}

uses
  Classes
  { you can add units after this }
  ,mainform,uHCDefines, uHCGCI, uHCGlobals, uHCTypeDefs, uHCEventHelpers,
  uHCDateTime, uHCAnnexHelpers, uHCAnnexDefs, uLazGUIGlobals, UHCUtils,
  Windows, Forms, ShellAPI, SysUtils, Types, Dialogs, Interfaces, Controls,
  runtimetypeinfocontrols, FileInfo;

const
  DLLVERN = 28;         //28
  DLLVERINTN = 32;      // 22022.027

var
  frmMain: TfrmMain;
  intGameTime, intGameTimeLeft: Long;
  dt_scen_start: PHCDateTimeRec;
  FileVersionInfo:TFileVersionInfo;
  strFileVersion, strTemp, gStrVerHC: string;
  pGrpTemp: PHCGroup;

{*** Return DLL internal version, i.e. revision 5 of my DLL ***}
function DLLVer():word;export;cdecl;
begin
  result := DLLVERN;
end;

{*** Return DLL interface version, i.e. revision 1 of HCE interface spec ***}
function DLLVerInt():word;export;cdecl;
begin
  result := DLLVERINTN;
end;

{*** Return DLL description, 255 characters max ***}
function DLLDesc():PAnsiChar;export;cdecl;
begin
  result := 'HCE Toolbox - export DLL by Don Thomas/Tony Eischens';
end;

{*** Free DLL ***}
procedure DLLFree();export;cdecl;
begin
  // Do any cleanup here, this is the end
  frmMain.WindowState:= wsMinimized;   //  250725;
  frmMain.Free;
end;

{*** Init DLL ***}
procedure DLLInit(hwndHC: HWND; pathHC: PAnsiChar; verHC: PAnsiChar;
  verSaveGame: LongWord; funcRefs: PHCDLL_FUNC_REF; PGEClosing: Pointer;
  PDLLBusy: Pointer; verInterface: LongWord; srandSeed: longword;
  harpoon_uuid_session: PAnsiChar; hclicense_dt_expire: Int64;
  hclicense_licensee: PAnsiChar; usblicense_desc: PAnsiChar;
  RESRSRStructureVersion: ulong;  AnnexVersion: ushort);export;cdecl;
var
    funcTmp: PHCDLL_FUNC_REF;

begin
  // here we initialize globals and such for the life of the DLL
  Application.Initialize;
  uLazGUIGlobals.PDLLBusy:=PDLLBusy;
  uLazGUIGlobals.PGEClosing:=PGEClosing;
  uLazGUIGlobals.intVerInterface:=verInterface;
  uLazGUIGlobals.intVerSaveGame:=verSaveGame;
  frmMain := TfrmMain.Create(nil);
  frmMain.ParentWindow := hwndHC;
  FileVersionInfo:= TFileVersionInfo.Create(nil);
  FileVersionInfo.ReadFileInfo;
  strFileVersion:=  FileVersionInfo.VersionStrings.Values['FileVersion']
  + ' - ' + FileVersionInfo.VersionStrings.Values['Comments'] //20230110
  + ' - ' + FileVersionInfo.VersionStrings.Values['FileDescription'];    // 20230404
  FileVersionInfo.Free;
  if Length(strFileVersion)= 0 then
   strFileVersion := '2.5.0.0';
  frmMain.Caption := 'Toolbox ' + strFileVersion;           //  20230404 put required GE into project optins
// assign pointer location for 'stuff'
  funcTmp := funcRefs;
  while (funcTmp <> nil) do
  begin
    if (funcTmp^.funcName = 'NewPtr') then
      frmMain.NewPtr := funcTmp^.funcPtr
    else if (funcTmp^.funcName = 'DispPtr') then
      frmMain.DisposPtr := funcTmp^.funcPtr
    else if (funcTmp^.funcName = 'del_HarpoonEvent_list') then
      frmMain.del_HarpoonEvent_list :=  PHCDLL_FUNC_REF(PHCDLL_FUNC_REF(funcRefs^.next).next).funcPtr
    else if (funcTmp^.funcName = 'QueueHarpoonEvent') then
      frmMain.HCQueueHarpoonEvent := THCQueueHarpoonEvent(funcTmp^.funcPtr)
    else if (funcTmp^.funcName = 'dllCheckLatLonDepth') then
      frmMain.HCdllCheckLatLonDepth := THCdllCheckLatLonDepth(funcTmp^.funcPtr)
    else if (funcTmp^.funcName = 'annex_id_to_disk_id') then
      frmMain.HCannex_id_to_disk_id := THCannex_id_to_disk_id(funcTmp^.funcPtr)
    else if (funcTmp^.funcName = 'disk_id_to_annex_id') then
      frmMain.HCdisk_id_to_annex_id := THCdisk_id_to_annex_id(funcTmp^.funcPtr)
      ;

    funcTmp := funcTmp^.next;
  end;
  gStrVerHC:= verHC;
  frmMain.Show;
  frmMain.PageControl1.ActivePageIndex:= 2;
  frmMain.intPageActive:= 2;
//  frmMain.SetSpinEditTop.Value:= frmMain.Top;    // position test             - frmMain.SetSpinEditTop.Value
  frmMain.Top:= StrToInt(frmMain.IniPropStorage1.StoredValue['frmTop']);                        //  20230105  position test
  frmMain.Left:= StrToInt(frmMain.IniPropStorage1.StoredValue['frmLeft']);
  frmMain.BringToFront;                     // 20230105  not needed I think  but influences top reading
end;

//procedure ProcessVCList(PThisVicCondn: PHCVictoryConditions);
//var
//  strVT, strUC, strOS, strDam, strDT: string;
//begin
//   frmMain.StatVCLstbx.Clear;
//   frmMain.StatVCLstbx.Items.Add('Import at Session Start');
//   strVT:= 'No VCs met';
//    while PThisVicCondn <> nil do
//    begin
//    if (PThisVicCondn^.gameTimeAchieved <> 0) then
//    begin
//      case PThisVicCondn^.VicType of
//        0:  strVT:= 'Minimum';
//        1:  strVT:= 'Total';
//        else  strVT:= '';
//      end;
//      if PThisVicCondn^.AndOrFlag = 1
//          then strVT:= strVT + '(and)'
//          else strVT:= strVT + '(or)';  // set victory type
//      case PThisVicCondn^.unitType of
//      0: strUC:= 'Carrier';
//      1: strUC:= 'Ship';
//      2: strUC:= 'Sub';
//      3: strUC:= 'Plane';
//      4: strUC:= 'Helo';
//      5: strUC:= 'Base';
//      6: strUC:= 'Fixed AD';
//      7: strUC:= 'Mobile AD';
//      8: strUC:= 'Armoured';
//      9: strUC:= 'UnArmred';
//      else strUC:= 'Unknown';
//      end;
//
//      case PThisVicCondn^.itemType of
//      0:  strDT:= '' ;
//      1:  strDT:= Format('BroadType %s',[AnnexData^.PBSStrings^.BroadVicType[PThisVicCondn^.DetailType]]);
//      2:  if  PThisVicCondn^.unitType = (3 or 4)
//          then  strDT:= Format('DetailType %s',[AnnexData^.PBSStrings^.ACType[PThisVicCondn^.DetailType]])
//          else  strDT:= Format('DetailType %s',[AnnexData^.PBSStrings^.ShipType[PThisVicCondn^.DetailType]]);
//      else strDT := '';
//      end;
//
//      if PThisVicCondn^.TimeOnStation = 0
//      then strOS:= 'Destroyed'
//      else strOS:= format('OnStation for %d Mins',[(PThisVicCondn^.TimeOnStation) div 60]);
//      if  PThisVicCondn^.numDamaged = 0
//          then strDam:= ''
//          else strDam:= Format('%d units %d %s damaged',
//          [PThisVicCondn^.numDamaged, PThisVicCondn^.percentDamage, '%']);
//
//      frmMain.StatVCLstbx.Items.Insert
//      (0, Format('GT achieved: %8.8d, vc#%.1d %s; %s; Pertains to: %s; %s; Require: %d %s, %s',
//      [PThisVicCondn^.gameTimeAchieved, PThisVicCondn^.id, frmMain.Side2String(PThisVicCondn^.WhichSide),
//      strVT, strUC, strDT, PThisVicCondn^.numKilled, strOS, strDam]));
//     end;        // end a met condition listing
//      PThisVicCondn:= PThisVicCondn^.next;
//    end;         // end all VCs
//    if strVT = 'No VCs met' then frmMain.StatVCLstbx.Items.Insert(0,'No VCs are yet met');
//    frmMain.StatVCLstbx.Items.Insert(0, 'Session Play Starts');
//end;


procedure StartGroup(StartGroup: PHCGroup);export;cdecl;
begin
     frmMain.PStartGroup:=StartGroup;
 //    ProcessVCList(frmMain.PVCList);
end;

procedure ScenLoad(strHarpPath: PAnsiChar; intScenNum: SmallInt; strScenFPath: PAnsiChar; intBSNum: ShortInt;
  strBSFName: PAnsiChar; intSide: byte; intGameTimeIn: Long; intGameTimeLeftIn: Long;
   AnnexDataIn: PHCAnnexes_disk; DatabaseVersionDBIn: PHCDATABASE_VERSION;
    ScenDataIn: PHCSCENARIO_DATA; ScenSaveDataIn: PHCSCENARIO_SAVE_DATA;
   GlobalsIn: PHCGlobals);export;cdecl;      //dt_scen_start_in: PHCDateTimeRec;    deleted dllintver21

var
  strScenNum, strBatSetName: string;
  TimeNowTimeRec: THCDateTimeRec;

begin
  frmMain.strMySide:=format('%d',[intSide]);
  frmMain.MySide:= intSide;
  case intSide of
  0: frmMain.EnmySide:= uchar(1);
  1: frmMain.EnmySide:= 0;
  end;
  frmMain.MLtmemLog.Lines.Clear;
  AnnexData:= AnnexDataIn;
//  frmMain.PAnnexData:= AnnexDataIn;       not used
//  frmMain.dt_scen_start:= dt_scen_start_in;    no longer valid and not in use
  HCAnnexHelper := THCAnnexHelper.Create(AnnexDataIn, frmMain.HCannex_id_to_disk_id,
    uLazGUIGlobals.intVerInterface, uLazGUIGlobals.intVerSaveGame);
  DatabaseVersionDB := DatabaseVersionDBIn; // ALE 20170923 current database
  ScenData := ScenDataIn; // ALE 20170923 unique identifiers, lineage, etc.
  ScenSaveData := ScenSaveDataIn; // ALE 20170923 save UUIDs and Scenario Orders
  // write up scen data in session page
  if intScenNum <> -1
    then
      begin
        strScenFPath:= 'BattleSet Scenario ';
        intScenNum:= intScenNum + 1;
        strScenNum:= Format('#%d',[intScenNum]);          //  20230416 output tidy up
      end
    else strScenNum:= '';
    if intBSNum = 35 then strBatSetName:= 'Custom'
                             else strBatSetName:= strBSFName;

    frmMain.StatSessLstBx.Items.Clear;         //20230416  reset screen each scenload
    frmMain.StatSessLstBx.Items.Add('HC version: ' + gStrVerHC);
    frmMain.StatSessLstBx.Items.Add(Format('FileLoaded: %s, %s',[strScenFPath, strScenNum]));
    frmMain.StatSessLstBx.Items.Add('ScenarioDescription: %s',[ScenSaveDataIn^.OrdersShortDesc]);
    frmMain.StatSessLstBx.Items.Add(Format('BattleSet=%d, BattleSet=%s, PlayerSide=%s',
    [intBSNum, strBatSetName, AnnexData^.PBSStrings^.SideName[intSide]]));
    frmMain.StatSessLstBx.Items.Add(Format('SE Version= %s',[ScenDataIn^.Lineage.Top.SEVersion]));
    frmMain.StatSessLstBx.Items.Add(Format('Loaded Database %s',[DatabaseVersionDBIn.RESFile.flavor]));
    frmMain.StatSessLstBx.Items.Add(Format('Original Database %s',[ScenDataIn^.Lineage.Top.DatabaseVersion.RESFile.flavor]));
    frmMain.StatSessLstBx.Items.Add(Format('Last saved game Database %s',[ScenDataIn^.DatabaseVersion.RESFile.flavor]));
    frmMain.StatSessLstBx.Items.Add(Format('Scenario Start %4.4d-%2.2d-%2.2d, %2.2d:%2.2d:%2.2d',
    [ScenDataIn^.dt_scen_start.year, ScenDataIn^.dt_scen_start.month,
        ScenDataIn^.dt_scen_start.day, ScenDataIn^.dt_scen_start.hour,
        ScenDataIn^.dt_scen_start.minute, ScenDataIn^.dt_scen_start.second]));
    TimeNowTimeRec:= HCDateTimeInc(ScenDataIn^.dt_scen_start,intGameTimeIn);

    frmMain.StatSessLstBx.Items.Add('This Session Start %4.4d-%2.2d-%2.2d, %2.2d:%2.2d:%2.2d',
    [TimeNowTimeRec.year, TimeNowTimeRec.month, TimeNowTimeRec.day, TimeNowTimeRec.hour,
    TimeNowTimeRec.minute, TimeNowTimeRec.second]);


end;

procedure MsgLog(MsgLine: PAnsiChar);export;cdecl;
begin
  if Pos('ITER', MsgLine) = 0 then  //  250109  filter out iterative save note
  begin
   while (frmMain.MLtmemLog.Lines.Count > frmMain.SETspnMLC.Value) do
    begin    frmMain.MLtmemLog.Lines.Delete(frmMain.MLtmemLog.Lines.Count - 1) end;
    frmMain.MLtmemLog.Lines.Insert(0, '');
    frmMain.MLtmemLog.Lines.Strings[0]:=Format('%s', [MsgLine]);
  end;
end;

procedure GameTime(GameTime: Long; GameTimeLeft: Long);export;cdecl;

begin
  frmMain.intGameTime := GameTime;
  frmMain.intGameTimeLeft:= GameTimeLeft;
  frmMain.RflLblGameTime.Caption:= Format('%9.9d',[GameTime]);
end;

procedure HarpoonEventHead(HarpEventHead: PHCHarpoonEvent);export;cdecl;

var PThisRefuelEvent: PHCAirRefuelEvent;

begin
  If HarpEventHead <> nil then
     begin
      frmMain.PHCEventHead:= HarpEventHead;     // update for use in eg spliting groups
     if ((HarpEventHead^.flag and CommandMask) = AIR_REFUEL_EVENT)      // ddt 20150720   have a refuel event    231003 [ and CommandMask)]
         and (PHCGroup(HarpEventHead^.ourGroup)^.side = frmMain.MySide)
     then
     begin
     PThisRefuelEvent:= HarpEventHead^.DataPtr;
      if ((frmMain.RflChkBPause.Checked = True) and (PThisRefuelEvent^.planestofuel[0] <> 205))
         or ((frmMain.RflChkBAutoPFinish.Checked = True) and  (PThisRefuelEvent^.planestofuel[0] = 255))
      then              //  so we list it if auto pause on and event due
      begin
      frmMain.RflLbxGroups.Clear;
      frmMain.RflLbxGroups.Items.Add(Format('%3.3s %d', [PHCGroup(HarpEventHead^.ourGroup)^.id,
                                                                            HarpEventHead^.Time]));
       //frmMain.RflChkBResume.Checked:= False;   }  DDT 210601 now use generic pause button
           //Byte(PDLLBusy^) := CTRUE;                }
       frmMain.RflChkBFind.Checked:= False;                //  240621 moved to before activate page
       frmMain.RflChkBTnkGrpSrch.Checked:= False;
       frmMain.intPageActive:= frmMain.PageControl1.ActivePageIndex;
       frmMain.WindowState:= wsNormal;
       frmMain.PageControl1.ActivePageIndex:=3;
       frmMain.RflLbxUnitFill(HarpEventHead^.ourGroup);
       frmMain.RflLbxDetailFill(HarpEventHead);
           if frmMain.RflBtnPause.Caption = 'Pause' then      // if not paused then click to pause (but should be)
             frmMain.RflBtnPause.Click;
           showmessage('Attention, refuel autopause!');         //  231007
       end;   // end an autoP box was ticked
      //  240621
     end;    //  end there was an our side refuel
     If  ((HarpEventHead^.flag and CommandMask) = ATTACK_EVENT)          // start        230103 old change of data form
         then If (PHCGroup(HarpEventHead^.ourGroup)^.side = frmMain.MySide)    //20230607 switch green to enemy if attacked
             then If format('%s',[PHCAttackEvent(HarpEventHead^.DataPtr)^.TargetGroup[1]])= '0'    // simple test
               then
               begin
// test:  ShowMessage(format('%s%s%s',[PHCAttackEvent(HarpEventHead^.DataPtr)^.TargetGroup[0], PHCAttackEvent(HarpEventHead^.DataPtr)^.TargetGroup[1],  PHCAttackEvent(HarpEventHead^.DataPtr)^.TargetGroup[2]]));
// test:  pGrpTemp:= frmMain.GroupID2GroupPtr(format('%s%s',[PHCAttackEvent(HarpEventHead^.DataPtr)^.TargetGroup[1],  PHCAttackEvent(HarpEventHead^.DataPtr)^.TargetGroup[2]]));
// test:  if pGrpTemp <> nil then ShowMessage(format('NeutSide: %d',[pGrpTemp^.side]));
// ShowMessage('You have attacked a neutral, your responsible group is stood down awaiting war crimes trial');
               strTemp:= format('Sir, from Fleet Command: your group %s has attacked a neutral and is stood down awaiting war crimes trial',
               [PHCGroup(HarpEventHead^.ourGroup)^.id]);
               frmMain.PostMessage(strTemp);
               PHCGroup(HarpEventHead^.ourGroup)^.side:= 2;    //  250807 make the group neutral
             // / pGrpTemp:= HCGroupID2GroupPtr(format('%s%s',
             //    [PHCAttackEvent(HarpEventHead^.DataPtr)^.TargetGroup[1],
             //    PHCAttackEvent(HarpEventHead^.DataPtr)^.TargetGroup[2]]), frmMain.PStartGroup);  //  202330622 moved function to hcutils
             //  if pGrpTemp <> nil then pGrpTemp^.side:= frmMain.EnmySide;     // attacked neutral group turns enemy
             //if (PHCGroup(HarpEventHead^.ourGroup)^.gtype = MT_HELO)
             //  or  (PHCGroup(HarpEventHead^.ourGroup)^.gtype = MT_AC)
             //    then PHCGroup(HarpEventHead^.ourGroup)^.side:= NEUTRALSIDE;
               end;
     // add same thing for neutral damaged
    end;
end;

//procedure VictoryConditionsList(PVCList: PHCVictoryConditions);export;cdecl;
//
//begin
//  frmMain.PVCList:= PVCList;
//end;

//  end;                      //  AnnexData^.PBSStrings^.SideName[whichside]

//procedure VictoryConditionAchieved(VictoryCondition: PHCVictoryConditions); export; cdecl;
////  fired in game when a victory condition is met, note for 'and' types it's when all are met
//var
//     strVT, strUC, strOS, strDam, strDT: string;
// begin
//      case VictoryCondition^.VicType of
//      0:  strVT:= 'Minimum';
//      1:  strVT:= 'Total';
//      else strVT:= '';
//      end;
//    if VictoryCondition^.AndOrFlag = 1
//        then strVT:= strVT + '(and)'
//        else strVT:= strVT + '(or)';  // set victory type
//    case VictoryCondition^.unitType of
//    0: strUC:= 'Carrier';
//    1: strUC:= 'Ship';
//    2: strUC:= 'Sub';
//    3: strUC:= 'Plane';
//    4: strUC:= 'Helo';
//    5: strUC:= 'Base';
//    6: strUC:= 'Fixed AD';
//    7: strUC:= 'Mobile AD';
//    8: strUC:= 'Armoured';
//    9: strUC:= 'UnArmred';
//    else strUC:= 'Unknown';
//    end;
//
//    case VictoryCondition^.itemType of
//    0:  strDT:= '' ;
//    1:  strDT:= Format('BroadType %s',[AnnexData^.PBSStrings^.BroadVicType[VictoryCondition^.DetailType]]);
//    2:  if  VictoryCondition^.unitType = (3 or 4)
//        then  strDT:= Format('DetailType %s',[AnnexData^.PBSStrings^.ACType[VictoryCondition^.DetailType]])
//        else  strDT:= Format('DetailType %s',[AnnexData^.PBSStrings^.ShipType[VictoryCondition^.DetailType]]);
//    else strDT := '';
//    end;
//    if VictoryCondition^.TimeOnStation = 0
//    then strOS:= 'Destroyed'
//    else strOS:= format('OnStation for %d Mins',[(VictoryCondition^.TimeOnStation) div 60]);
//    if  VictoryCondition^.numDamaged = 0
//        then strDam:= ''
//        else strDam:= Format('%d units %d %s damaged',
//        [VictoryCondition^.numDamaged, VictoryCondition^.percentDamage, '%']);
//
//    frmMain.StatVCLstbx.Items.Insert
//    (0, Format('GT achieved: %8.8d, vc#%.1d %s; %s; Pertains to: %s; %s; Require: %d %s, %s',
//    [VictoryCondition^.gameTimeAchieved, VictoryCondition^.id,
//     AnnexData^.PBSStrings^.SideName[VictoryCondition^.WhichSide],
//     strVT, strUC, strDT, VictoryCondition^.numKilled, strOS, strDam]));  //  20230420 added ID
// end;

procedure ActGroupPtr(ActGroup: PHCGroup); export; cdecl;   //  240624
begin
  frmMain.gPActGroup:= ActGroup;
   if ActGroup <> nil then                //  if there is an active group and it is myside reset Surf/sub
   if ActGroup^.side = frmMain.MySide
    then
    case  frmMain.PageControl1.ActivePageIndex of
    4:    frmMain.SVSearchGroup();
    0:    frmMain.AGFgrpSrch();
    1:    frmMain.AtkGrpSrch();
    3:    frmMain.RefuelSheetUpdate();
    end
   else   // ActGroup is not friendly
   begin
   frmMain.PselectedEGroup:= ActGroup;
   if frmMain.PageControl1.ActivePageIndex = 1
     then frmMain.AtkEGroupSrch();
   end;
end;

procedure ActUnitPtr(ActUnit: PHCUnit); export; cdecl;      //  240624
  begin
    frmMain.gPActUnit:= ActUnit;
    if ActUnit <> nil then
    begin
    if PHCGroup(ActUnit^.Group)^.side = frmMain.MySide
      then
        case frmMain.PageControl1.ActivePageIndex of            //  240625
        4:   begin
             frmMain.gPActGroup := ActUnit^.Group;        //  get actunit listed and selected
             frmMain.SVSearchGroup();
             end;
        0:   frmMain.AGFgrpSrch();
        1:   frmMain.AtkGrpSrch();
        end
    else
    if (PHCGroup(ActUnit^.Group)^.side = frmMain.EnmySide) or  (PHCGroup(ActUnit^.Group)^.side = NEUTRALSIDE)
       then
       begin
        if frmMain.PageControl1.ActivePageIndex = 1
        then
        frmMain.AtkEGroupSrch();        //  240626  if there is a ActUnit = EUnit then should result in the group and unit selected
        frmMain.PselectedEGroup:= ActUnit^.Group;
        frmMain.PSelectedEUnit:= ActUnit;  //  240808 - if showing side not known then selctions weren't made by search
       end;
    end;    //  end actunit <> nil
  end;

procedure AllDeadUnitsPtr(AllDeadUnitsPtr: PHCDeadListItem);export;cdecl;
begin
    frmMain.PAllDeadUnit:= AllDeadUnitsPtr;
end;


exports
  DLLVer,
  DLLVerInt,
  DLLDesc,
  DLLFree,
  DLLInit,
  ScenLoad,
  MsgLog,
  HarpoonEventHead,
  GameTime,
//  VictoryConditionsList,
//  VictoryConditionLevels,
//  VictoryConditionAchieved,
  AllDeadUnitsPtr,
  ActGroupPtr,
  ActUnitPtr,
  StartGroup; // ALE 20130205 Sends updated GroupPtr whenever that changes
end.

