#include "MetroidPrime/CStateManager.hpp"

#include "MetroidPrime/CControlMapper.hpp"
#include "MetroidPrime/CEnvFxManager.hpp"
#include "MetroidPrime/CGameCollision.hpp"
#include "MetroidPrime/CGameLight.hpp"
#include "MetroidPrime/CMapWorldInfo.hpp"
#include "MetroidPrime/CRipple.hpp"
#include "MetroidPrime/CRumbleManager.hpp"
#include "MetroidPrime/CScriptMailbox.hpp"
#include "MetroidPrime/CStateManagerContainer.hpp"
#include "MetroidPrime/CWorld.hpp"
#include "MetroidPrime/GameObjectLists.hpp"
#include "MetroidPrime/Player/CPlayer.hpp"
#include "MetroidPrime/Player/CPlayerState.hpp"
#include "MetroidPrime/Player/CWorldLayerState.hpp"
#include "MetroidPrime/Player/CWorldTransManager.hpp"
#include "MetroidPrime/ScriptObjects/CScriptMazeNode.hpp"

#include "Collision/CCollisionPrimitive.hpp"
#include "Collision/CRayCastResult.hpp"
#include "Kyoto/Basics/RAssertDolphin.hpp"
#include "Kyoto/CARAMManager.hpp"
#include "Kyoto/CARAMToken.hpp"
#include "Kyoto/CTimeProvider.hpp"
#include "Kyoto/Graphics/CLight.hpp"
#include "MetaRender/CCubeRenderer.hpp"

#include "rstl/algorithm.hpp"

extern "C" {
void sub_8036ccfc();
}

namespace {
class area_sorter {
public:
  area_sorter(CVector3f ref, TAreaId aid) : reference(ref), visAreaId(aid) {}

  bool operator()(const CGameArea* a, const CGameArea* b) const;

private:
  CVector3f reference;
  TAreaId visAreaId;
};

bool area_sorter::operator()(const CGameArea* a, const CGameArea* b) const {
  if (a->GetId() == b->GetId()) {
    return false;
  }
  if (visAreaId == a->GetId()) {
    return false;
  }
  if (visAreaId == b->GetId()) {
    return true;
  }
  return CVector3f::Dot(reference, a->GetAABB().GetCenterPoint()) >
         CVector3f::Dot(reference, b->GetAABB().GetCenterPoint());
}

} // namespace

class CLightPredicate {
public:
  bool operator()(const CLight& a, const CLight& b) const {
    if (a.GetPriority() > b.GetPriority()) {
      return true;
    } else if (a.GetPriority() == b.GetPriority()) {
      return a.GetIntensity() > b.GetIntensity();
    } else {
      return false;
    }
  }
};

CStateManager::CStateManager(const rstl::ncrc_ptr< CScriptMailbox >& mailbox,
                             const rstl::ncrc_ptr< CMapWorldInfo >& mwInfo,
                             const rstl::ncrc_ptr< CPlayerState >& playerState,
                             const rstl::ncrc_ptr< CWorldTransManager >& wtMgr,
                             const rstl::ncrc_ptr< CWorldLayerState >& layerState)
: x0_nextFreeIndex(0)
, x4_objectIndexArray(0)
, x808_objectLists(rstl::auto_ptr< CObjectList >())

, x86c_stateManagerContainer(rs_new CStateManagerContainer())

, x870_cameraManager(&x86c_stateManagerContainer->x0_cameraManager)
, x874_sortedListManager(&x86c_stateManagerContainer->x3c0_sortedListManager)
, x878_weaponMgr(&x86c_stateManagerContainer->xe3d8_weaponManager)
, x87c_fluidPlaneManager(&x86c_stateManagerContainer->xe3ec_fluidPlaneManager)
, x880_envFxManager(&x86c_stateManagerContainer->xe510_envFxManager)
, x884_actorModelParticles(&x86c_stateManagerContainer->xf168_actorModelParticles)
, x88c_rumbleManager(&x86c_stateManagerContainer->xf250_rumbleManager)

, x8b8_playerState(playerState)
, x8bc_mailbox(mailbox)
, x8c0_mapWorldInfo(mwInfo)
, x8c4_worldTransManager(wtMgr)
, x8c8_worldLayerState(layerState)
, x8cc_nextAreaId(0)
, x8d0_prevAreaId(kInvalidAreaId)
, x8d4_inputFrameIdx(0)
, x8d8_updateFrameIdx(0)
, x8dc_objectDrawToken(0)

, x8f0_shadowTex(gpSimplePool->GetObj("DefaultShadow"))
, x8fc_random(0)
, x900_random(nullptr)
, x904_gameState(kGS_Running)
, xb3c_initPhase(kIP_LoadWorld)

// based on map, uses the call with the count.
, xb84_camFilterPasses(CCameraFilterPass())

// TODO: should not be inlined, but the constructor above is inlined
, xd14_camBlurPasses(kCFS_Max, CCameraBlurPass())

, xeec_hintIdx(-1)
, xef0_hintPeriods(0)
, xef4_pendingScreenTex()
, xf08_pauseHudMessage(kInvalidAssetId)

, xf0c_escapeTimer(0.0f)
, xf10_escapeTotalTime(0.0f)
, xf14_curTimeMod900(0.0f)
, xf18_bossId(kInvalidUniqueId)
, xf1c_totalBossEnergy(0.0f)

, xf20_bossStringIdx(0)
, xf24_thermColdScale1(0.0f)
, xf28_thermColdScale2(0.0f)

, xf2c_viewportScaleX(1.f)
, xf30_viewportScaleY(1.f)
, xf34_thermalFlag(kTD_Bypass)

, xf38_skipCineSpecialFunc(kInvalidUniqueId)
, xf6c_playerActorHead(kInvalidUniqueId)
, xf70_currentMaze(nullptr)
, xf74_lastTrigger(kInvalidUniqueId)
, xf76_lastRelay(kInvalidUniqueId)

, xf78_hudMessageTime(0.0f)
, xf7c_projectedShadow(nullptr)
, xf80_hudMessageFrameCount(0)

, xf84_(kInvalidAssetId)
, xf88_(kInvalidAssetId)
, xf8c_(0.0f)
, xf90_deferredTransition(kSMT_InGame)

, xf94_24_readyToRender(false)
, xf94_25_quitGame(false)
, xf94_26_generatingObject(false)
, xf94_27_inMapScreen(false)
, xf94_28_inSaveUI(false)
, xf94_29_cinematicPause(false)
, xf94_30_fullThreat(false)

{
  x808_objectLists[0] = rs_new CObjectList(kOL_All);
  x808_objectLists[1] = rs_new CActorList();
  x808_objectLists[2] = rs_new CPhysicsActorList();
  x808_objectLists[3] = rs_new CGameCameraList();
  x808_objectLists[4] = rs_new CGameLightList();
  x808_objectLists[5] = rs_new CListeningAiList();
  x808_objectLists[6] = rs_new CAiWaypointList();
  x808_objectLists[7] = rs_new CPlatformAndDoorList();

  gpRender->SetDrawableCallback(RendererDrawCallback, this);
  CMemory::SetOutOfMemoryCallback(MemoryAllocatorAllocationFailedCallback, this);

  x90c_loaderFuncs.resize(x90c_loaderFuncs.capacity());
  x90c_loaderFuncs[kST_Trigger] = ScriptLoader::LoadTrigger;
  x90c_loaderFuncs[kST_DamageableTrigger] = ScriptLoader::LoadDamageableTrigger;
  x90c_loaderFuncs[kST_Actor] = ScriptLoader::LoadActor;
  x90c_loaderFuncs[kST_Enemy] = ScriptLoader::LoadEnemy;
  x90c_loaderFuncs[kST_Waypoint] = ScriptLoader::LoadWaypoint;
  x90c_loaderFuncs[kST_Door] = ScriptLoader::LoadDoor;
  x90c_loaderFuncs[kST_Effect] = ScriptLoader::LoadEffect;
  x90c_loaderFuncs[kST_Timer] = ScriptLoader::LoadTimer;
  x90c_loaderFuncs[kST_Counter] = ScriptLoader::LoadCounter;
  x90c_loaderFuncs[kST_Sound] = ScriptLoader::LoadSound;
  x90c_loaderFuncs[kST_Platform] = ScriptLoader::LoadPlatform;
  x90c_loaderFuncs[kST_Generator] = ScriptLoader::LoadGenerator;
  x90c_loaderFuncs[kST_Dock] = ScriptLoader::LoadDock;
  x90c_loaderFuncs[kST_Camera] = ScriptLoader::LoadCamera;
  x90c_loaderFuncs[kST_CameraWaypoint] = ScriptLoader::LoadCameraWaypoint;
  x90c_loaderFuncs[kST_NewIntroBoss] = ScriptLoader::LoadNewIntroBoss;
  x90c_loaderFuncs[kST_SpawnPoint] = ScriptLoader::LoadSpawnPoint;
  x90c_loaderFuncs[kST_CameraHint] = ScriptLoader::LoadCameraHint;
  x90c_loaderFuncs[kST_CameraHintTrigger] = ScriptLoader::LoadCameraHintTrigger;
  x90c_loaderFuncs[kST_Pickup] = ScriptLoader::LoadPickup;
  x90c_loaderFuncs[kST_Pickup] = ScriptLoader::LoadJumpPoint;
  x90c_loaderFuncs[kST_MemoryRelay] = ScriptLoader::LoadMemoryRelay;
  x90c_loaderFuncs[kST_RandomRelay] = ScriptLoader::LoadRandomRelay;
  x90c_loaderFuncs[kST_Relay] = ScriptLoader::LoadRelay;

  x90c_loaderFuncs[kST_Beetle] = ScriptLoader::LoadBeetle;
  x90c_loaderFuncs[kST_HUDMemo] = ScriptLoader::LoadHUDMemo;
  x90c_loaderFuncs[kST_CameraFilterKeyframe] = ScriptLoader::LoadCameraFilterKeyframe;
  x90c_loaderFuncs[kST_CameraBlurKeyframe] = ScriptLoader::LoadCameraBlurKeyframe;
  x90c_loaderFuncs[kST_Debris] = ScriptLoader::LoadDebris;
  x90c_loaderFuncs[kST_CameraShaker] = ScriptLoader::LoadCameraShaker;
  x90c_loaderFuncs[kST_ActorKeyframe] = ScriptLoader::LoadActorKeyframe;
  x90c_loaderFuncs[kST_AIKeyframe] = ScriptLoader::LoadAIKeyframe;
  x90c_loaderFuncs[kST_Water] = ScriptLoader::LoadWater;
  x90c_loaderFuncs[kST_Steam] = ScriptLoader::LoadSteam;
  x90c_loaderFuncs[kST_Ripple] = ScriptLoader::LoadRipple;
  x90c_loaderFuncs[kST_Warwasp] = ScriptLoader::LoadWarWasp;
  x90c_loaderFuncs[kST_MapStation] = ScriptLoader::LoadMapStation;
  x90c_loaderFuncs[kST_SpacePirate] = ScriptLoader::LoadSpacePirate;
  x90c_loaderFuncs[kST_CoverPoint] = ScriptLoader::LoadCoverPoint;
  x90c_loaderFuncs[kST_SpiderBallWaypoint] = ScriptLoader::LoadSpiderBallWaypoint;
  x90c_loaderFuncs[kST_BloodFlower] = ScriptLoader::LoadBloodFlower;
  x90c_loaderFuncs[kST_PathCamera] = ScriptLoader::LoadPathCamera;
  x90c_loaderFuncs[kST_GrapplePoint] = ScriptLoader::LoadGrapplePoint;
  x90c_loaderFuncs[kST_FlickerBat] = ScriptLoader::LoadFlickerBat;
  x90c_loaderFuncs[kST_ChozoGhost] = ScriptLoader::LoadChozoGhost;
  x90c_loaderFuncs[kST_PuddleSpore] = ScriptLoader::LoadPuddleSpore;
  x90c_loaderFuncs[kST_DebugCameraWaypoint] = ScriptLoader::LoadDebugCameraWaypoint;
  x90c_loaderFuncs[kST_SpiderBallAttractionSurface] = ScriptLoader::LoadSpiderBallAttractionSurface;
  x90c_loaderFuncs[kST_PuddleToadGamma] = ScriptLoader::LoadPuddleToadGamma;
  x90c_loaderFuncs[kST_FireFlea] = ScriptLoader::LoadFireFlea;
  x90c_loaderFuncs[kST_DistanceFog] = ScriptLoader::LoadDistanceFog;
  x90c_loaderFuncs[kST_DockAreaChange] = ScriptLoader::LoadDockAreaChange;
  x90c_loaderFuncs[kST_ActorRotate] = ScriptLoader::LoadActorRotate;
  x90c_loaderFuncs[kST_SpecialFunction] = ScriptLoader::LoadSpecialFunction;
  x90c_loaderFuncs[kST_Metaree] = ScriptLoader::LoadMetaree;
  x90c_loaderFuncs[kST_SpankWeed] = ScriptLoader::LoadSpankWeed;
  x90c_loaderFuncs[kST_Parasite] = ScriptLoader::LoadParasite;
  x90c_loaderFuncs[kST_PlayerHint] = ScriptLoader::LoadPlayerHint;
  x90c_loaderFuncs[kST_Ripper] = ScriptLoader::LoadRipper;
  x90c_loaderFuncs[kST_PickupGenerator] = ScriptLoader::LoadPickupGenerator;
  x90c_loaderFuncs[kST_PointOfInterest] = ScriptLoader::LoadPointOfInterest;
  x90c_loaderFuncs[kST_Drone] = ScriptLoader::LoadDrone;
  x90c_loaderFuncs[kST_Metroid] = ScriptLoader::LoadMetroid;
  x90c_loaderFuncs[kST_DebrisExtended] = ScriptLoader::LoadDebrisExtended;
  x90c_loaderFuncs[kST_BallTrigger] = ScriptLoader::LoadBallTrigger;
  x90c_loaderFuncs[kST_TargetingPoint] = ScriptLoader::LoadTargetingPoint;
  x90c_loaderFuncs[kST_EMPulse] = ScriptLoader::LoadEMPulse;
  x90c_loaderFuncs[kST_IceSheegoth] = ScriptLoader::LoadIceSheegoth;
  x90c_loaderFuncs[kST_PlayerActor] = ScriptLoader::LoadPlayerActor;
  x90c_loaderFuncs[kST_Flaahgra] = ScriptLoader::LoadFlaahgra;
  x90c_loaderFuncs[kST_AreaAttributes] = ScriptLoader::LoadAreaAttributes;
  x90c_loaderFuncs[kST_FishCloud] = ScriptLoader::LoadFishCloud;
  x90c_loaderFuncs[kST_FishCloudModifier] = ScriptLoader::LoadFishCloudModifier;
  x90c_loaderFuncs[kST_VisorFlare] = ScriptLoader::LoadVisorFlare;
  x90c_loaderFuncs[kST_WorldTeleporter] = ScriptLoader::LoadWorldTeleporter;
  x90c_loaderFuncs[kST_VisorGoo] = ScriptLoader::LoadVisorGoo;
  x90c_loaderFuncs[kST_JellyZap] = ScriptLoader::LoadJellyZap;
  x90c_loaderFuncs[kST_ControllerAction] = ScriptLoader::LoadControllerAction;
  x90c_loaderFuncs[kST_Switch] = ScriptLoader::LoadSwitch;
  x90c_loaderFuncs[kST_PlayerStateChange] = ScriptLoader::LoadPlayerStateChange;
  x90c_loaderFuncs[kST_Thardus] = ScriptLoader::LoadThardus;
  x90c_loaderFuncs[kST_SaveStation] = ScriptLoader::LoadSaveStation;
  x90c_loaderFuncs[kST_WallCrawlerSwarm] = ScriptLoader::LoadWallCrawlerSwarm;
  x90c_loaderFuncs[kST_AIJumpPoint] = ScriptLoader::LoadAiJumpPoint;
  x90c_loaderFuncs[kST_FlaahgraTentacle] = ScriptLoader::LoadFlaahgraTentacle;
  x90c_loaderFuncs[kST_RoomAcoustics] = ScriptLoader::LoadRoomAcoustics;
  x90c_loaderFuncs[kST_FlyingPirate] = ScriptLoader::LoadFlyingPirate;
  x90c_loaderFuncs[kST_ColorModulate] = ScriptLoader::LoadColorModulate;
  x90c_loaderFuncs[kST_ThardusRockProjectile] = ScriptLoader::LoadThardusRockProjectile;
  x90c_loaderFuncs[kST_Midi] = ScriptLoader::LoadMidi;
  x90c_loaderFuncs[kST_StreamedAudio] = ScriptLoader::LoadStreamedAudio; // Or music?
  x90c_loaderFuncs[kST_WorldTeleporterToo] = ScriptLoader::LoadWorldTeleporter;
  x90c_loaderFuncs[kST_Repulsor] = ScriptLoader::LoadRepulsor;
  x90c_loaderFuncs[kST_GunTurret] = ScriptLoader::LoadGunTurret;
  x90c_loaderFuncs[kST_FogVolume] = ScriptLoader::LoadFogVolume;
  x90c_loaderFuncs[kST_Babygoth] = ScriptLoader::LoadBabygoth;
  x90c_loaderFuncs[kST_Eyeball] = ScriptLoader::LoadEyeball;
  x90c_loaderFuncs[kST_RadialDamage] = ScriptLoader::LoadRadialDamage;
  x90c_loaderFuncs[kST_CameraPitchVolume] =
      ScriptLoader::LoadCameraPitchVolume; // LoadScriptCameraPitchVolume
  x90c_loaderFuncs[kST_ElitePirate] = ScriptLoader::LoadElitePirate;
  x90c_loaderFuncs[kST_MetroidBeta] = ScriptLoader::LoadMetroidBeta;
  x90c_loaderFuncs[kST_EnvFxDensityController] = ScriptLoader::LoadEnvFxDensityController;
  x90c_loaderFuncs[kST_Magdolite] = ScriptLoader::LoadMagdolite;
  x90c_loaderFuncs[kST_TeamAIMgr] = ScriptLoader::LoadTeamAIMgr;
  x90c_loaderFuncs[kST_SnakeWeedSwarm] = ScriptLoader::LoadSnakeWeedSwarm;
  x90c_loaderFuncs[kST_ActorContraption] = ScriptLoader::LoadActorContraption;
  x90c_loaderFuncs[kST_AtomicAlpha] = ScriptLoader::LoadAtomicAlpha;
  x90c_loaderFuncs[kST_Oculus] = ScriptLoader::LoadOculus;
  x90c_loaderFuncs[kST_Geemer] = ScriptLoader::LoadGeemer;
  x90c_loaderFuncs[kST_SpindleCamera] = ScriptLoader::LoadSpindleCamera;
  x90c_loaderFuncs[kST_RumbleEffect] = ScriptLoader::LoadRumbleEffect;
  x90c_loaderFuncs[kST_AmbientAI] = ScriptLoader::LoadAmbientAI;
  x90c_loaderFuncs[0x76] = NULL;
  x90c_loaderFuncs[kST_AtomicBeta] = ScriptLoader::LoadAtomicBeta;
  x90c_loaderFuncs[kST_IceZoomer] = ScriptLoader::LoadIceZoomer;
  x90c_loaderFuncs[kST_Puffer] = ScriptLoader::LoadPuffer;
  x90c_loaderFuncs[kST_Tryclops] = ScriptLoader::LoadTryclops;
  x90c_loaderFuncs[kST_Ridley] = ScriptLoader::LoadRidley;
  x90c_loaderFuncs[kST_Seedling] = ScriptLoader::LoadSeedling;
  x90c_loaderFuncs[kST_ThermalHeatFader] = ScriptLoader::LoadThermalHeatFader;
  x90c_loaderFuncs[0x7e] = NULL;
  x90c_loaderFuncs[kST_Burrower] = ScriptLoader::LoadBurrower;
  x90c_loaderFuncs[kST_ScriptBeam] = ScriptLoader::LoadBeam;
  x90c_loaderFuncs[0x80] = NULL;
  x90c_loaderFuncs[kST_MetroidPrimeStage1] = ScriptLoader::LoadMetroidPrimeStage1;
  x90c_loaderFuncs[0x1f] = NULL;
  x90c_loaderFuncs[0x23] = NULL;
  x90c_loaderFuncs[kST_WorldLightFader] = ScriptLoader::LoadWorldLightFader;
  x90c_loaderFuncs[kST_MetroidPrimeStage2] = ScriptLoader::LoadMetroidPrimeEssence;
  x90c_loaderFuncs[kST_MazeNode] = ScriptLoader::LoadMazeNode;
  x90c_loaderFuncs[kST_OmegaPirate] = ScriptLoader::LoadOmegaPirate;
  x90c_loaderFuncs[kST_PhazonPool] = ScriptLoader::LoadPhazonPool;
  x90c_loaderFuncs[kST_PhazonHealingNodule] = ScriptLoader::LoadPhazonHealingNodule;
  x90c_loaderFuncs[kST_NewCameraShaker] = ScriptLoader::LoadNewCameraShaker;
  x90c_loaderFuncs[kST_ShadowProjector] = ScriptLoader::LoadShadowProjector;
  x90c_loaderFuncs[kST_EnergyBall] = ScriptLoader::LoadEnergyBall;

  CGameCollision::InitCollision();
  CMemory::OffsetFakeStatics(x808_objectLists.size() * sizeof(CObjectList) + 0x11c);
  ControlMapper::ResetCommandFilters();
  x8f0_shadowTex.Lock();
}

CStateManager::~CStateManager() {
  CMemory::OffsetFakeStatics(
      -(x808_objectLists.size() * sizeof(CObjectList) + 0x11c)); // TODO what is this 11c?
  x88c_rumbleManager->HardStopAll();
  x880_envFxManager->Cleanup();

  x900_random = &x8fc_random;

  CObjectList& list = *x808_objectLists[0];
  ClearGraveyard();
  for (int i = 0; i != kMaxObjects; ++i) {
    CEntity* ent = list[i];
    if (ent && ent->GetUniqueId() != x84c_player->GetUniqueId()) {
      ent->AcceptScriptMsg(kSM_Deleted, kInvalidUniqueId, *this);
      RemoveObject(ent->GetUniqueId());
      delete ent;
    }
  }
  ClearGraveyard();
  x84c_player->AcceptScriptMsg(kSM_Deleted, kInvalidUniqueId, *this);
  RemoveObject(x84c_player->GetUniqueId());
  delete x84c_player;
  x84c_player = nullptr;
  CCollisionPrimitive::Uninitialize();

  CMemory::SetOutOfMemoryCallback(nullptr, nullptr);
}

TUniqueId CStateManager::AllocateUniqueId() {
  const ushort lastIndex = x0_nextFreeIndex;
  ushort ourIndex;
  do {
    ourIndex = x0_nextFreeIndex;
    x0_nextFreeIndex = (ourIndex + 1) & 0x3ff;
    if (x0_nextFreeIndex == lastIndex) {
      rs_debugger_printf("Object list full!");
    }
  } while (ObjectListById(kOL_All).GetObjectByIndex(ourIndex) != nullptr);

  x4_objectIndexArray[ourIndex] = (x4_objectIndexArray[ourIndex] + 1) & 0x3f;
  if (TUniqueId(x4_objectIndexArray[ourIndex], ourIndex) == kInvalidUniqueId) {
    x4_objectIndexArray[ourIndex] = 0;
  }

  return TUniqueId(x4_objectIndexArray[ourIndex], ourIndex);
}

CRayCastResult CStateManager::RayStaticIntersection(const CVector3f& pos, const CVector3f& dir,
                                                    float length,
                                                    const CMaterialFilter& filter) const {
  return CGameCollision::RayStaticIntersection(*this, pos, dir, length, filter);
}

void CStateManager::RemoveObject(TUniqueId id) {}

void CStateManager::ClearGraveyard() {}

const bool CStateManager::MemoryAllocatorAllocationFailedCallback(const void* obj, unsigned int) {
  return static_cast< CStateManager* >(const_cast< void* >(obj))->SwapOutAllPossibleMemory();
}

bool CStateManager::SwapOutAllPossibleMemory() {
  sub_8036ccfc();
  CARAMManager::WaitForAllDMAsToComplete();
  CARAMToken::UpdateAllDMAs();
  return true;
}

void CStateManager::RendererDrawCallback(const void*, const void*, int) {}

void CStateManager::DrawWorld() const {

  const CTimeProvider timeProvider(xf14_curTimeMod900);
  const CViewport backupViewport = CGraphics::GetViewport();

  /* Area camera is in (not necessarily player) */
  const TAreaId visAreaId = GetVisAreaId();

  x850_world->TouchSky();

  const CFrustumPlanes frustum = SetupViewForDraw(CGraphics::GetViewport());
  const CTransform4f backupViewMatrix = CGraphics::GetViewMatrix();

  int areaCount = 0;
  const CGameArea* areaArr[10];

  CGameArea::CConstChainIterator it = x850_world->GetChainHead(CWorld::kC_Alive);
  CGameArea::CConstChainIterator end = x850_world->GetAliveAreasEnd();
  for (; it != end; ++it) {
    if (areaCount == 10) {
      break;
    }
    CGameArea::EOcclusionState occState = CGameArea::kOS_Occluded;
    if (it->IsLoaded()) {
      occState = it->GetOcclusionState();
    }
    if (occState == CGameArea::kOS_Visible) {
      areaArr[areaCount++] = &*it;
    }
  }

  // rstl::sort(&areaArr[0], &areaArr[areaCount], area_sorter(CGraphics::GetViewPoint(),
  // visAreaId));
}

void CStateManager::BuildDynamicLightListForWorld() {
  if (x8b8_playerState->GetActiveVisor(*this) == CPlayerState::kPV_Thermal) {
    x8e0_dynamicLights = rstl::vector< CLight >();
    return;
  }

  const CObjectList& list = GetObjectListById(kOL_GameLight);
  int listSize = list.size();
  if (listSize == 0) {
    return;
  }

  if (x8e0_dynamicLights.capacity() != listSize) {
    x8e0_dynamicLights = rstl::vector< CLight >();
    x8e0_dynamicLights.reserve(listSize);
  } else {
    x8e0_dynamicLights.clear();
  }

  for (int idx = list.GetFirstObjectIndex(); idx != -1; idx = list.GetNextObjectIndex(idx)) {
    const CGameLight* light = static_cast< const CGameLight* >(list[idx]);
    if (light && light->GetActive()) {
      const CLight& l = light->GetLight();
      if (l.GetIntensity() > FLT_EPSILON && l.GetRadius() > FLT_EPSILON) {
        // TODO: This shouldn't be inlined, but currently is.
        x8e0_dynamicLights.push_back(l);
      }
    }
  }
  rstl::sort(x8e0_dynamicLights.begin(), x8e0_dynamicLights.end(), CLightPredicate());
}
