{$mode objfpc}{$H+}
unit uRangeRings;

// CC 20260312 TGlobe4 weapon range ring helpers
// CC 20260921 also hosts formation zone rings — same geodesic circle geometry,
//  drawn through the same Create/Update/Remove helpers with a black pen

interface

uses
  Graphics,
  TGClasses, Globe4, TGSysUtils, GlobeUtils, TGPresenters, TGObjects;
  // CC 20260924 uHCDefines dropped with ZONE_COUNT

const
  // CC 20260402 ring type indices — mirrors HC BEST_* constants
  RING_ASuW       = 0;
  RING_AAW        = 1;
  RING_SURF_RADAR = 2;
  RING_AIR_RADAR  = 3;
  RING_AIR_RANGE  = 4;
  RING_BINGO      = 5;
  RING_COUNT      = 6;

  // CC 20260402 per-ring-type colors (BGR format)
  RANGE_RING_COLORS: array[0..RING_COUNT-1] of TColor = (
    TColor($0000A5FF),  // [0] ASuW weapon = orange
    TColor($00FFFF00),  // [1] AAW weapon = cyan
    TColor($0000FF00),  // [2] Surf radar = green
    TColor($00FF00FF),  // [3] Air radar = magenta
    TColor($00FFFFFF),  // [4] Air range = white
    TColor($00808080)   // [5] Bingo range = gray
  );

  // CC 20260924 ZONE_COUNT / FORMATION_ZONE_NAMES removed: formation rings now
  //  run over all MAX_FORMATION_ZONES slots (decision 7); names via uFormationMath.RingName

  // CC 20260921 all formation zones draw as solid black lines
  FORMATION_ZONE_COLOR = TColor($00000000);

  // CC 20260924 formation sector spokes every 45 deg from true north, matching the GE
  //  display and HC's FormationSector000_045..FormationSector315_000 bits
  FORMATION_SECTOR_COUNT = 8;

procedure CreateRangeRing(aGlobe: TGlobe4; aLayer: TGlobeLayer;
  const pCenter: TPointLL; rangeNM: Integer; aColor: TColor;
  var ioNextID: Integer; out presID: Integer; out objID: Integer);

procedure UpdateRangeRingPosition(aLayer: TGlobeLayer; objID: Integer;
  const pCenter: TPointLL; rangeNM: Integer);

procedure RemoveRangeRing(aLayer: TGlobeLayer; presID, objID: Integer);

// CC 20260924 one polyline object holding all sector spokes (one chain each),
//  drawn from the inner to the outer formation ring; removed with RemoveRangeRing
procedure CreateSectorLines(aGlobe: TGlobe4; aLayer: TGlobeLayer;
  const pCenter: TPointLL; innerNM, outerNM: Integer; aColor: TColor;
  var ioNextID: Integer; out presID: Integer; out objID: Integer);

procedure UpdateSectorLines(aLayer: TGlobeLayer; objID: Integer;
  const pCenter: TPointLL; innerNM, outerNM: Integer);

// CC 20260922 ring/zone toggles moved to gMapSettings (uMapConfig)

implementation

procedure BuildRingPoints(const pCenter: TPointLL; rangeNM: Integer;
  pChain: TPointStore);
var
  iBearing: Integer;
  iDist: Integer;
begin
  iDist := GlobeUnitsFrom(rangeNM, NauticalMile);
  pChain.Count := 0;
  for iBearing := 0 to 35 do
    pChain.Add(AngleDistToGCPoint(pCenter, DecimalToGlobeUnits(iBearing * 10), iDist));
  pChain.Add(pChain[0]); // close the ring
end;

procedure CreateRangeRing(aGlobe: TGlobe4; aLayer: TGlobeLayer;
  const pCenter: TPointLL; rangeNM: Integer; aColor: TColor;
  var ioNextID: Integer; out presID: Integer; out objID: Integer);
var
  lPres: TPolylinePresenter;
  lDataObj: TGeoDataObject;
begin
  lPres := TPolylinePresenter.Create(aGlobe, 0);
  aLayer.Presenters.Add(lPres);
  lPres.PolyPen.PenColor := aColor;
  presID := lPres.PresenterID;
  lDataObj := TGeoDataObject.Create(aLayer.Objects);
  BuildRingPoints(pCenter, rangeNM, lDataObj.Chains[0]);
  lDataObj.PresenterID := lPres.PresenterID;
  lDataObj.ID := ioNextID;
  objID := ioNextID;
  Inc(ioNextID);
end;

procedure UpdateRangeRingPosition(aLayer: TGlobeLayer; objID: Integer;
  const pCenter: TPointLL; rangeNM: Integer);
var
  lObj: TGlobeObject;
begin
  lObj := aLayer.ObjectByID(objID);
  if lObj <> nil then
    BuildRingPoints(pCenter, rangeNM, TGeoDataObject(lObj).Chains[0]);
end;

procedure BuildSectorChains(const pCenter: TPointLL; innerNM, outerNM: Integer;
  pChains: TChainStore);
var
  i: Integer;
  lBearing: Integer;
begin
  pChains.Count := FORMATION_SECTOR_COUNT;
  for i := 0 to FORMATION_SECTOR_COUNT - 1 do
  begin
    lBearing := DecimalToGlobeUnits(i * (360 div FORMATION_SECTOR_COUNT));
    pChains[i].Count := 0;
    pChains[i].Add(AngleDistToGCPoint(pCenter, lBearing,
      GlobeUnitsFrom(innerNM, NauticalMile)));
    pChains[i].Add(AngleDistToGCPoint(pCenter, lBearing,
      GlobeUnitsFrom(outerNM, NauticalMile)));
  end;
end;

procedure CreateSectorLines(aGlobe: TGlobe4; aLayer: TGlobeLayer;
  const pCenter: TPointLL; innerNM, outerNM: Integer; aColor: TColor;
  var ioNextID: Integer; out presID: Integer; out objID: Integer);
var
  lPres: TPolylinePresenter;
  lDataObj: TGeoDataObject;
begin
  lPres := TPolylinePresenter.Create(aGlobe, 0);
  aLayer.Presenters.Add(lPres);
  lPres.PolyPen.PenColor := aColor;
  presID := lPres.PresenterID;
  lDataObj := TGeoDataObject.Create(aLayer.Objects);
  BuildSectorChains(pCenter, innerNM, outerNM, lDataObj.Chains);
  lDataObj.PresenterID := lPres.PresenterID;
  lDataObj.ID := ioNextID;
  objID := ioNextID;
  Inc(ioNextID);
end;

procedure UpdateSectorLines(aLayer: TGlobeLayer; objID: Integer;
  const pCenter: TPointLL; innerNM, outerNM: Integer);
var
  lObj: TGlobeObject;
begin
  lObj := aLayer.ObjectByID(objID);
  if lObj <> nil then
    BuildSectorChains(pCenter, innerNM, outerNM, TGeoDataObject(lObj).Chains);
end;

procedure RemoveRangeRing(aLayer: TGlobeLayer; presID, objID: Integer);
var
  lObj: TGlobeObject;
  lPres: TGlobePresenter;
begin
  lObj := aLayer.ObjectByID(objID);
  if lObj <> nil then
  begin
    aLayer.Objects.Delete(lObj);
    lObj.Free;
  end;
  lPres := aLayer.Presenters.ByID(presID, False);
  if lPres <> nil then
  begin
    aLayer.Presenters.Delete(lPres);
    lPres.Free;
  end;
end;

end.
