// Resolve from the scenario header even before a world exists: the backend asks
// for its mission name during startup. Child scenario filenames are not terrains.
class ERR_TerrainDisplayName
{
 // GetWorldFile() returns a path, often $Addon:Worlds/..., not a GUID resource.
 // ResourceName.GetPath() asks the resource system to resolve it and logs a
 // zero-GUID warning on every call. Compare/display paths without that lookup.
 static string Path(string world)
 {
  if (world.Length() >= 18 && world.Substring(0, 1) == "{" && world.Substring(17, 1) == "}")
   return world.Substring(18, world.Length() - 18);
  return world;
 }
 static string Label(string world)
 {
  if (world.IsEmpty()) return "";
  string name = FilePath.StripPath(Path(world));
  int extension = name.IndexOf(".");
  if (extension >= 0) name = name.Substring(0, extension);
  // Packed worlds may hide their parent container before native world loading.
  // Strip conventional scenario wrappers for that early callback fallback.
  string lower = name; lower.ToLower();
  foreach (string prefix : {"gm_", "err_", "conflict_", "combatops_", "campaign_"})
  {
   if (lower.IndexOf(prefix) != 0) continue;
   name = name.Substring(prefix.Length(), name.Length() - prefix.Length());
   lower = name; lower.ToLower(); break;
  }
  foreach (string suffix : {"_test", "_gm", "_exploration", "_conflict", "_combatops"})
  {
   int start = lower.Length() - suffix.Length();
   if (start < 0 || lower.Substring(start, suffix.Length()) != suffix) continue;
   name = name.Substring(0, start); lower = name; lower.ToLower(); break;
  }
  if (lower == "eden") return "Everon";
  name.Replace("_", " "); name.Replace("\n", " "); name.Replace("\r", " ");
  name = name.Trim();
  if (name.Length() > 80) name = name.Substring(0, 80);
  if (!name.IsEmpty()) { string first = name.Substring(0, 1); first.ToUpper(); name = first + name.Substring(1, name.Length() - 1); }
  return name;
 }
 static string Resolve(ResourceName world)
 {
  array<ResourceName> seen = {};
  for (int depth = 0; depth < 16 && !world.IsEmpty(); depth++)
  {
   if (seen.Contains(world)) break;
   seen.Insert(world);
   Resource resource = BaseContainerTools.LoadContainer(world);
   if (!resource || !resource.IsValid()) break;
   BaseContainer container = resource.GetResource().ToBaseContainer();
   if (!container) break;
   ResourceName parent;
   if (!container.Get("Parent", parent) || parent.IsEmpty() || seen.Contains(parent)) break;
   world = parent;
  }
  return Label(world);
 }
}

// Scenario selection still loads the terrain's header. Advertise the configured
// Escape mode through the native DS name callback, then update local metadata
// only when the authority actually starts Escape or its bootstrap reaches a client.
modded class ArmaReforgerScripted
{
 protected ref SCR_MissionHeader m_ERRPresentedTerrainHeader;
 protected string m_ERROriginalTerrainTitle;
 protected string m_ERROriginalTerrainGameMode;
 protected string m_ERRAppliedTerrainTitle;
 protected string m_ERRTitleWorld;
 protected string m_ERRTerrainLabel;
 protected BaseWorld m_ERRPresentedTerrainWorld;
 protected bool m_ERRTitleLoaded;
 protected float m_ERRTerrainPresentationWait;

 string ERR_TerrainMissionTitle(SCR_MissionHeader header)
 {
  string world = header.GetWorldResourceName();
  string loadedWorld = GetWorldFile();
  // After the actual Escape bootstrap, the running world wins over a stale
  // source header. Before that, do not label an incoming scenario with the
  // outgoing world's terrain. A saved next-round selection is never consulted.
  bool confirmed = m_ERRPresentedTerrainHeader == header && m_ERRPresentedTerrainWorld && m_ERRPresentedTerrainWorld == GetWorld();
  if (confirmed && !loadedWorld.IsEmpty()) world = loadedWorld;
  bool loaded = GetWorld() && GetWorld().GetNumSubScenes() > 0 && !world.IsEmpty() && ERR_TerrainDisplayName.Path(loadedWorld) == ERR_TerrainDisplayName.Path(world);
  if (loaded)
   world = GetWorld().GetSubSceneName(0);
  if (world != m_ERRTitleWorld || loaded != m_ERRTitleLoaded)
  {
   m_ERRTitleWorld = world; m_ERRTitleLoaded = loaded;
   m_ERRTerrainLabel = ERR_TerrainDisplayName.Resolve(world);
  }
  string title = "Co10 Escape Reintroduced";
  if (!m_ERRTerrainLabel.IsEmpty()) title += " - " + m_ERRTerrainLabel;
  return title;
 }

 override string GetMissionName()
 {
  SCR_MissionHeader header = SCR_MissionHeader.Cast(GetMissionHeader());
  // Our own presentation has already set GameMode=Escape. It still needs to
  // follow the running terrain when the root scene becomes available later.
  if (header && header == m_ERRPresentedTerrainHeader)
  {
   ERR_RefreshTerrainPresentation();
   return header.m_sName;
  }
  // The engine requests this before the dedicated world has loaded. Preserve
  // authored Escape names and explicit opt-outs; do not change the scenario ID.
  if (Replication.IsServer() && header && header.m_sGameMode != "Escape" && m_ERRTerrainStage != ERR_TerrainTakeoverStage.FAILED && ERR_TerrainTakeoverSettings.Enabled())
   return ERR_TerrainMissionTitle(header);
  return super.GetMissionName();
 }

 void ERR_PresentTerrainTakeover()
 {
  SCR_MissionHeader header = SCR_MissionHeader.Cast(GetMissionHeader());
  if (!header || SCR_Global.IsEditMode()) return;
  if (m_ERRPresentedTerrainHeader == header && m_ERRPresentedTerrainWorld == GetWorld())
  { ERR_RefreshTerrainPresentation(); return; }
  ERR_RestoreTerrainPresentation();
  m_ERRPresentedTerrainHeader = header;
  m_ERRPresentedTerrainWorld = GetWorld();
  m_ERROriginalTerrainTitle = header.m_sName;
  m_ERROriginalTerrainGameMode = header.m_sGameMode;
  m_ERRAppliedTerrainTitle = ERR_TerrainMissionTitle(header);
  header.m_sName = m_ERRAppliedTerrainTitle;
  header.m_sGameMode = "Escape";
  PrintFormat("Reintroduced [terrain presentation]: mission=%1; source scenario=%2", header.m_sName, header.GetHeaderResourceName());
 }

 void ERR_RefreshTerrainPresentation()
 {
  SCR_MissionHeader header = SCR_MissionHeader.Cast(GetMissionHeader());
  if (!header || header != m_ERRPresentedTerrainHeader || SCR_Global.IsEditMode()) return;
  if (m_ERRPresentedTerrainWorld != GetWorld() || header.m_sName != m_ERRAppliedTerrainTitle) return;
  string title = ERR_TerrainMissionTitle(header);
  if (title == m_ERRAppliedTerrainTitle) return;
  m_ERRAppliedTerrainTitle = title; header.m_sName = title;
  PrintFormat("Reintroduced [terrain presentation]: refreshed mission=%1; loaded world=%2", title, GetWorldFile());
 }

 string ERR_TerrainLoadingTitle(SCR_MissionHeader requested)
 {
  if (!requested) return "";
  SCR_MissionHeader header = SCR_MissionHeader.Cast(GetMissionHeader());
  // The loading screen can hold a separate copy of the same scenario header.
  // Never replace another incoming scenario's title with the outgoing world.
  if (!header || header != m_ERRPresentedTerrainHeader || m_ERRPresentedTerrainWorld != GetWorld()) return requested.m_sName;
  if (requested != header && (requested.GetHeaderResourceName().IsEmpty() || requested.GetHeaderResourceName() != header.GetHeaderResourceName())) return requested.m_sName;
  ERR_RefreshTerrainPresentation();
  return header.m_sName;
 }

 override void OnUpdate(BaseWorld world, float timeslice)
 {
  super.OnUpdate(world, timeslice);
  if (!m_ERRPresentedTerrainHeader) return;
  m_ERRTerrainPresentationWait += timeslice;
  if (m_ERRTerrainPresentationWait < 0.5) return;
  m_ERRTerrainPresentationWait = 0;
  ERR_RefreshTerrainPresentation();
 }

 protected void ERR_RestoreTerrainPresentation()
 {
  if (m_ERRPresentedTerrainHeader)
  {
   // A later change by another system belongs to that system.
   if (m_ERRPresentedTerrainHeader.m_sName == m_ERRAppliedTerrainTitle) m_ERRPresentedTerrainHeader.m_sName = m_ERROriginalTerrainTitle;
   if (m_ERRPresentedTerrainHeader.m_sGameMode == "Escape") m_ERRPresentedTerrainHeader.m_sGameMode = m_ERROriginalTerrainGameMode;
  }
  m_ERRPresentedTerrainHeader = null;
  m_ERRPresentedTerrainWorld = null;
  m_ERROriginalTerrainTitle = "";
  m_ERROriginalTerrainGameMode = "";
  m_ERRAppliedTerrainTitle = "";
 }

 override protected void OnBeforeWorldCleanup()
 {
  ERR_RestoreTerrainPresentation();
  m_ERRTitleWorld = ""; m_ERRTerrainLabel = ""; m_ERRTitleLoaded = false; m_ERRTerrainPresentationWait = 0;
  super.OnBeforeWorldCleanup();
 }
}

// Vanilla copies the header title once, before the replicated Escape bootstrap
// may arrive. Refresh only the title; do not recreate loading widgets/hints.
modded class SCR_LoadingScreenComponent
{
 protected ref SCR_MissionHeader m_ERRLoadingTerrainHeader;
 protected string m_ERRLoadingTerrainTitle;
 protected float m_ERRLoadingTerrainWait;

 override void OnMissionDataRetrieved(SCR_MissionHeader header)
 {
  super.OnMissionDataRetrieved(header);
  m_ERRLoadingTerrainHeader = header;
  m_ERRLoadingTerrainTitle = ""; m_ERRLoadingTerrainWait = 0;
  ERR_RefreshLoadingTerrainTitle();
 }
 protected void ERR_RefreshLoadingTerrainTitle()
 {
  if (!m_ERRLoadingTerrainHeader || !m_Widgets || !m_Widgets.m_wTitle || !GetGame()) return;
  string title = GetGame().ERR_TerrainLoadingTitle(m_ERRLoadingTerrainHeader);
  if (title == m_ERRLoadingTerrainTitle) return;
  m_ERRLoadingTerrainTitle = title;
  m_Widgets.m_wTitle.SetText(title);
 }
 override void Update(float timeSlice, float progress = 0, float minDurationRatio = 0)
 {
  super.Update(timeSlice, progress, minDurationRatio);
  m_ERRLoadingTerrainWait += timeSlice;
  if (m_ERRLoadingTerrainWait < 0.5) return;
  m_ERRLoadingTerrainWait = 0;
  ERR_RefreshLoadingTerrainTitle();
 }
}
