// Client bootstrap for the explicitly selected takeover prefab. The native game
// mode pointer already refers to the incoming entity inside its constructor, so
// capture original managers during the original mode's initialization instead.
class ERR_TerrainTakeoverModeClass : ERR_GameModeClass {}
class ERR_TerrainTakeoverMode : ERR_GameMode
{
 void ERR_TerrainTakeoverMode(IEntitySource src, IEntity parent)
 {
  if (!GetGame() || Replication.IsServer()) return;
  GetGame().ERR_BeginClientTerrainTakeover(this);
 }
}
modded class ArmaReforgerScripted
{
 protected ref array<IEntity> m_ERRClientOldManagers = {};
 protected bool m_ERRClientTerrainTakeover;
 bool ERR_IsClientTerrainTakeover() { return m_ERRClientTerrainTakeover; }
 protected SCR_EntityCatalogManagerComponent m_ERROldCatalog;
 protected EntityID m_ERROldLoadoutId;
 protected SCR_AdditionalGameModeSettingsComponent m_ERROldSettings;
 protected SCR_PerceivedFactionManagerComponent m_ERROldPerceivedFaction;
 protected SCR_PlayerListedAdminManagerComponent m_ERROldAdmins;
 protected SCR_IdentityManagerComponent m_ERROldIdentity;
 protected SCR_ArsenalManagerComponent m_ERROldArsenal;
 protected SCR_GroupsManagerComponent m_ERROldGroups;
 protected SCR_SupportStationManagerComponent m_ERROldSupport;
 protected SCR_TaskFinishHistoryManagerComponent m_ERROldTaskHistory;
 protected SCR_TaskManagerUIComponent m_ERROldTaskUI;
 protected SCR_TextsTaskManagerComponent m_ERROldTaskTexts;

 void ERR_ClientCaptureOriginal(SCR_BaseGameMode mode)
 {
  if (Replication.IsServer() || ERR_GameMode.Cast(mode)) return;
  m_ERROldCatalog = SCR_EntityCatalogManagerComponent.GetInstance();
  if (GetLoadoutManager()) m_ERROldLoadoutId = GetLoadoutManager().GetID();
  m_ERROldSettings = SCR_AdditionalGameModeSettingsComponent.GetInstance();
  m_ERROldPerceivedFaction = SCR_PerceivedFactionManagerComponent.GetInstance();
  m_ERROldAdmins = SCR_PlayerListedAdminManagerComponent.GetInstance();
  m_ERROldIdentity = SCR_IdentityManagerComponent.GetInstance();
  m_ERROldArsenal = SCR_ArsenalManagerComponent.Cast(mode.FindComponent(SCR_ArsenalManagerComponent));
  m_ERROldGroups = SCR_GroupsManagerComponent.Cast(mode.FindComponent(SCR_GroupsManagerComponent));
  m_ERROldSupport = SCR_SupportStationManagerComponent.Cast(mode.FindComponent(SCR_SupportStationManagerComponent));
  m_ERROldTaskHistory = SCR_TaskFinishHistoryManagerComponent.Cast(mode.FindComponent(SCR_TaskFinishHistoryManagerComponent));
  m_ERROldTaskUI = SCR_TaskManagerUIComponent.Cast(mode.FindComponent(SCR_TaskManagerUIComponent));
  m_ERROldTaskTexts = SCR_TextsTaskManagerComponent.Cast(mode.FindComponent(SCR_TextsTaskManagerComponent));

  m_ERRClientOldManagers = {mode, GetLoadoutManager(), GetFactionManager(), GetPerceptionManager(), GetChat()};
 }
 override void UnregisterLoadoutManager(SCR_LoadoutManager instance)
 {
  if (m_ERRClientTerrainTakeover && instance && instance.GetID() == m_ERROldLoadoutId && instance != GetLoadoutManager())
  {
   Print("Reintroduced [terrain client]: completed original loadout unregister");
   return;
  }
  super.UnregisterLoadoutManager(instance);
 }
 override protected void OnBeforeWorldCleanup()
 {
  m_ERRClientTerrainTakeover = false;
  m_ERRClientOldManagers.Clear();
  m_ERROldCatalog = null;
  m_ERROldSettings = null;
  m_ERROldPerceivedFaction = null;
  m_ERROldAdmins = null;
  m_ERROldIdentity = null;
  m_ERROldArsenal = null;
  m_ERROldGroups = null;
  m_ERROldSupport = null;
  m_ERROldTaskHistory = null;
  m_ERROldTaskUI = null;
  m_ERROldTaskTexts = null;
  m_ERROldLoadoutId = EntityID.INVALID;
  super.OnBeforeWorldCleanup();
 }
 void ERR_BeginClientTerrainTakeover(ERR_TerrainTakeoverMode incoming)
 {
  if (m_ERRClientOldManagers.IsEmpty()) return;
  m_ERRClientTerrainTakeover = true;
  SCR_BaseGameMode mode = SCR_BaseGameMode.Cast(m_ERRClientOldManagers[0]);
  PrintFormat("Reintroduced [terrain client]: drain retained mode=%1 incoming=%2", mode, incoming);
  if (mode) mode.OnGameEnd();
  // Owner entities can already be invalid while native destruction is deferred.
  // Compare the captured component itself, never clear a newer singleton.
  SCR_EntityCatalogManagerComponent.ERR_ReleaseClientCatalog(m_ERROldCatalog);
  m_DataCollectorComponent = null;
  SCR_AdditionalGameModeSettingsComponent.ERR_ReleaseTerrainSingleton(m_ERROldSettings);
  SCR_PerceivedFactionManagerComponent.ERR_ReleaseTerrainSingleton(m_ERROldPerceivedFaction);
  SCR_PlayerListedAdminManagerComponent.ERR_ReleaseTerrainSingleton(m_ERROldAdmins);
  SCR_IdentityManagerComponent.ERR_ReleaseTerrainSingleton(m_ERROldIdentity);
  SCR_ArsenalManagerComponent.ERR_ReleaseTerrainSingleton(m_ERROldArsenal);
  SCR_GroupsManagerComponent.ERR_ReleaseTerrainSingleton(m_ERROldGroups);
  SCR_SupportStationManagerComponent.ERR_ReleaseTerrainSingleton(m_ERROldSupport);
  SCR_TaskFinishHistoryManagerComponent.ERR_ReleaseTerrainSingleton(m_ERROldTaskHistory);
  SCR_TaskManagerUIComponent.ERR_ReleaseTerrainSingleton(m_ERROldTaskUI);
  SCR_TextsTaskManagerComponent.ERR_ReleaseTerrainSingleton(m_ERROldTaskTexts);

  foreach (IEntity manager : m_ERRClientOldManagers) if (manager && manager != incoming) delete manager;
  m_ERRClientOldManagers.Clear();
  PrintFormat("Reintroduced [terrain client]: drained native mode=%1 factions=%2 loadouts=%3", GetGameMode(), GetFactionManager(), GetLoadoutManager());
 }
}
modded class SCR_EntityCatalogManagerComponent
{
 static void ERR_ReleaseClientCatalog(SCR_EntityCatalogManagerComponent expected)
 {
  if (s_Instance == expected) s_Instance = null;
 }
 protected bool m_ERRWaitingForFaction;
 void ERR_CompleteFactionInit()
 {
  if (!m_ERRWaitingForFaction || !GetGame().GetFactionManager()) return;
  m_ERRWaitingForFaction = false;
  ClearEventMask(GetOwner(), EntityEvent.FRAME);
  super.EOnInit(GetOwner());
  Print("Reintroduced [terrain client]: catalog completed during faction initialization");
 }
 override void EOnInit(IEntity owner)
 {
  if (!Replication.IsServer() && ERR_TerrainTakeoverMode.Cast(owner) && !GetGame().GetFactionManager())
  {
   m_ERRWaitingForFaction = true;
   SetEventMask(owner, EntityEvent.FRAME);
   return;
  }
  super.EOnInit(owner);
 }
 override void EOnFrame(IEntity owner, float timeSlice)
 {
  if (!m_ERRWaitingForFaction || !GetGame().GetFactionManager()) return;
  m_ERRWaitingForFaction = false;
  ClearEventMask(owner, EntityEvent.FRAME);
  super.EOnInit(owner);
  Print("Reintroduced [terrain client]: catalog initialized after factions");
 }
}
modded class SCR_DelegateFactionManagerComponent
{
 override void OnFactionsInit(array<Faction> factions)
 {
  if (!Replication.IsServer() && GetGame().ERR_IsClientTerrainTakeover())
  {
   SCR_EntityCatalogManagerComponent catalog = SCR_EntityCatalogManagerComponent.GetInstance();
   if (catalog) catalog.ERR_CompleteFactionInit();
   SCR_IdentityManagerComponent identity = SCR_IdentityManagerComponent.GetInstance();
   if (identity) identity.ERR_CompleteTerrainFactionInit();
   // The server replicates these delegates. During the initial snapshot the
   // vanilla Runtime() guard is false and would create duplicates on the client.
   return;
  }
  super.OnFactionsInit(factions);
 }
}
// Both peers consume the same extra index before the vanilla payload. Vanilla
// RplLoad dereferences its faction before applying its own serialized index;
// runtime takeover delegates must bind first. Ordinary missions only consume
// the prefix and retain their normal initialization.
modded class SCR_EditableFactionComponent
{
 override bool RplSave(ScriptBitWriter writer)
 {
  writer.WriteInt(m_iFactionIndex);
  return super.RplSave(writer);
 }
 override bool RplLoad(ScriptBitReader reader)
 {
  int initialIndex;
  if (!reader.ReadInt(initialIndex)) return false;
  if (GetGame().ERR_IsClientTerrainTakeover() && !m_ScrFaction)
  {
   SetFactionIndexBroadcast(initialIndex);
   if (!m_ScrFaction) return false;
  }
  return super.RplLoad(reader);
 }
}

modded class SCR_BaseGameMode
{
 override void EOnInit(IEntity owner)
 {
  super.EOnInit(owner);
  GetGame().ERR_ClientCaptureOriginal(this);
 }
}

modded class SCR_AdditionalGameModeSettingsComponent
{
 static void ERR_ReleaseTerrainSingleton(SCR_AdditionalGameModeSettingsComponent expected)
 {
  if (s_Instance == expected) s_Instance = null;
 }
}

modded class SCR_PerceivedFactionManagerComponent
{
 static void ERR_ReleaseTerrainSingleton(SCR_PerceivedFactionManagerComponent expected)
 {
  if (s_Instance == expected) s_Instance = null;
 }
}

modded class SCR_PlayerListedAdminManagerComponent
{
 static void ERR_ReleaseTerrainSingleton(SCR_PlayerListedAdminManagerComponent expected)
 {
  if (m_Instance == expected) m_Instance = null;
 }
}

modded class SCR_IdentityManagerComponent
{
 protected bool m_ERRWaitingForTerrainFaction;
 override void EOnInit(IEntity owner)
 {
  if (!Replication.IsServer() && GetGame().ERR_IsClientTerrainTakeover() && ERR_TerrainTakeoverMode.Cast(owner) && !GetGame().GetFactionManager())
  {
   m_ERRWaitingForTerrainFaction = true;
   return;
  }
  super.EOnInit(owner);
 }
 void ERR_CompleteTerrainFactionInit()
 {
  if (!m_ERRWaitingForTerrainFaction || !GetGame().GetFactionManager()) return;
  m_ERRWaitingForTerrainFaction = false;
  super.EOnInit(GetOwner());
 }

 static void ERR_ReleaseTerrainSingleton(SCR_IdentityManagerComponent expected)
 {
  if (m_sInstance == expected) m_sInstance = null;
 }
}
