#include "Runtime/Weapon/CGunController.hpp"

#include "Runtime/CStateManager.hpp"
#include "Runtime/Character/CCharLayoutInfo.hpp"
#include "Runtime/Character/CPASAnimParmData.hpp"

namespace metaforce {

void CGunController::LoadFidgetAnimAsync(CStateManager& mgr, s32 type, s32 gunId, s32 animSet) {
  x30_fidget.LoadAnimAsync(*x0_modelData.GetAnimationData(), type, gunId, animSet, mgr);
}

void CGunController::EnterFidget(CStateManager& mgr, s32 type, s32 gunId, s32 animSet) {
  x54_curAnimId = x30_fidget.SetAnim(*x0_modelData.GetAnimationData(), type, gunId, animSet, mgr);
  x50_gunState = EGunState::Fidget;
}

void CGunController::EnterFreeLook(CStateManager& mgr, s32 gunId, s32 setId) {
  if (x50_gunState != EGunState::ComboFire && !x58_25_enteredComboFire)
    x54_curAnimId = x4_freeLook.SetAnim(*x0_modelData.GetAnimationData(), gunId, setId, 0, mgr, 0.f);
  else
    x4_freeLook.SetLoopState(x1c_comboFire.GetLoopState());
  x50_gunState = EGunState::FreeLook;
}

void CGunController::EnterComboFire(CStateManager& mgr, s32 gunId) {
  if (x50_gunState != EGunState::FreeLook)
    x54_curAnimId = x1c_comboFire.SetAnim(*x0_modelData.GetAnimationData(), gunId, 0, mgr, 0.f);
  else
    x1c_comboFire.SetLoopState(x4_freeLook.GetLoopState());
  x50_gunState = EGunState::ComboFire;
  x58_25_enteredComboFire = true;
}

void CGunController::EnterStruck(CStateManager& mgr, float angle, bool bigStrike, bool b2) {
  switch (x50_gunState) {
  case EGunState::Default:
  case EGunState::ComboFire:
  case EGunState::Idle:
  case EGunState::Strike:
  case EGunState::BigStrike:
    return;
  case EGunState::FreeLook:
    x4_freeLook.SetIdle(true);
    break;
  default:
    break;
  }

  const CPASDatabase& pasDatabase = x0_modelData.GetAnimationData()->GetCharacterInfo().GetPASDatabase();
  CPASAnimParmData parms(pas::EAnimationState::LieOnGround, CPASAnimParm::FromInt32(x4_freeLook.GetGunId()),
                         CPASAnimParm::FromReal32(angle), CPASAnimParm::FromBool(bigStrike),
                         CPASAnimParm::FromBool(b2));
  std::pair<float, s32> anim = pasDatabase.FindBestAnimation(parms, *mgr.GetActiveRandom(), -1);
  x0_modelData.GetAnimationData()->EnableLooping(false);
  CAnimPlaybackParms aparms(anim.second, -1, 1.f, true);
  x0_modelData.GetAnimationData()->SetAnimation(aparms, false);
  x54_curAnimId = anim.second;
  x58_25_enteredComboFire = false;
  x50_gunState = bigStrike ? EGunState::BigStrike : EGunState::Strike;
}

void CGunController::EnterIdle(CStateManager& mgr) {
  CPASAnimParm parm = CPASAnimParm::NoParameter();
  switch (x50_gunState) {
  case EGunState::FreeLook:
    parm = CPASAnimParm::FromEnum(1);
    x4_freeLook.SetIdle(true);
    break;
  case EGunState::ComboFire:
    parm = CPASAnimParm::FromEnum(1);
    x1c_comboFire.SetIdle(true);
    break;
  default:
    return;
  }

  const CPASDatabase& pasDatabase = x0_modelData.GetAnimationData()->GetCharacterInfo().GetPASDatabase();
  CPASAnimParmData parms(pas::EAnimationState::Locomotion, parm);
  std::pair<float, s32> anim = pasDatabase.FindBestAnimation(parms, *mgr.GetActiveRandom(), -1);
  x0_modelData.GetAnimationData()->EnableLooping(false);
  CAnimPlaybackParms aparms(anim.second, -1, 1.f, true);
  x0_modelData.GetAnimationData()->SetAnimation(aparms, false);
  x54_curAnimId = anim.second;
  x50_gunState = EGunState::Idle;
  x58_25_enteredComboFire = false;
}

bool CGunController::Update(float dt, CStateManager& mgr) {
  CAnimData& animData = *x0_modelData.GetAnimationData();
  switch (x50_gunState) {
  case EGunState::FreeLook: {
    x58_24_animDone = x4_freeLook.Update(animData, dt, mgr);
    if (!x58_24_animDone || !x58_25_enteredComboFire)
      break;

    EnterComboFire(mgr, x4_freeLook.GetGunId());
    x58_24_animDone = false;
    break;
  }
  case EGunState::ComboFire:
    x58_24_animDone = x1c_comboFire.Update(animData, dt, mgr);
    break;
  case EGunState::Fidget:
    x58_24_animDone = x30_fidget.Update(animData, dt, mgr);
    break;
  case EGunState::Strike: {
    if (animData.IsAnimTimeRemaining(0.001f, "Whole Body"))
      break;
    x54_curAnimId = x4_freeLook.SetAnim(animData, x4_freeLook.GetGunId(), x4_freeLook.GetSetId(), 0, mgr, 0.f);
    x50_gunState = EGunState::FreeLook;
    break;
  }
  case EGunState::BigStrike:
    x58_24_animDone = !animData.IsAnimTimeRemaining(0.001f, "Whole Body");
    break;
  default:
    break;
  }

  if (!x58_24_animDone)
    return false;

  x50_gunState = EGunState::Inactive;
  x58_25_enteredComboFire = false;

  return true;
}

void CGunController::ReturnToDefault(CStateManager& mgr, float dt, bool setState) {
  CAnimData& animData = *x0_modelData.GetAnimationData();

  switch (x50_gunState) {
  case EGunState::Strike:
    x50_gunState = EGunState::FreeLook;
    [[fallthrough]];
  case EGunState::Idle:
    x4_freeLook.SetIdle(false);
    [[fallthrough]];
  case EGunState::FreeLook:
    if (setState)
      break;
    x54_curAnimId = x4_freeLook.SetAnim(animData, x4_freeLook.GetGunId(), x4_freeLook.GetSetId(), 2, mgr, dt);
    x58_25_enteredComboFire = false;
    break;
  case EGunState::ComboFire:
    x54_curAnimId = x1c_comboFire.SetAnim(animData, x1c_comboFire.GetGunId(), 2, mgr, dt);
    break;
  case EGunState::Fidget:
    ReturnToBasePosition(mgr, dt);
    break;
  case EGunState::BigStrike:
    x4_freeLook.SetIdle(false);
    break;
  default:
    break;
  }

  if (setState)
    x50_gunState = EGunState::Default;
}

void CGunController::ReturnToBasePosition(CStateManager& mgr, float) {
  const CPASDatabase& pasDatabase = x0_modelData.GetAnimationData()->GetCharacterInfo().GetPASDatabase();
  std::pair<float, s32> anim =
      pasDatabase.FindBestAnimation(CPASAnimParmData(pas::EAnimationState::KnockBack), *mgr.GetActiveRandom(), -1);
  x0_modelData.GetAnimationData()->EnableLooping(false);
  CAnimPlaybackParms parms(anim.second, -1, 1.f, true);
  x0_modelData.GetAnimationData()->SetAnimation(parms, false);
  x54_curAnimId = anim.second;
  x58_25_enteredComboFire = false;
}

void CGunController::Reset() {
  x58_24_animDone = true;
  x58_25_enteredComboFire = false;
  x50_gunState = EGunState::Inactive;
}
} // namespace metaforce
