#include "Runtime/MP1/CInGameGuiManager.hpp"

#include <algorithm>
#include <array>

#include "Runtime/CDependencyGroup.hpp"
#include "Runtime/CSimplePool.hpp"
#include "Runtime/GameGlobalObjects.hpp"
#include "Runtime/Audio/CSfxManager.hpp"
#include "Runtime/AutoMapper/CAutoMapper.hpp"
#include "Runtime/Camera/CFirstPersonCamera.hpp"
#include "Runtime/Graphics/CBooRenderer.hpp"
#include "Runtime/GuiSys/CGuiCamera.hpp"
#include "Runtime/GuiSys/CGuiFrame.hpp"
#include "Runtime/GuiSys/CGuiModel.hpp"
#include "Runtime/GuiSys/CGuiWidgetDrawParms.hpp"
#include "Runtime/Input/CInputGenerator.hpp"
#include "Runtime/MP1/CSamusHud.hpp"
#include "Runtime/Particle/CGenDescription.hpp"
#include "Runtime/World/CPlayer.hpp"

#include "TCastTo.hpp" // Generated file, do not modify include path

namespace metaforce::MP1 {

constexpr std::array InGameGuiDGRPs{
    "InGameGui_DGRP"sv, "Ice_DGRP"sv,         "Phazon_DGRP"sv,         "Plasma_DGRP"sv,
    "Power_DGRP"sv,     "Wave_DGRP"sv,        "BallTransition_DGRP"sv, "GravitySuit_DGRP"sv,
    "Ice_Anim_DGRP"sv,  "Plasma_Anim_DGRP"sv, "PowerSuit_DGRP"sv,      "Power_Anim_DGRP"sv,
    "VariaSuit_DGRP"sv, "Wave_Anim_DGRP"sv,
};

constexpr std::array PauseScreenDGRPs{
    "InventorySuitPower_DGRP"sv,         "InventorySuitVaria_DGRP"sv,        "InventorySuitGravity_DGRP"sv,
    "InventorySuitPhazon_DGRP"sv,        "InventorySuitFusionPower_DGRP"sv,  "InventorySuitFusionVaria_DGRP"sv,
    "InventorySuitFusionGravity_DGRP"sv, "InventorySuitFusionPhazon_DGRP"sv, "SamusBallANCS_DGRP"sv,
    "SamusSpiderBallANCS_DGRP"sv,        "PauseScreenDontDump_DGRP"sv,       "PauseScreenDontDump_NoARAM_DGRP"sv,
    "PauseScreenTokens_DGRP"sv,
};

std::vector<TLockedToken<CDependencyGroup>> CInGameGuiManager::LockPauseScreenDependencies() {
  std::vector<TLockedToken<CDependencyGroup>> ret;
  ret.reserve(PauseScreenDGRPs.size());
  for (const auto& dgrp : PauseScreenDGRPs) {
    ret.emplace_back(g_SimplePool->GetObj(dgrp));
  }
  return ret;
}

bool CInGameGuiManager::CheckDGRPLoadComplete() const {
  const auto isLoaded = [](const auto& entry) { return entry.IsLoaded(); };

  return std::all_of(x5c_pauseScreenDGRPs.cbegin(), x5c_pauseScreenDGRPs.cend(), isLoaded) &&
         std::all_of(xc8_inGameGuiDGRPs.cbegin(), xc8_inGameGuiDGRPs.cend(), isLoaded);
}

void CInGameGuiManager::BeginStateTransition(EInGameGuiState state, CStateManager& stateMgr) {
  if (x1c0_nextState == state)
    return;

  x1bc_prevState = x1c0_nextState;
  x1c0_nextState = state;

  switch (state) {
  case EInGameGuiState::InGame: {
    CSfxManager::SetChannel(CSfxManager::ESfxChannels::Game);
    x4c_saveUI.reset();
    x38_autoMapper->UnmuteAllLoopedSounds();
    break;
  }
  case EInGameGuiState::PauseHUDMessage: {
    x44_messageScreen = std::make_unique<CMessageScreen>(x124_pauseGameHudMessage, x128_pauseGameHudTime);
    break;
  }
  case EInGameGuiState::PauseSaveGame: {
    x4c_saveUI = std::make_unique<CSaveGameScreen>(ESaveContext::InGame, g_GameState->GetCardSerial());
    break;
  }
  default: {
    if (x1bc_prevState >= EInGameGuiState::Zero && x1bc_prevState <= EInGameGuiState::InGame)
      x1f8_26_deferTransition = true;
    break;
  }
  }

  x3c_pauseScreenBlur->OnNewInGameGuiState(state, stateMgr);
  if (!x1f8_26_deferTransition)
    DoStateTransition(stateMgr);
}

void CInGameGuiManager::EnsureStates(CStateManager& stateMgr) {
  if (x1f8_26_deferTransition) {
    if (!x3c_pauseScreenBlur->IsGameDraw()) {
      DestroyAreaTextures(stateMgr);
      x1f8_26_deferTransition = false;
      DoStateTransition(stateMgr);
    }
  }
}

void CInGameGuiManager::DoStateTransition(CStateManager& stateMgr) {
  x34_samusHud->OnNewInGameGuiState(x1c0_nextState, stateMgr);
  x38_autoMapper->OnNewInGameGuiState(x1c0_nextState, stateMgr);

  bool needsLock;
  switch (x1c0_nextState) {
  case EInGameGuiState::PauseGame:
  case EInGameGuiState::PauseLogBook:
    if (!x48_pauseScreen) {
      const auto& pState = stateMgr.GetPlayerState();
      const CPlayerState::EPlayerSuit suit = pState->GetCurrentSuitRaw();
      int suitResIdx;
      if (pState->IsFusionEnabled()) {
        switch (suit) {
        case CPlayerState::EPlayerSuit::Phazon:
          suitResIdx = 7;
          break;
        case CPlayerState::EPlayerSuit::Gravity:
          suitResIdx = 6;
          break;
        case CPlayerState::EPlayerSuit::Varia:
          suitResIdx = 5;
          break;
        default:
          suitResIdx = 4;
          break;
        }
      } else {
        switch (suit) {
        case CPlayerState::EPlayerSuit::Phazon:
          suitResIdx = 3;
          break;
        case CPlayerState::EPlayerSuit::Gravity:
          suitResIdx = 2;
          break;
        case CPlayerState::EPlayerSuit::Varia:
          suitResIdx = 1;
          break;
        default:
          suitResIdx = 0;
          break;
        }
      }

      CPauseScreen::ESubScreen screen = x1c0_nextState == EInGameGuiState::PauseLogBook
                                            ? CPauseScreen::ESubScreen::LogBook
                                            : CPauseScreen::ESubScreen::Inventory;
      CDependencyGroup* suitGrp = x5c_pauseScreenDGRPs[suitResIdx].GetObj();
      x48_pauseScreen = std::make_unique<CPauseScreen>(screen, *suitGrp, *suitGrp);
    }
    [[fallthrough]];

  case EInGameGuiState::MapScreen:
  case EInGameGuiState::PauseSaveGame:
  case EInGameGuiState::PauseHUDMessage:
    needsLock = true;
    break;
  default:
    needsLock = false;
    break;
  }

  for (CToken& tok : xe8_pauseResources) {
    if (needsLock)
      tok.Lock();
    else
      tok.Unlock();
  }
}

void CInGameGuiManager::DestroyAreaTextures(CStateManager& stateMgr) {}

void CInGameGuiManager::TryReloadAreaTextures() {}

CInGameGuiManager::CInGameGuiManager(CStateManager& stateMgr, CArchitectureQueue& archQueue)
: x0_iggmPreLoad(g_SimplePool->GetObj("PreLoadIGGM_DGRP"))
, x1c_rand(1234)
, x20_faceplateDecor(stateMgr)
, x50_deathDot(g_SimplePool->GetObj("TXTR_DeathDot"))
, x5c_pauseScreenDGRPs(LockPauseScreenDependencies()) {
  x1e0_helmetVisMode = g_tweakGui->GetHelmetVisMode();
  x1e4_enableTargetingManager = g_tweakGui->GetEnableTargetingManager();
  x1e8_enableAutoMapper = g_tweakGui->GetEnableAutoMapper();
  x1ec_hudVisMode = g_tweakGui->GetHudVisMode();
  x1f0_enablePlayerVisor = g_tweakGui->GetEnablePlayerVisor();

  x1f4_visorStaticAlpha = stateMgr.GetPlayer().GetVisorStaticAlpha();

  xc8_inGameGuiDGRPs.reserve(InGameGuiDGRPs.size());
  for (const auto& dgrp : InGameGuiDGRPs) {
    xc8_inGameGuiDGRPs.emplace_back(g_SimplePool->GetObj(dgrp));
  }
}

bool CInGameGuiManager::CheckLoadComplete(CStateManager& stateMgr) {
  switch (x18_loadPhase) {
  case ELoadPhase::LoadDepsGroup: {
    if (!x0_iggmPreLoad.IsLoaded())
      return false;
    const auto& tags = x0_iggmPreLoad->GetObjectTagVector();
    x8_preLoadDeps.reserve(tags.size());
    for (const SObjectTag& tag : tags) {
      x8_preLoadDeps.push_back(g_SimplePool->GetObj(tag));
      x8_preLoadDeps.back().Lock();
    }
    x0_iggmPreLoad.Unlock();
    x18_loadPhase = ELoadPhase::PreLoadDeps;
    [[fallthrough]];
  }
  case ELoadPhase::PreLoadDeps: {
    for (CToken& tok : x8_preLoadDeps)
      if (!tok.IsLoaded())
        return false;
    x18_loadPhase = ELoadPhase::LoadDeps;
    x30_playerVisor = std::make_unique<CPlayerVisor>(stateMgr);
    x34_samusHud = std::make_unique<CSamusHud>(stateMgr);
    x38_autoMapper = std::make_unique<CAutoMapper>(stateMgr);
    x3c_pauseScreenBlur = std::make_unique<CPauseScreenBlur>();
    x40_samusReflection = std::make_unique<CSamusFaceReflection>(stateMgr);
    [[fallthrough]];
  }
  case ELoadPhase::LoadDeps: {
    if (!x38_autoMapper->CheckLoadComplete())
      return false;
    if (!x34_samusHud->CheckLoadComplete(stateMgr))
      return false;
    if (!x50_deathDot.IsLoaded())
      return false;
    if (!CheckDGRPLoadComplete())
      return false;
    x8_preLoadDeps.clear();

    CGuiFrame& baseHud = *x34_samusHud->x274_loadedFrmeBaseHud;
    x144_basewidget_automapper = baseHud.FindWidget("BaseWidget_AutoMapper");
    x148_model_automapper = static_cast<CGuiModel*>(baseHud.FindWidget("Model_AutoMapper"));
    x14c_basehud_camera = baseHud.GetFrameCamera();
    x150_basewidget_functional = baseHud.FindWidget("BaseWidget_Functional");

    x154_automapperRotate = zeus::CQuaternion(x144_basewidget_automapper->GetWorldTransform().basis);
    x164_automapperOffset = x144_basewidget_automapper->GetLocalPosition();
    x170_camRotate = zeus::CQuaternion();
    x180_camOffset =
        x14c_basehud_camera->GetLocalPosition() + zeus::CVector3f(0.f, 2.f, g_tweakAutoMapper->GetCamVerticalOffset());

    zeus::CMatrix3f mtx(x170_camRotate);
    x18c_mapCamXf = zeus::CTransform(mtx, x180_camOffset);

    BeginStateTransition(EInGameGuiState::InGame, stateMgr);
    x18_loadPhase = ELoadPhase::Done;
    [[fallthrough]];
  }
  case ELoadPhase::Done: {
    x34_samusHud->Touch();
    return true;
  }
  default:
    return false;
  }
}

void CInGameGuiManager::RefreshHudOptions() { x34_samusHud->RefreshHudOptions(); }

void CInGameGuiManager::OnNewPauseScreenState(CArchitectureQueue& archQueue) {
  if (x1c0_nextState != EInGameGuiState::PauseGame && x1c0_nextState != EInGameGuiState::PauseLogBook) {
    if (x48_pauseScreen && x48_pauseScreen->IsTransitioning())
      return;
    x48_pauseScreen.reset();
  }

  if (x1c0_nextState >= EInGameGuiState::Zero && x1c0_nextState <= EInGameGuiState::InGame) {
    if (x44_messageScreen)
      x44_messageScreen.reset();
    RefreshHudOptions();
  }
  x1bc_prevState = x1c0_nextState;
}

void CInGameGuiManager::UpdateAutoMapper(float dt, CStateManager& stateMgr) {
  x38_autoMapper->Update(dt, stateMgr);
  zeus::CTransform xf =
      x148_model_automapper->GetParent()->GetWorldTransform() * x144_basewidget_automapper->GetTransform();
  x154_automapperRotate = zeus::CQuaternion(xf.basis);
  x164_automapperOffset = xf.origin;

  x170_camRotate = zeus::CQuaternion(x14c_basehud_camera->GetWorldTransform().basis);
  x180_camOffset = x14c_basehud_camera->GetWorldTransform().basis[1] * 2.f + x14c_basehud_camera->GetWorldPosition() +
                   x14c_basehud_camera->GetWorldTransform().basis[2] * g_tweakAutoMapper->GetCamVerticalOffset();

  float frameLength = std::tan(zeus::degToRad(x14c_basehud_camera->GetProjection().xbc_fov) / 2.f) / 0.7f;
  float scaleX = frameLength * g_tweakAutoMapper->GetAutomapperScaleX();
  float scaleZ = frameLength * g_tweakAutoMapper->GetAutomapperScaleZ();
  if (x38_autoMapper->IsFullyOutOfMiniMapState()) {
    x148_model_automapper->SetO2WTransform(zeus::CTransform(x170_camRotate, x180_camOffset) *
                                           zeus::CTransform::Scale(scaleX, 1.f, scaleZ));
    x18c_mapCamXf =
        zeus::CTransform(x170_camRotate, x180_camOffset) * zeus::CTransform::Scale(frameLength, 1.f, frameLength);
    x148_model_automapper->SetColor(g_tweakAutoMapper->GetAutomapperWidgetColor());
  } else if (x38_autoMapper->IsFullyInMiniMapState()) {
    x148_model_automapper->SetO2WTransform(zeus::CTransform(x154_automapperRotate, x164_automapperOffset));
    x18c_mapCamXf = x148_model_automapper->GetWorldTransform();
    x148_model_automapper->SetColor(g_tweakAutoMapper->GetAutomapperWidgetMiniColor());
  } else {
    float t;
    if (x38_autoMapper->GetNextState() != CAutoMapper::EAutoMapperState::MiniMap)
      t = x38_autoMapper->GetInterp();
    else
      t = 1.f - x38_autoMapper->GetInterp();
    float st = t * (frameLength - 1.f) + 1.f;
    zeus::CQuaternion rotate = zeus::CQuaternion::slerp(x154_automapperRotate, x170_camRotate, t);
    zeus::CVector3f offset = x164_automapperOffset * (1.f - t) + x180_camOffset * t;
    x18c_mapCamXf = zeus::CTransform(rotate, offset) * zeus::CTransform::Scale(st, 1.f, st);
    x148_model_automapper->SetO2WTransform(
        zeus::CTransform(rotate, offset) *
        zeus::CTransform::Scale(t * (scaleX - 1.f) + 1.f, 1.f, t * (scaleZ - 1.f) + 1.f));
    x148_model_automapper->SetColor(zeus::CColor::lerp(g_tweakAutoMapper->GetAutomapperWidgetMiniColor(),
                                                       g_tweakAutoMapper->GetAutomapperWidgetColor(), t));
  }
}

void CInGameGuiManager::Update(CStateManager& stateMgr, float dt, CArchitectureQueue& archQueue, bool useHud) {
  EnsureStates(stateMgr);

  if (x1d8_onScreenTexAlpha == 0.f)
    x1dc_onScreenTexTok = TLockedToken<CTexture>();

  if (x1c4_onScreenTex.x0_id != stateMgr.GetPendingScreenTex().x0_id) {
    if (!x1dc_onScreenTexTok) {
      x1c4_onScreenTex = stateMgr.GetPendingScreenTex();
      if (x1c4_onScreenTex.x0_id.IsValid()) {
        x1dc_onScreenTexTok = g_SimplePool->GetObj({FOURCC('TXTR'), x1c4_onScreenTex.x0_id});
        x1d8_onScreenTexAlpha = FLT_EPSILON;
      }
    } else {
      if (!stateMgr.GetPendingScreenTex().x0_id.IsValid() &&
          stateMgr.GetPendingScreenTex().x4_origin == zeus::CVector2i(0, 0)) {
        x1c4_onScreenTex.x4_origin = stateMgr.GetPendingScreenTex().x4_origin;
        x1c4_onScreenTex.x0_id = {};
        x1d8_onScreenTexAlpha = 0.f;
      } else {
        x1d8_onScreenTexAlpha = std::max(0.f, x1d8_onScreenTexAlpha - dt);
      }
    }
  } else if (x1c4_onScreenTex.x0_id.IsValid() && x1dc_onScreenTexTok.IsLoaded()) {
    x1d8_onScreenTexAlpha = std::min(x1d8_onScreenTexAlpha + dt, 1.f);
  }

  if (useHud) {
    if (stateMgr.GetPlayer().GetVisorStaticAlpha() != x1f4_visorStaticAlpha) {
      if (TCastToPtr<CFirstPersonCamera>(stateMgr.GetCameraManager()->GetCurrentCamera(stateMgr))) {
        if (std::fabs(stateMgr.GetPlayer().GetVisorStaticAlpha() - x1f4_visorStaticAlpha) < 0.5f) {
          if (x1f4_visorStaticAlpha == 0.f)
            CSfxManager::SfxStart(SFXui_hud_reboot, 1.f, 0.f, false, 0x7f, false, kInvalidAreaId);
          else if (x1f4_visorStaticAlpha == 1.f)
            CSfxManager::SfxStart(SFXui_hud_shutdown, 1.f, 0.f, false, 0x7f, false, kInvalidAreaId);
        }
      }
    }
    x1f4_visorStaticAlpha = stateMgr.GetPlayer().GetVisorStaticAlpha();
    x20_faceplateDecor.Update(dt, stateMgr);
    x40_samusReflection->Update(dt, stateMgr, x1c_rand);
    if (x1f0_enablePlayerVisor)
      x30_playerVisor->Update(dt, stateMgr);
    if (x1f8_25_playerAlive)
      x34_samusHud->Update(dt, stateMgr, x1e0_helmetVisMode, x1ec_hudVisMode != EHudVisMode::Zero,
                           x1e4_enableTargetingManager);
  }

  if (x1e8_enableAutoMapper)
    UpdateAutoMapper(dt, stateMgr);

  x3c_pauseScreenBlur->Update(dt, stateMgr, !x140_);

  if (x4c_saveUI) {
    CIOWin::EMessageReturn ret = x4c_saveUI->Update(dt);
    if (ret != CIOWin::EMessageReturn::Normal) {
      x1f8_27_exitSaveUI = ret == CIOWin::EMessageReturn::Exit;
      BeginStateTransition(EInGameGuiState::InGame, stateMgr);
    }
  } else if (x44_messageScreen) {
    if (!x44_messageScreen->Update(dt, x3c_pauseScreenBlur->GetBlurAmt()))
      BeginStateTransition(EInGameGuiState::InGame, stateMgr);
  }

  if (x48_pauseScreen) {
    x48_pauseScreen->Update(dt, stateMgr, x1c_rand, archQueue);
    if (x1bc_prevState == x1c0_nextState) {
      if (x48_pauseScreen->ShouldSwitchToMapScreen())
        BeginStateTransition(EInGameGuiState::MapScreen, stateMgr);
      else if (x48_pauseScreen->ShouldSwitchToInGame())
        BeginStateTransition(EInGameGuiState::InGame, stateMgr);
    }
  }

  x34_samusHud->Touch();
  x30_playerVisor->Touch();
  x34_samusHud->GetTargetingManager().Touch();

  if (x1bc_prevState != x1c0_nextState) {
    if (x1c0_nextState == EInGameGuiState::Zero || x1c0_nextState == EInGameGuiState::InGame)
      TryReloadAreaTextures();
    if ((!x38_autoMapper->IsInMapperStateTransition() || !x1e8_enableAutoMapper) &&
        x3c_pauseScreenBlur->IsNotTransitioning())
      OnNewPauseScreenState(archQueue);
  }

  xf8_camFilter.Update(dt);
  if (stateMgr.GetCameraManager()->IsInCinematicCamera()) {
    stateMgr.SetViewportScale(zeus::CVector2f(1.f, 1.f));
  } else {
    stateMgr.SetViewportScale(zeus::CVector2f(
        std::min(x30_playerVisor->GetDesiredViewportScaleX(stateMgr), x34_samusHud->GetViewportScale().x()),
        std::min(x30_playerVisor->GetDesiredViewportScaleY(stateMgr), x34_samusHud->GetViewportScale().y())));
  }

  x1f8_25_playerAlive = stateMgr.GetPlayerState()->IsPlayerAlive();
}

bool CInGameGuiManager::IsInGameStateNotTransitioning() const {
  return (x1bc_prevState >= EInGameGuiState::Zero && x1bc_prevState <= EInGameGuiState::InGame &&
          x1c0_nextState >= EInGameGuiState::Zero && x1c0_nextState <= EInGameGuiState::InGame);
}

bool CInGameGuiManager::IsInPausedStateNotTransitioning() const {
  return (x1bc_prevState >= EInGameGuiState::MapScreen && x1bc_prevState <= EInGameGuiState::PauseHUDMessage &&
          x1c0_nextState >= EInGameGuiState::MapScreen && x1c0_nextState <= EInGameGuiState::PauseHUDMessage);
}

void CInGameGuiManager::ProcessControllerInput(CStateManager& stateMgr, const CFinalInput& input,
                                               CArchitectureQueue& archQueue) {
  if (input.ControllerIdx() == 0) {
    if (!IsInGameStateNotTransitioning()) {
      if (IsInPausedStateNotTransitioning()) {
        if (x1bc_prevState == EInGameGuiState::MapScreen) {
          if (x38_autoMapper->IsInMapperState(CAutoMapper::EAutoMapperState::MapScreen) ||
              x38_autoMapper->IsInMapperState(CAutoMapper::EAutoMapperState::MapScreenUniverse)) {
            x38_autoMapper->ProcessControllerInput(input, stateMgr);
            if (x38_autoMapper->CanLeaveMapScreen(stateMgr))
              BeginStateTransition(EInGameGuiState::InGame, stateMgr);
          }
          return;
        }
        if (x1bc_prevState == EInGameGuiState::PauseSaveGame) {
          x4c_saveUI->ProcessUserInput(input);
          return;
        }
        if (x1bc_prevState == EInGameGuiState::PauseHUDMessage) {
          x44_messageScreen->ProcessControllerInput(input);
          return;
        }
        if (x48_pauseScreen)
          x48_pauseScreen->ProcessControllerInput(stateMgr, input);
      }
    } else {
      x34_samusHud->ProcessControllerInput(input);
    }
  }
}

void CInGameGuiManager::PreDraw(CStateManager& stateMgr, bool cameraActive) {
  if (x48_pauseScreen)
    x48_pauseScreen->PreDraw();
  if (cameraActive)
    x40_samusReflection->PreDraw(stateMgr);
}

void CInGameGuiManager::Draw(CStateManager& stateMgr) {
  SCOPED_GRAPHICS_DEBUG_GROUP("CInGameGuiManager::Draw", zeus::skBlue);
  // if (!GetIsGameDraw())
  //    g_Renderer->x318_26_requestRGBA6 = true;
  if (x1d8_onScreenTexAlpha > 0.f && x1dc_onScreenTexTok.IsLoaded()) {
    if (!m_onScreenQuad || m_onScreenQuad->GetTex().GetObj() != x1dc_onScreenTexTok.GetObj())
      m_onScreenQuad.emplace(EFilterType::Blend, x1dc_onScreenTexTok);

    // No depth read/write
    // Alpha blend
    int w =
        (g_Viewport.x0_left + (x1c4_onScreenTex.x4_origin.x - g_Viewport.x8_width) / 2 + x1c4_onScreenTex.xc_extent.x) -
        x1c4_onScreenTex.x4_origin.x;
    int h =
        (g_Viewport.x4_top + (x1c4_onScreenTex.x4_origin.y - g_Viewport.xc_height) / 2 - x1c4_onScreenTex.xc_extent.y) -
        x1c4_onScreenTex.x4_origin.y;
    zeus::CRectangle rect(x1c4_onScreenTex.x4_origin.x / float(g_Viewport.x8_width),
                          x1c4_onScreenTex.x4_origin.y / float(g_Viewport.xc_height), w / float(g_Viewport.x8_width),
                          h / float(g_Viewport.xc_height));
    m_onScreenQuad->draw(zeus::CColor(1.f, x1d8_onScreenTexAlpha), 1.f, rect);
  }

  float staticAlpha = 0.f;
  if (stateMgr.GetPlayer().GetMorphballTransitionState() == CPlayer::EPlayerMorphBallState::Unmorphed &&
      stateMgr.GetPlayer().GetDeathTime() > 0.f)
    staticAlpha = zeus::clamp(0.f, stateMgr.GetPlayer().GetDeathTime() / (0.3f * 2.5f), 1.f);

  bool notInCine = !stateMgr.GetCameraManager()->IsInCinematicCamera();
  bool drawVisor = false;
  /* Let's always draw the HUD except in cinematic mode */
  if (notInCine /* && (x1bc_prevState == EInGameGuiState::InGame || x1c0_nextState == EInGameGuiState::InGame) */)
    drawVisor = true;

  // if (x3c_pauseScreenBlur->IsGameDraw())
  {
    x34_samusHud->GetTargetingManager().Draw(stateMgr, true);
    CGraphics::SetDepthRange(DEPTH_SCREEN_ACTORS, DEPTH_GUN);
    bool scanVisor = stateMgr.GetPlayerState()->GetActiveVisor(stateMgr) == CPlayerState::EPlayerVisor::Scan;
    if (drawVisor && x1f0_enablePlayerVisor) {
      if (stateMgr.GetPlayer().GetCameraState() == CPlayer::EPlayerCameraState::FirstPerson)
        x20_faceplateDecor.Draw(stateMgr);
      CTargetingManager* tgtMgr = nullptr;
      if (scanVisor && x1e4_enableTargetingManager)
        tgtMgr = &x34_samusHud->GetTargetingManager();
      x30_playerVisor->Draw(stateMgr, tgtMgr);
    }
    x40_samusReflection->Draw(stateMgr);
    if (drawVisor) {
      CGraphics::SetDepthRange(DEPTH_HUD, DEPTH_SCREEN_ACTORS);
      if (staticAlpha > 0.f)
        m_randomStatic.draw(zeus::CColor(1.f, staticAlpha), 1.f);
      x34_samusHud->Draw(stateMgr, x1f4_visorStaticAlpha * (1.f - staticAlpha), x1e0_helmetVisMode,
                         x1ec_hudVisMode != EHudVisMode::Zero, x1e4_enableTargetingManager && !scanVisor);
    }
  }

  bool preDrawBlur = true;
  if (x1bc_prevState >= EInGameGuiState::Zero && x1bc_prevState <= EInGameGuiState::InGame)
    if (x1bc_prevState != EInGameGuiState::MapScreen && x1c0_nextState != EInGameGuiState::MapScreen)
      preDrawBlur = false;
  if (preDrawBlur)
    x3c_pauseScreenBlur->Draw(stateMgr);

  if (notInCine && x1e8_enableAutoMapper &&
      (x3c_pauseScreenBlur->IsGameDraw() || x1bc_prevState == EInGameGuiState::MapScreen ||
       x1c0_nextState == EInGameGuiState::MapScreen)) {
    float t;
    if (stateMgr.GetPlayerState()->GetCurrentVisor() == CPlayerState::EPlayerVisor::Combat)
      t = stateMgr.GetPlayerState()->GetVisorTransitionFactor();
    else
      t = 0.f;

    float mapAlpha;
    if (g_tweakGui->GetShowAutomapperInMorphball())
      mapAlpha = 1.f;
    else if (stateMgr.GetPlayer().GetMorphballTransitionState() == CPlayer::EPlayerMorphBallState::Unmorphed)
      mapAlpha = 1.f;
    else
      mapAlpha = 0.f;

    x34_samusHud->GetBaseHudFrame()->GetFrameCamera()->Draw(CGuiWidgetDrawParms(0.f, zeus::skZero3f));
    CGraphics::SetDepthRange(DEPTH_NEAR, DEPTH_HUD);
    x148_model_automapper->SetIsVisible(true);
    x148_model_automapper->Draw(CGuiWidgetDrawParms(1.f, zeus::skZero3f));
    // ZTest no write
    x38_autoMapper->Draw(stateMgr, zeus::CTransform::Translate(0.f, 0.02f, 0.f) * x18c_mapCamXf,
                         mapAlpha * x1f4_visorStaticAlpha * t);
    // Zest and write
    x148_model_automapper->SetIsVisible(false);
  }

  if (!preDrawBlur)
    x3c_pauseScreenBlur->Draw(stateMgr);

  if (x1e0_helmetVisMode != EHelmetVisMode::ReducedUpdate && notInCine) {
    float camYOff;
    if (!x48_pauseScreen)
      camYOff = 0.f;
    else
      camYOff = x48_pauseScreen->GetHelmetCamYOff();
    x34_samusHud->DrawHelmet(stateMgr, camYOff);
  }

  if (x4c_saveUI)
    x4c_saveUI->Draw();

  if (x44_messageScreen)
    x44_messageScreen->Draw();

  if (x48_pauseScreen)
    x48_pauseScreen->Draw();

  xf8_camFilter.Draw();

  if (stateMgr.GetPlayer().GetDeathTime() > 0.f) {
    float dieDur;
    if (stateMgr.GetPlayer().GetMorphballTransitionState() == CPlayer::EPlayerMorphBallState::Unmorphed)
      dieDur = 2.5f;
    else
      dieDur = 6.f;

    float alpha = zeus::clamp(0.f, stateMgr.GetPlayer().GetDeathTime() / dieDur, 1.f);
    m_deathWhiteout.draw(zeus::CColor(1.f, alpha));

    float zStart = dieDur - 0.5f - 0.5f - 1.f;
    float xStart = 0.5f - zStart;
    float colStart = 0.5f - xStart;
    if (stateMgr.GetPlayer().GetDeathTime() > zStart) {
      float zT = 1.f - zeus::clamp(0.f, (stateMgr.GetPlayer().GetDeathTime() - zStart) / 0.5f, 1.f);
      float xT = 1.f - zeus::clamp(0.f, (stateMgr.GetPlayer().GetDeathTime() - xStart) / 0.5f, 1.f);
      float colT = 1.f - zeus::clamp(0.f, (stateMgr.GetPlayer().GetDeathTime() - colStart) / 0.5f, 1.f);
      SClipScreenRect rect(g_Viewport);
      CGraphics::ResolveSpareTexture(rect);
      m_deathBlackout.draw(zeus::skBlack);
      float z = 0.5f * (zT * zT * zT * zT * zT * (g_Viewport.xc_height - 12.f) + 12.f);
      float x = 0.5f * (xT * (g_Viewport.x8_width - 12.f) + 12.f);

      const std::array<CTexturedQuadFilter::Vert, 4> verts{{
          {{-x, 0.f, z}, {0.f, 0.f}},
          {{-x, 0.f, -z}, {0.f, 1.f}},
          {{x, 0.f, z}, {1.f, 0.f}},
          {{x, 0.f, -z}, {1.f, 1.f}},
      }};

      if (!m_deathRenderTexQuad)
        m_deathRenderTexQuad.emplace(EFilterType::Blend, CGraphics::g_SpareTexture.get());
      m_deathRenderTexQuad->drawVerts(zeus::CColor(1.f, colT), verts);

      if (!m_deathDotQuad)
        m_deathDotQuad.emplace(EFilterType::Multiply, x50_deathDot);
      m_deathDotQuad->drawVerts(zeus::CColor(1.f, colT), verts);
    }
  }
}

void CInGameGuiManager::ShowPauseGameHudMessage(CStateManager& stateMgr, CAssetId pauseMsg, float time) {
  x124_pauseGameHudMessage = pauseMsg;
  x128_pauseGameHudTime = time;
  PauseGame(stateMgr, EInGameGuiState::PauseHUDMessage);
}

void CInGameGuiManager::PauseGame(CStateManager& stateMgr, EInGameGuiState state) {
  g_InputGenerator->SetMotorState(EIOPort::Zero, EMotorState::Stop);
  CSfxManager::SetChannel(CSfxManager::ESfxChannels::PauseScreen);
  BeginStateTransition(state, stateMgr);
}

void CInGameGuiManager::StartFadeIn() {
  xf8_camFilter.SetFilter(EFilterType::Multiply, EFilterShape::Fullscreen, 0.f, zeus::skBlack, -1);
  xf8_camFilter.DisableFilter(0.5f);
}

bool CInGameGuiManager::GetIsGameDraw() const {
  // Always draw world for URDE, even while paused
  return true;
  // return x3c_pauseScreenBlur->IsGameDraw();
}

} // namespace metaforce::MP1
