#include "d/a/obj/d_a_obj_windmill.h"

#include "d/a/d_a_player.h"
#include "d/a/obj/d_a_obj_base.h"
#include "d/a/obj/d_a_obj_pinwheel.h"
#include "d/col/bg/d_bg_s.h"
#include "d/d_light_env.h"
#include "d/d_rumble.h"
#include "d/d_sc_game.h"
#include "d/d_stage.h"
#include "d/flag/sceneflag_manager.h"
#include "d/flag/storyflag_manager.h"
#include "d/snd/d_snd_wzsound.h"
#include "f/f_base.h"
#include "m/m_angle.h"
#include "m/m_vec.h"
#include "nw4r/g3d/res/g3d_resanmchr.h"
#include "nw4r/g3d/res/g3d_resfile.h"
#include "nw4r/g3d/res/g3d_resmdl.h"
#include "s/s_Math.h"
#include "toBeSorted/arc_managers/current_stage_arc_manager.h"
#include "toBeSorted/event_manager.h"

SPECIAL_ACTOR_PROFILE(OBJ_WINDMILL, dAcOwindmill_c, fProfile::OBJ_WINDMILL, 0x1DC, 0, 6);

STATE_DEFINE(dAcOwindmill_c, Wait);
STATE_DEFINE(dAcOwindmill_c, CameraMove);
STATE_DEFINE(dAcOwindmill_c, Move);
STATE_DEFINE(dAcOwindmill_c, Collect);
STATE_DEFINE(dAcOwindmill_c, CollectStick);

const s16 dAcOwindmill_c::sStateTimer1_0x50 = 0x50;
const f32 dAcOwindmill_c::sTargetHalf = 0.5f;
const f32 dAcOwindmill_c::sTargetOne = 1.f;
const s16 dAcOwindmill_c::sStateTimer1_0xC = 0xC;
const s16 dAcOwindmill_c::sStateTimer1_0x32 = 0x32;
const s16 dAcOwindmill_c::sStateTimer2_0xF = 0xF;
const s16 dAcOwindmill_c::s0xF_unused = 0xF;

bool dAcOwindmill_c::createHeap() {
    mHasPropellor = getFromParams(0x14, 1);
    void *data = CurrentStageArcManager::GetInstance()->getData("g3d/stage.brres");
    mResFile = nw4r::g3d::ResFile(data);
    dStage_c::bindStageResToFile(&mResFile);
    dStage_c::bindSkyCmnToResFile(&mResFile);

    nw4r::g3d::ResMdl mdl = mResFile.GetResMdl("StageF000Windmill");
    TRY_CREATE(mMdl.create(mdl, &mAllocator, 0x120));

    nw4r::g3d::ResAnmChr resAnmChr = mResFile.GetResAnmChr("StageF000Windmill");
    TRY_CREATE(mAnmChr.create(mdl, resAnmChr, &mAllocator, nullptr));

    nw4r::g3d::ResAnmClr resAnmClr = mResFile.GetResAnmClr("StageF000Windmill");
    TRY_CREATE(mAnmClr.create(mdl, resAnmClr, &mAllocator, nullptr, 1));

    void *dzb = CurrentStageArcManager::GetInstance()->getData("dzb/StageF000Windmill.dzb");
    void *plc = CurrentStageArcManager::GetInstance()->getData("dat/StageF000Windmill.plc");
    updateMatrix();
    return !mBgW.Set((cBgD_t *)dzb, (PLC *)plc, cBgW::MOVE_BG_e, &mWorldMtx, &mScale);
}

int dAcOwindmill_c::create() {
    CREATE_ALLOCATOR(dAcOwindmill_c);
    mBgW.SetCrrFunc(dBgS_MoveBGProc_Typical);
    dBgS::GetInstance()->Regist(&mBgW, this);
    mSetRotation = getFromParams(0, 0xF);
    mSceneFlagInPlace = getFromParams(4, 0xFF);
    mSceneFlagBeingBlown = getFromParams(0xC, 0xFF);
    mWindmillRotation.set(mRotation.y.mVal);
    mWindmillPosition = mPosition;

    mWindmillPosition.x += -95.22334f * mAng(mRotation.y + mAng::d2s(17.005f)).sin();
    mWindmillPosition.z += -95.22334f * mAng(mRotation.y + mAng::d2s(17.005f)).cos();
    mWindmillPosition.y += -86.11143f;

    if (mHasPropellor || isWindmillRepaired()) {
        attachPinwheel();
    }
    mCallback.attach(mMdl);
    mMdl.setAnm(mAnmChr);
    mMdl.setAnm(mAnmClr);
    mAnmClr.setFrame(0.f, 0);
    field_0x634 = mAnmClr.getFrameMax(0) - 1.f;
    if (isLightTowerRaised2() || SceneflagManager::sInstance->checkBoolFlag(mRoomID, mSceneFlagInPlace)) {
        s8 delta = mSetRotation - mCurrentStuckPos;
        if (delta < 0) {
            delta += 0xC;
        }
        mRotation.y += mAng::d2s(30) * delta;
        mCurrentStuckPos = mSetRotation;
        mStateMgr.changeState(StateID_CollectStick);
        mTimer1 = 0;
        mTimer2 = 0;
        mAnmClr.setFrame(field_0x634, 0);
    } else {
        mStateMgr.changeState(StateID_Wait);
    }
    mMdl.setPriorityDraw(0x1C, 9);
    mBoundingBox.Set(mVec3_c(-440.f, -10.f, -160.f), mVec3_c(440.f, 910.f, 250.f));
    mCullingDistance = 500000.f;
    return SUCCEEDED;
}

s32 dAcOwindmill_c::sRotationIncrement1 = 0x44;
s32 dAcOwindmill_c::sRotationIncrement2 = 0x44;
s32 dAcOwindmill_c::sRotationIncrement3 = -0x73e9;

int dAcOwindmill_c::doDelete() {
    return SUCCEEDED;
}

int dAcOwindmill_c::actorExecute() {
    mStateMgr.executeState();
    updateMatrix();
    mMdl.setLocalMtx(mWorldMtx);
    mBgW.Move();
    mAnmChr.play();
    if (dScGame_c::currentSpawnInfo.isNight() &&
        SceneflagManager::sInstance->checkBoolFlag(mRoomID, mSceneFlagBeingBlown)) {
        dLightEnv_c::GetInstance().set_override_spf(0xb, 0xb, 0.f);
    }
    return SUCCEEDED;
}

int dAcOwindmill_c::draw() {
    drawModelType1(&mMdl);
    return SUCCEEDED;
}

void dAcOwindmill_c::initializeState_Wait() {
    mTimer1 = sStateTimer1_0x32;
}
void dAcOwindmill_c::executeState_Wait() {
    if (mAnmClr.getFrame(0) != 0.f) {
        mAnmClr.play();
    }
    if (!mHasPinwheel && isWindmillRepaired()) {
        attachPinwheel();
    }
    sLib::addCalc(&field_0x630, sTargetHalf, 0.5f, 0.1f, 0.f);
    mAnmChr.setRate(field_0x630);

    if (mPinwheel.get() != NULL && mPinwheel.get()->mBeingBlown) {
        mStateMgr.changeState(StateID_CameraMove);
    } else {
        const dAcPy_c *link = dAcPy_c::GetLink();
        if (sLib::calcTimer(&mTimer1) == 0 || !link->checkActionFlagsCont(0x100)) {
            mTimer1 = 0;
            SceneflagManager::sInstance->unsetFlag(mRoomID, mSceneFlagBeingBlown);
        }
        if (SceneflagManager::sInstance->checkBoolFlag(mRoomID, mSceneFlagBeingBlown)) {
            link->setWindMillPos(&mWindmillPosition);
        }
    }
}
void dAcOwindmill_c::finalizeState_Wait() {}

void dAcOwindmill_c::initializeState_CameraMove() {
    mTimer1 = sStateTimer1_0xC;
    dAcPy_c::GetLink()->setWindMillPos(&mWindmillPosition);
}

void dAcOwindmill_c::executeState_CameraMove() {
    if (mPinwheel.get() != NULL && mPinwheel.get()->mBeingBlown) {
        SceneflagManager::sInstance->setFlag(mRoomID, mSceneFlagBeingBlown);
    }
    sLib::addCalc(&field_0x630, sTargetHalf, 0.5f, 0.1f, 0.f);
    mAnmChr.setRate(field_0x630);
    const dAcPy_c *link = dAcPy_c::GetLink();
    if (!link->checkActionFlagsCont(0x100)) {
        SceneflagManager::sInstance->unsetFlag(mRoomID, mSceneFlagBeingBlown);
        const sStateIDIf_c *oldState = mStateMgr.getOldStateID();
        mStateMgr.changeState(*oldState);
    } else {
        if (SceneflagManager::sInstance->checkBoolFlag(mRoomID, mSceneFlagBeingBlown)) {
            link->setWindMillPos(&mWindmillPosition);
        }
        if (sLib::calcTimer(&mTimer1) == 0) {
            if (mPinwheel.get() != NULL && mPinwheel.get()->mBeingBlown) {
                *mStateMgr.getOldStateID() == StateID_CollectStick;
                mStateMgr.changeState(StateID_Move);
            } else {
                const sStateIDIf_c *oldState = mStateMgr.getOldStateID();
                mStateMgr.changeState(*oldState);
            }
        }
    }
}

void dAcOwindmill_c::finalizeState_CameraMove() {}

void dAcOwindmill_c::initializeState_Move() {
    mTimer1 = sStateTimer1_0x50;
}

void dAcOwindmill_c::executeState_Move() {
    holdSound(SE_Windmil_ROLL_LV);
    if (field_0x63A) {
        if (field_0x634 != mAnmClr.getFrame(0)) {
            mAnmClr.play();
        }
        if (sLib::calcTimer(&mTimer3) == 0) {
            field_0x63A = false;
            mAnmClr.setRate(-1.f, 0);
        }
    } else {
        if (mAnmClr.getFrame(0) != 0.f) {
            mAnmClr.play();
        }
    }
    if (mPinwheel.get() != NULL && mPinwheel.get()->mBeingBlown) {
        SceneflagManager::sInstance->setFlag(mRoomID, mSceneFlagBeingBlown);
    }
    const dAcPy_c *link = dAcPy_c::GetLink();
    if (!link->checkActionFlagsCont(0x100)) {
        SceneflagManager::sInstance->unsetFlag(mRoomID, mSceneFlagBeingBlown);
    }

    if (SceneflagManager::sInstance->checkBoolFlag(mRoomID, mSceneFlagBeingBlown)) {
        link->setWindMillPos(&mWindmillPosition);
    }
    sLib::addCalc(&field_0x630, sTargetOne, 0.5f, 0.1f, 0.f);
    mAnmChr.setRate(field_0x630);
    mRotation.y += sRotationIncrement1;
    if (sLib::calcTimer(&mTimer1) == 0) {
        mCurrentStuckPos++;
        if (mCurrentStuckPos == 0xC) {
            mCurrentStuckPos = 0;
        }
        if (mCurrentStuckPos == mSetRotation) {
            mStateMgr.changeState(StateID_Collect);
        } else {
            if (mPinwheel.get() != NULL && mPinwheel.get()->mBeingBlown) {
                mStateMgr.changeState(StateID_Move);
            } else {
                mStateMgr.changeState(StateID_Wait);
            }
        }
    }
}

void dAcOwindmill_c::finalizeState_Move() {
    field_0x63A = false;
    mAnmClr.setRate(-1.f, 0);

    mRotation.y = mWindmillRotation + mAng::fromDeg(mCurrentStuckPos * 30);
}

void dAcOwindmill_c::initializeState_Collect() {}

void dAcOwindmill_c::executeState_Collect() {
    const dAcPy_c *link = dAcPy_c::GetLink();
    if (SceneflagManager::sInstance->checkBoolFlag(mRoomID, mSceneFlagBeingBlown)) {
        link->setWindMillPos(&mWindmillPosition);
    }
    if (mPinwheel.get() != NULL && mPinwheel.get()->mBeingBlown) {
        field_0x63A = true;
        startSound(SE_Windmil_LIGHT);
        mTimer3 = 15.f - (field_0x634 + 1.f);
        mAnmClr.setRate(1.f, 0);
        sLib::addCalc(&field_0x630, sTargetOne, 0.5f, 0.1f, 0.f);
        mAnmChr.setRate(field_0x630);
        mRotation.y += sRotationIncrement2;
        mStateMgr.changeState(StateID_Move);
        mTimer1--;
    } else {
        sLib::addCalc(&field_0x630, sTargetHalf, 0.5f, 0.1f, 0.f);
        mAnmChr.setRate(field_0x630);
        startSound(SE_Windmil_CORRECT);
        dRumble_c::start(dRumble_c::sRumblePreset3, 0x11);
        mStateMgr.changeState(StateID_CollectStick);
    }
}

void dAcOwindmill_c::finalizeState_Collect() {}

void dAcOwindmill_c::initializeState_CollectStick() {
    mTimer1 = sStateTimer1_0x32;
    mTimer2 = sStateTimer2_0xF;
    mAnmClr.setRate(1.f, 0);
}

void dAcOwindmill_c::executeState_CollectStick() {
    if (!EventManager::sInstance->isInEvent()) {
        SceneflagManager::sInstance->setFlag(mRoomID, mSceneFlagInPlace);
    }
    const dAcPy_c *link = dAcPy_c::GetLink();
    if (SceneflagManager::sInstance->checkBoolFlag(mRoomID, mSceneFlagBeingBlown)) {
        link->setWindMillPos(&mWindmillPosition);
    }
    if (mPinwheel.get() != NULL && mPinwheel.get()->mBeingBlown) {
        sLib::addCalc(&field_0x630, sTargetOne, 0.5f, 0.1f, 0.f);
        mAnmChr.setRate(field_0x630);
        if (mTimer2 != 0) {
            field_0x63A = true;
            startSound(SE_Windmil_LIGHT);
            mTimer3 = 15.f - (field_0x634 + 1.f);
            mStateMgr.changeState(StateID_Move);
            mAnmClr.setRate(1.f, 0);
        } else {
            mStateMgr.changeState(StateID_CameraMove);
        }
    } else {
        sLib::addCalc(&field_0x630, sTargetHalf, 0.5f, 0.1f, 0.f);
        mAnmChr.setRate(field_0x630);
        if (mTimer2 == 1) {
            startSound(SE_Windmil_LIGHT);
        }
        if (sLib::calcTimer(&mTimer2) == 0) {
            if (field_0x634 != mAnmClr.getFrame(0)) {
                mAnmClr.play();
            }
        }
        if (sLib::calcTimer(&mTimer1) == 0 || !link->checkActionFlagsCont(0x100)) {
            mTimer1 = 0;
            SceneflagManager::sInstance->unsetFlag(mRoomID, mSceneFlagBeingBlown);
        }
    }
}

void dAcOwindmill_c::finalizeState_CollectStick() {
    SceneflagManager::sInstance->unsetFlag(mRoomID, mSceneFlagInPlace);
    mAnmClr.setRate(-1.f, 0);
}

bool dAcOwindmill_c::isWindmillRepaired() {
    return StoryflagManager::sInstance->getCounterOrFlag(STORYFLAG_661);
}

bool dAcOwindmill_c::isLightTowerRaised2() {
    return StoryflagManager::sInstance->getCounterOrFlag(STORYFLAG_662);
}

void dAcOwindmill_c::attachPinwheel() {
    mAng3_c pinwheelRot = mRotation;
    pinwheelRot.y += sRotationIncrement3;
    dAcOpinwheel_c *pinwheel = (dAcOpinwheel_c *)dAcObjBase_c::create(
        fProfile::OBJ_PINWHEEL, mRoomID, 0, &mWindmillPosition, &pinwheelRot, nullptr, 0xFFFFFFFF
    );
    mPinwheel.link(pinwheel);
    mHasPinwheel = true;
}
