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

#include "c/c_lib.h"
#include "c/c_math.h"
#include "common.h"
#include "d/a/d_a_player.h"
#include "d/a/obj/d_a_obj_base.h"
#include "d/col/bg/d_bg_s.h"
#include "d/col/cc/d_cc_s.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/m3d/m_fanm.h"
#include "m/m_angle.h"
#include "m/m_mtx.h"
#include "m/m_vec.h"
#include "nw4r/g3d/g3d_anmclr.h"
#include "nw4r/g3d/res/g3d_resanmchr.h"
#include "nw4r/g3d/res/g3d_resanmclr.h"
#include "nw4r/g3d/res/g3d_resfile.h"
#include "nw4r/g3d/res/g3d_resmdl.h"
#include "nw4r/math/math_triangular.h"
#include "s/s_Math.h"
#include "toBeSorted/dowsing_target.h"
#include "toBeSorted/event.h"

SPECIAL_ACTOR_PROFILE(OBJ_DUNGEON_SHIP, dAcODungeonShip_c, fProfile::OBJ_DUNGEON_SHIP, 0x17B, 0, 3);

dCcD_SrcCps dAcODungeonShip_c::sCcSrc = {
    {

     {0, 0x1003E, {0, 0, 0}, 0, 0, 0, 0, 0, 0},
     {~(AT_TYPE_BUGNET | AT_TYPE_BEETLE | AT_TYPE_GLITTERING_SPORES | AT_TYPE_0x8000 | AT_TYPE_WIND),
         0x111,
         {0, 0, 0x407},
         0,
         0},
     {0xE8},
     },
    {1000.0f},
};

STATE_DEFINE(dAcODungeonShip_c, Wait);
STATE_DEFINE(dAcODungeonShip_c, Transparency);
STATE_DEFINE(dAcODungeonShip_c, AppearEvent);
STATE_DEFINE(dAcODungeonShip_c, Appear);
STATE_DEFINE(dAcODungeonShip_c, End);

const f32 dAcODungeonShip_c::someFloats[18] = {
    1.0f,   1.0f,   0.0f, 500.0f, 0.0f, 20.0f,   50.0f,   30.0f, 100.0f,
    150.0f, 100.0f, 0.0f, 0.0f,   0.0f, 4000.0f, 2000.0f, 0.0f,  0.0f,
};

void dAcODungeonShip_c::eventIn_Wrapper(void *arg) {
    if (arg != nullptr) {
        static_cast<dAcODungeonShip_c *>(arg)->eventIn();
    }
}

void dAcODungeonShip_c::eventEnd_Wrapper(void *arg) {
    if (arg != nullptr) {
        static_cast<dAcODungeonShip_c *>(arg)->eventEnd();
    }
}

bool dAcODungeonShip_c::createHeap() {
    const char *arcName = "ShipDungeon";
    mRes = nw4r::g3d::ResFile(getOarcResFile(arcName));
    nw4r::g3d::ResMdl mdl = mRes.GetResMdl("ShipDungeonN");
    if (!mMdl.fn_8001F3B0(mdl, &mAllocator, 0x120)) {
        return false;
    }

    nw4r::g3d::ResAnmChr anmChr = mRes.GetResAnmChr("ShipDungeonN");
    if (!mAnmChr.create(mdl, anmChr, &mAllocator, nullptr)) {
        return false;
    }
    mMdl.setAnm(mAnmChr);

    nw4r::g3d::ResAnmClr anmClr = mRes.GetResAnmClr("ShipDungeonN");
    if (!mAnmMatClr.create(mdl, anmClr, &mAllocator, nullptr, 1)) {
        return false;
    }

    mAnmMatClr.setRate(0.0f, 0);
    mAnmChr.setRate(0.0f);
    mMdl.setPriorityDraw(0x7F, 4);

    const char *dzbPlcName = "ShipDungeonN";
    void *dzb = getOarcDZB(arcName, dzbPlcName);
    void *plc = getOarcPLC(arcName, dzbPlcName);
    updateMatrix();
    if (mBg.Set((cBgD_t *)dzb, (PLC *)plc, 1, &mWorldMtx, &mScale)) {
        return false;
    }

    // wat
    if (!SceneflagManager::sInstance->checkBoolFlag(mRoomID, (mParams >> 0x10) & 0xFF)) {
        goto ok;
    } else {
        bool result = mBg.InitMapStuff(&mAllocator);
        if (result == false) {
            return false;
        }
    }
ok:
    return true;
}

int dAcODungeonShip_c::create() {
    if (!StoryflagManager::sInstance->getCounterOrFlag(STORYFLAG_SANDSHIP_DOWSING)) {
        return FAILED;
    }

    if (SceneflagManager::sInstance->checkBoolFlag(mRoomID, (mParams >> 0x10) & 0xFF)) {
        mIsDocked = true;
    }

    CREATE_ALLOCATOR_SIZE(dAcODungeonShip_c, 0x40000);

    mPathIdx = mParams & 0xFF;
    if (mPathIdx == 0xFF) {
        return FAILED;
    }

    dBgS::GetInstance()->Regist(&mBg, this);
    mStts.SetRank(2);
    mCc.Set(sCcSrc);
    mCc.SetStts(mStts);
    mAcceleration = 0.0f;
    mMaxSpeed = 0.0f;
    mBoundingBox.Set(mVec3_c(-6000.0f, -500.0f, -2000.0f), mVec3_c(5000.0f, 4000.0f, 2000.0f));
    if (SceneflagManager::sInstance->checkBoolFlag(mRoomID, (mParams >> 0x10) & 0xFF)) {
        dBgS::GetInstance()->RegistBg(&mBg, this);
        mStateMgr.changeState(StateID_End);
    } else {
        mStateMgr.changeState(StateID_Wait);
    }

    mEffects[0].init(this);
    field_0x8D8 = 0;
    mEffects[1].init(this);
    field_0x8D9 = 0;

    if (!SceneflagManager::sInstance->checkBoolFlag(mRoomID, (mParams >> 0x10) & 0xFF)) {
        mDowsingOffset.set(0.0f, 500.0f, 0.0f);
        mDowsingTarget.initialize(DowsingTarget::SLOT_STORY_EVENT, 0, &mDowsingOffset, 10000.0);
        mDowsingTarget.doRegister();
        mIsRoaming = true;
    }

    mCullingDistance = 200000.0f;
    unsetActorProperty(AC_PROP_0x1);
    mAppearEventFromParam = (mParams >> 0x18);
    field_0x849 = mRotation.x;
    mRotation.x = 0;
    updateMatrix();
    mBg.Move();
    return SUCCEEDED;
}

int dAcODungeonShip_c::doDelete() {
    if (mIsRoaming) {
        mDowsingTarget.doUnregister();
    }
    return SUCCEEDED;
}

int dAcODungeonShip_c::actorExecute() {
    field_0x850++;
    field_0x86C++;
    mStateMgr.executeState();

    // I hate whatever this stupid pattern in actors is with
    // redundant casts and adding 0.0f to stuff
    int tempZero = 0;
    mVec3_c offsetPosition(mPosition.x, mPosition.y + 1200.0f + tempZero, mPosition.z);

    f32 scale = tempZero + 2100.0f;
    mVec3_c directedScale = mVec3_c::Ex * scale;
    directedScale.rotY(mRotation.y);

    f32 extent = tempZero + 1800.0f;
    mCc.Set(offsetPosition + directedScale, offsetPosition - directedScale, extent);

    dCcS::GetInstance()->Set(&mCc);
    mMdl.play();
    mAnmMatClr.play();
    mMdl.calc(false);
    if (field_0x8D8) {
        mEffects[0].holdEffect(PARTICLE_RESOURCE_ID_MAPPING_682_, mPosition, &mRotation, nullptr, nullptr, nullptr);
    }

    if (field_0x8D9) {
        mMtx_c tmpMtx;
        mMdl.getNodeWorldMtx(0, tmpMtx);
        if (mEffects[1].hasEmitters()) {
            mEffects[1].setTransform(tmpMtx);
        } else if (field_0x8DA == 0) {
            mEffects[1].startEffect(PARTICLE_RESOURCE_ID_MAPPING_683_, tmpMtx, nullptr, nullptr);
            field_0x8DA = 1;
        }
    }
    updateMatrixAndScale();
    mBg.Move();
    return SUCCEEDED;
}

int dAcODungeonShip_c::draw() {
    mMdl.mProc.mCallback.field_0x08 = field_0x862;
    mMdl.mProc.mCallback.field_0x09 = 1;
    drawModelType1(&mMdl);
    return SUCCEEDED;
}

void dAcODungeonShip_c::initializeState_Wait() {
    fn_485_1660();
}
void dAcODungeonShip_c::executeState_Wait() {
    if (mCc.ChkTgHit() && mCc.ChkTgAtHitType(AT_TYPE_BOMB)) {
        mNumTimesHit++;
        field_0x862 = 0x8C;
        field_0x863 = 0;
        field_0x864 = 0x8C;
        mAppearEvent = field_0x849;
        startSound(SE_d2ship_APPEAR);
        mStateMgr.changeState(StateID_AppearEvent);
    } else {
        fn_485_1BF0();
    }
}
void dAcODungeonShip_c::finalizeState_Wait() {}

void dAcODungeonShip_c::initializeState_Transparency() {
    field_0x862 = 0;
}
void dAcODungeonShip_c::executeState_Transparency() {
    if (mCc.ChkTgHit() && mCc.ChkTgAtHitType(AT_TYPE_BOMB)) {
        if (mNumTimesHit == 1) {
            field_0x862 = 0xA0;
            field_0x863 = 0;
            field_0x855 = 0;
            mNumTimesHit++;
            startSound(SE_d2ship_APPEAR);
        } else if (mNumTimesHit == 2) {
            field_0x863 = 0;
            mNumTimesHit++;
            SceneflagManager::sInstance->setFlag(mRoomID, (mParams >> 0x10) & 0xFF);
            mAppearEvent = mAppearEventFromParam;
            field_0x8D8 = 1;
            mStateMgr.changeState(StateID_AppearEvent);
            startSound(SE_d2ship_APPEAR);
            return;
        }
    }

    if (mNumTimesHit == 2) {
        if (field_0x855 < 0x28) {
            field_0x855++;
        }
        f32 cos = nw4r::math::CosIdx((field_0x855 * 0x199));
        if (cos < 0.0f) {
            cos = 0.0f;
        }
        field_0x862 = cos * 160.0f;
    } else {
        sLib::chaseUC(&field_0x862, field_0x863, 8);
    }
    field_0x858 = 20.0f;

    const dAcPy_c *link = dAcPy_c::GetLink();
    if (link != nullptr) {
        if (field_0x868 > 0) {
            field_0x868--;
        }

        // f32 distToLink = getSquareDistanceTo(link->position);
        f32 dist2 = 100000000.0f;
        f32 dist1 = 225000000.0f;
        bool isWithinDist2 = getSquareDistanceTo(link->mPosition) < dist2;
        bool isWithinDist1 = getSquareDistanceTo(link->mPosition) < dist1;
        s32 tmp1 = fn_485_1960();
        bool tmp2 = tmp1 < 0x1555;
        if (isWithinDist1 && field_0x868 == 0 && field_0x862 == 0 && tmp2) {
            if (field_0x856 > 0) {
                field_0x856 = 0xFF;
                mPath.setFlag(0x40000000);
            } else {
                field_0x856 = 1;
                mPath.unsetFlag(0x40000000);
            }

            u16 idx = fn_485_1900();
            if (idx != 0xFFFF) {
                mPath.setSegment(idx, 1.0f - mPath.getSegmentTime());
                field_0x868 = 900;
            }
        }

        if (isWithinDist2 && !tmp2) {
            field_0x858 = 50.0f;
        }

        if (mPath.checkFlag(0x40000000) && field_0x858 > 0.0f) {
            field_0x858 *= -1;
        }
    }
    sLib::addCalc(&mSpeed, field_0x858, 0.02f, 1.0f, 0.1f);
    fn_485_1720();
}
void dAcODungeonShip_c::finalizeState_Transparency() {}

void dAcODungeonShip_c::initializeState_AppearEvent() {
    if (mAppearEvent != 0xFF) {
        Event ev(mAppearEvent, mRoomID, 0x100001, (void *)&eventIn_Wrapper, (void *)&eventEnd_Wrapper);
        mEvent.scheduleEvent(ev, 0);
    }
}
void dAcODungeonShip_c::executeState_AppearEvent() {
    if (mAppearEvent != 0xFF) {
        if (mEvent.isThisActorInEvent()) {
            if (mAppearEvent == mAppearEventFromParam) {
                field_0x8D9 = 1;
            }

            switch (mEvent.getCurrentEventCommand()) {
                case 'jmap':
                case 'wait':
                    mEvent.isAdvance();
                    mEvent.advanceNext();
                    break;
                case 'app2':
                    if (mEvent.isAdvance()) {
                        mAnmChr.setRate(1.0f);
                        mAnmChr.setPlayState(m3d::PLAY_MODE_1);
                        mAnmMatClr.setRate(1.0f, 0);
                    } else {
                        mEvent.advanceNext();
                    }
                    fn_485_1EE0();
                    break;
                case 'appe':
                    if (mEvent.isAdvance()) {
                        mAnmChr.setRate(1.0f);
                        mAnmChr.setPlayState(m3d::PLAY_MODE_1);
                        mAnmMatClr.setFrame(0.0f, 0);
                        mAnmMatClr.setRate(1.0f, 0);
                    } else if (mAnmChr.isStop()) {
                        mEvent.advanceNext();
                    }
                    fn_485_1EE0();
                    break;
                case 'app1':
                    sLib::chaseUC(&field_0x862, field_0x863, 2);
                    if (field_0x862 == field_0x863) {
                        mEvent.advanceNext();
                    }
                    fn_485_1DF0();
                    break;
                case 'init':
                    if (mEvent.isAdvance()) {
                        f32 f;
                        if (mEvent.getSingleFloatData(&f, 'ang0', 0) == 1) {
                            mAng ang = mAng::fromDeg(f);
                            mRotation.y = ang;
                        }
                        mVec3_c vec;
                        if (mEvent.getSingleVecData(&vec, 'pos0', 0) == 1) {
                            mPosition = vec;
                        }
                        field_0x8D8 = 0;
                    }
                    mEvent.advanceNext();
                    break;
                default: mEvent.advanceNext(); break;
            }
        } else {
            Event ev(mAppearEvent, mRoomID, 0x100001, (void *)&eventIn_Wrapper, (void *)&eventEnd_Wrapper);
            mEvent.scheduleEvent(ev, 0);
        }
    } else {
        sLib::chaseUC(&field_0x862, field_0x863, 3);
        if (field_0x862 == field_0x863) {
            if (mAppearEvent == mAppearEventFromParam) {
                mStateMgr.changeState(StateID_Appear);
            } else {
                mStateMgr.changeState(StateID_Transparency);
            }
        }
    }
}
void dAcODungeonShip_c::finalizeState_AppearEvent() {}

void dAcODungeonShip_c::initializeState_Appear() {
    field_0x862 = 0xFF;
}
void dAcODungeonShip_c::executeState_Appear() {
    if (!mIsDocked) {
        fn_485_1A90();
    }
}
void dAcODungeonShip_c::finalizeState_Appear() {}

void dAcODungeonShip_c::initializeState_End() {
    field_0x862 = 0xFF;
}
void dAcODungeonShip_c::executeState_End() {
    if (!mIsDocked) {
        fn_485_1A90();
    }
}
void dAcODungeonShip_c::finalizeState_End() {}

void dAcODungeonShip_c::eventIn() {}
void dAcODungeonShip_c::eventEnd() {
    if (mNumTimesHit == 1) {
        mStateMgr.changeState(StateID_Transparency);
    } else {
        mStateMgr.changeState(StateID_Appear);
    }
}

void dAcODungeonShip_c::fn_485_1660() {
    f32 time = 0.0f;
    f32 speed = 0.0f;
    f32 unk = 0.0001f;
    field_0x856 = 1;
    if (mPath.init(mPathIdx, mRoomID, 0, 0, false, time, speed, unk)) {
        mPath.setSegment(0, time);
        mPosition = mPath.getPosition();
        mOldPosition = mPath.getPosition();
    }
}

void dAcODungeonShip_c::fn_485_1720() {
    mPath.setSpeed(mSpeed);
    mPath.execute();
    mPosition = mPath.getPosition();

    mVec3_c tmp;
    mPath.getDirection(tmp);
    mRotation.y = cM::atan2s(tmp.x, tmp.z);
    if (mPath.checkFlag(0x40000000)) {
        mRotation.y += 0x7FFF;
    }
    mRotation.y += 0x4000;
    mAngle.y = mRotation.y;

    int factor = 0x12C;
    f32 tmp2 = nw4r::math::SinIdx(field_0x850 * 800);
    tmp2 *= factor;
    field_0x84C = tmp2;
}

bool dAcODungeonShip_c::fn_485_1830(s32 pathSegmentIndex, s32 direction, s32 *out) {}

u16 dAcODungeonShip_c::fn_485_1900() {
    s32 sign = mPath.checkFlag(0x40000000) ? -1 : 1;
    s32 result;
    if (fn_485_1830(mPath.getSegmentIndex(), sign, &result)) {
        return result;
    } else {
        return -1;
    }
}

u32 dAcODungeonShip_c::fn_485_1960() {
    const dAcPy_c *link = dAcPy_c::GetLink();
    if (link == nullptr) {
        return 0;
    }
    mVec3_c v = mVec3_c::Ez;
    v.rotY(mAngle.y + mAng(-0x4000));
    mVec3_c dist = link->mPosition - mPosition;
    dist.y = 0.0f;
    dist.normalizeRS();
    s16 a1 = cLib::targetAngleY(mVec3_c::Zero, v);
    s16 a2 = cLib::targetAngleY(mVec3_c::Zero, dist);
    return mAng::abs((s32)(a1 - a2));
}

f32 dAcODungeonShip_c::fn_485_1A50() {
    return 4000.0f;
}

f32 dAcODungeonShip_c::fn_485_1A60() {
    return 2000.0f;
}

void dAcODungeonShip_c::fn_485_1A70(mVec3_c *v) {
    v->set(0.0f, 0.0f, 0.0f);
}

void dAcODungeonShip_c::fn_485_1A90() {}

void dAcODungeonShip_c::fn_485_1BF0() {}

void dAcODungeonShip_c::fn_485_1DF0() {
    const dAcPy_c *link = dAcPy_c::GetLink();
    if (link != nullptr) {
        // unused, stack problems
        mVec3_c dist = mPosition - link->mPosition;
        fn_485_1960();
        field_0x858 = 30.0f;
        sLib::addCalc(&mSpeed, 30.0f, 0.02f, 1.0f, 0.1f);
        fn_485_1720();
    }
}

void dAcODungeonShip_c::updateMatrixAndScale() {
    updateMatrix();
    mMdl.setLocalMtx(mWorldMtx);
    mMdl.setScale(mScale);
}

void dAcODungeonShip_c::fn_485_1EE0() {
    if (cM::isZero(mAnmMatClr.getRate(0))) {
        return;
    }

    nw4r::g3d::AnmObjMatClrRes *nw4rObj = mAnmMatClr.getChild(0);
    nw4r::g3d::ClrAnmResult result;
    field_0x862 = nw4rObj->GetResult(&result, 0)->rgba[nw4r::g3d::ClrAnmResult::CLA_TEV1];
}

void unusedFloats() {
    // these are probably not floats but I have no idea why this data is here
    2.24E-43f;
    5.6E-44f;
}
