/**
 * d_a_mbdoor.cpp
 * Object - Double doors (Forsaken Fortress tower & Puppet Ganon boss rooms)
 */

#include "d/dolzel_rel.h" // IWYU pragma: keep
#include "d/actor/d_a_mbdoor.h"
#include "res/Object/Mbdoor.h"
#include "res/Object/Gbdoor.h"
#include "f_op/f_op_actor_mng.h"
#include "d/d_com_inf_game.h"
#include "m_Do/m_Do_ext.h"
#include "m_Do/m_Do_mtx.h"
#include "d/actor/d_a_player.h"

enum {
    ACT_WAIT,
    ACT_SET_START,
    ACT_SET_ANGLE,
    ACT_END,
    ACT_OPEN,
    ACT_STOP_OPEN,
    ACT_SET_GOAL,
    ACT_SET_GOAL2,
    ACT_ADJUSTMENT,
};

/* 00000078-00000084       .text getSwbit__10daMbdoor_cFv */
u8 daMbdoor_c::getSwbit() {
    return fopAcM_GetParam(this) & 0xFF;
}

/* 00000084-00000090       .text getType__10daMbdoor_cFv */
u8 daMbdoor_c::getType() {
    return home.angle.z & 0x3F;
}

/* 00000090-0000009C       .text getShapeType__10daMbdoor_cFv */
u8 daMbdoor_c::getShapeType() {
    return (fopAcM_GetParam(this) & 0x00000F00) >> 0x08;
}

/* 0000009C-000000E4       .text getArcName__10daMbdoor_cFv */
const char* daMbdoor_c::getArcName() {
    switch (getShapeType()) {
    case 1:
        return "Gbdoor";
    default:
        return "Mbdoor";
    }
}

/* 000000E4-00000120       .text getFuBdl__10daMbdoor_cFv */
u32 daMbdoor_c::getFuBdl() {
    switch (getShapeType()) {
    case 1:
        return dRes_INDEX_GBDOOR_BDL_V_GBDFU_e;
    default:
        return dRes_INDEX_MBDOOR_BDL_S_MBDFU_e;
    }
}

/* 00000120-0000015C       .text getLBdl__10daMbdoor_cFv */
u32 daMbdoor_c::getLBdl() {
    switch (getShapeType()) {
    case 1:
        return dRes_INDEX_GBDOOR_BDL_V_GBD_L_e;
    default:
        return dRes_INDEX_MBDOOR_BDL_S_MBD_L_e;
    }
}

/* 0000015C-00000198       .text getRBdl__10daMbdoor_cFv */
u32 daMbdoor_c::getRBdl() {
    switch (getShapeType()) {
    case 1:
        return dRes_INDEX_GBDOOR_BDL_V_GBD_R_e;
    default:
        return dRes_INDEX_MBDOOR_BDL_S_MBD_R_e;
    }
}

/* 00000198-000001D4       .text getToBdl__10daMbdoor_cFv */
u32 daMbdoor_c::getToBdl() {
    switch (getShapeType()) {
    case 1:
        return dRes_INDEX_GBDOOR_BDL_V_GBDTO_e;
    default:
        return dRes_INDEX_MBDOOR_BDL_S_MBDTO_e;
    }
}

/* 000001D4-00000210       .text getDzb__10daMbdoor_cFv */
u32 daMbdoor_c::getDzb() {
    switch (getShapeType()) {
    case 1:
        return dRes_INDEX_GBDOOR_DZB_GBD_e;
    default:
        return dRes_INDEX_MBDOOR_DZB_S_MBDFU_e;
    }
}

/* 00000210-00000254       .text getLOffset__10daMbdoor_cFv */
f32 daMbdoor_c::getLOffset() {
    switch (getShapeType()) {
    case 1:
        return -300.0f;
    default:
        return -255.0f;
    }
}

/* 00000254-00000298       .text getROffset__10daMbdoor_cFv */
f32 daMbdoor_c::getROffset() {
    switch (getShapeType()) {
    case 1:
        return 300.0f;
    default:
        return 277.0f;
    }
}

/* 00000298-000002DC       .text getToOffset__10daMbdoor_cFv */
f32 daMbdoor_c::getToOffset() {
    switch (getShapeType()) {
    case 1:
        return 335.0f;
    default:
        return 300.0f;
    }
}

/* 000002DC-000002FC       .text CheckCreateHeap__FP10fopAc_ac_c */
static BOOL CheckCreateHeap(fopAc_ac_c* i_this) {
    return ((daMbdoor_c*)i_this)->CreateHeap();
}

/* 000002FC-0000060C       .text CreateHeap__10daMbdoor_cFv */
BOOL daMbdoor_c::CreateHeap() {
    J3DModelData* modelData = static_cast<J3DModelData*>(dComIfG_getObjectRes(getArcName(), getFuBdl()));
    JUT_ASSERT(198, modelData != NULL)
    mpFuModel = mDoExt_J3DModel__create(modelData, 0x80000, 0x11000022);
    if (!mpFuModel) { return FALSE; }
    
    modelData = static_cast<J3DModelData*>(dComIfG_getObjectRes(getArcName(), getLBdl()));
    JUT_ASSERT(209, modelData != NULL)
    mpLModel = mDoExt_J3DModel__create(modelData, 0x80000, 0x11000022);
    if (!mpLModel) { return FALSE; }
    
    modelData = static_cast<J3DModelData*>(dComIfG_getObjectRes(getArcName(), getRBdl()));
    JUT_ASSERT(220, modelData != NULL)
    mpRModel = mDoExt_J3DModel__create(modelData, 0x80000, 0x11000022);
    if (!mpRModel) { return FALSE; }
    
    modelData = static_cast<J3DModelData*>(dComIfG_getObjectRes(getArcName(), getToBdl()));
    JUT_ASSERT(231, modelData != NULL)
    mpToModel = mDoExt_J3DModel__create(modelData, 0x80000, 0x11000022);
    if (!mpToModel) { return FALSE; }
    
    mpBgW = new dBgW();
    if (!mpBgW) { return FALSE; }
    cBgD_t* dzbData = (cBgD_t*)dComIfG_getObjectRes(getArcName(), getDzb());
    if (!dzbData) { return FALSE; }
    calcMtx();
    bool error = mpBgW->Set(dzbData, cBgW::MOVE_BG_e, &mpFuModel->getBaseTRMtx());
    return error != true ? TRUE : FALSE;
}

/* 0000060C-00000898       .text calcMtx__10daMbdoor_cFv */
void daMbdoor_c::calcMtx() {
    // Transform the door's frame.
    mDoMtx_stack_c::transS(current.pos);
    mDoMtx_stack_c::YrotM(home.angle.y);
    mpFuModel->setBaseTRMtx(mDoMtx_stack_c::get());
    
    cXyz offset;
    offset.set(0.0f, 0.0f, -150.0f);
    mDoMtx_stack_c::multVec(&offset, &mGoalPos);
    offset.set(0.0f, 0.0f, -400.0f);
    mDoMtx_stack_c::multVec(&offset, &mGoal2Pos);
    
    // Transform door's right half.
    if (field_0x2ad == 0) {
        mpLModel->setBaseTRMtx(mDoMtx_stack_c::get());
        if (field_0x2b2 != 0) {
            mDoMtx_stack_c::transM(getROffset(), 0.0f, 0.0f);
            mDoMtx_stack_c::YrotM(field_0x2b2);
            mDoMtx_stack_c::transM(-getROffset(), 0.0f, 0.0f);
        }
        offset.set(80.0f, 0.0f, 75.0f);
        mDoMtx_stack_c::multVec(&offset, &field_0x2c4);
    }
    mpRModel->setBaseTRMtx(mDoMtx_stack_c::get());
    
    // Transform door's left half.
    if (field_0x2ad == 1) {
        if (field_0x2b2 != 0) {
            mDoMtx_stack_c::transM(getLOffset(), 0.0f, 0.0f);
            mDoMtx_stack_c::YrotM(-field_0x2b2);
            mDoMtx_stack_c::transM(-getLOffset(), 0.0f, 0.0f);
        }
        offset.set(-80.0f, 0.0f, 75.0f);
        mDoMtx_stack_c::multVec(&offset, &field_0x2c4);
        mpLModel->setBaseTRMtx(mDoMtx_stack_c::get());
    }
    
    // Transform door's bar.
    mDoMtx_stack_c::transS(current.pos);
    mDoMtx_stack_c::YrotM(home.angle.y);
    if (field_0x2b0) {
        mDoMtx_stack_c::transM(getToOffset(), 231.0f, 0.0f);
        mDoMtx_stack_c::ZrotM(field_0x2b0);
        mDoMtx_stack_c::transM(-getToOffset(), -231.0f, 0.0f);
    }
    mpToModel->setBaseTRMtx(mDoMtx_stack_c::get());
}

/* 00000898-00000A44       .text CreateInit__10daMbdoor_cFv */
BOOL daMbdoor_c::CreateInit() {
    int swbit = getSwbit();
    int type = getType();
    
    if (dComIfG_Bgsp()->Regist(mpBgW, this)) {
        JUT_ASSERT(334, FALSE);
    }
    field_0x2ac = true;
    tevStr.mRoomNo = current.roomNo;
    
    if (type == 2) {
        setAction(1);
        field_0x2b0 = 0;
        field_0x2b2 = 0;
    } else if ((type == 0 && (swbit != 0xFF && !dComIfGs_isSwitch(swbit, fopAcM_GetRoomNo(this)))) ||
               (type == 1 && (swbit == 0xFF || dComIfGs_isSwitch(swbit, fopAcM_GetRoomNo(this))))) {
        setAction(1);
        field_0x2b0 = 0;
        field_0x2b2 = 0;
    } else {
        setAction(4);
        field_0x2b0 = -0x3F65;
        field_0x2b2 = 0;
    }
    
    field_0x2bc = 30;
    attention_info.position.y += 250.0f;
    eyePos.y += 250.0f;
    attention_info.flags = fopAc_Attn_ACTION_DOOR_e;
    calcMtx();
    mpBgW->Move();
    fopAcM_SetStatusMap(this, 0xB);
    
    return TRUE;
}

/* 00000A44-00000AF4       .text create__10daMbdoor_cFv */
cPhs_State daMbdoor_c::create() {
    cPhs_State phase_state = dComIfG_resLoad(&mPhs, getArcName());
    fopAcM_ct_Retail(this, daMbdoor_c);
    
    if (phase_state != cPhs_COMPLEATE_e) {
        return phase_state;
    }

    fopAcM_ct_Demo(this, daMbdoor_c);

    if (!fopAcM_entrySolidHeap(this, CheckCreateHeap, 0x8200)) {
        return cPhs_ERROR_e;
    }
    
    CreateInit();
    
    return cPhs_COMPLEATE_e;
}

/* 00000AF4-00000B3C       .text getDemoAction__10daMbdoor_cFv */
int daMbdoor_c::getDemoAction() {
    static char* action_table[] = {
        "WAIT",
        "SET_START",
        "SET_ANGLE",
        "END",
        "OPEN",
        "STOP_OPEN",
        "SET_GOAL",
        "SET_GOAL2",
        "ADJUSTMENT",
    };
    
    return dComIfGp_evmng_getMyActIdx(mEvtStaffId, action_table, ARRAY_SIZE(action_table), FALSE, 0);
}

/* 00000B3C-00000F78       .text demoProc__10daMbdoor_cFv */
void daMbdoor_c::demoProc() {
    // Explicit cast from daPy_py_c to daPy_py_c necessary for matching regalloc.
    daPy_py_c* player = (daPy_py_c*)daPy_getPlayerActorClass();
    int actIdx = getDemoAction();
    cXyz goal;
    s16 angle;
    
    if (dComIfGp_evmng_getIsAddvance(mEvtStaffId)) {
        switch (actIdx) {
        case ACT_SET_START:
            calcMtx();
            goal = field_0x2c4;
            dComIfGp_evmng_setGoal(&goal);
            break;
        case ACT_SET_ANGLE:
            angle = current.angle.y + 0x7FFF;
            player->changeDemoMoveAngle(angle);
            break;
        case ACT_OPEN:
            fopAcM_seStart(this, JA_SE_OBJ_MJ_GATE_OPEN, 0);
            field_0x2b4 = 0;
            break;
        case ACT_STOP_OPEN:
            fopAcM_seStart(this, JA_SE_OBJ_MJ_GATE_BAR_OPEN, 0);
            field_0x2b4 = 0;
            field_0x2b6 = 1;
            break;
        case ACT_SET_GOAL:
            goal = mGoalPos;
            dComIfGp_evmng_setGoal(&goal);
            break;
        case ACT_SET_GOAL2:
            goal = mGoal2Pos;
            dComIfGp_evmng_setGoal(&goal);
            break;
        case ACT_ADJUSTMENT:
            calcMtx();
            mAdjustmentTimer = 0;
            int* timerP = dComIfGp_evmng_getMyIntegerP(mEvtStaffId, "Timer");
            if (timerP) {
                mAdjustmentTimer = *timerP;
            }
            break;
        }
    }
    
    switch (actIdx) {
    case ACT_END:
        break;
    case ACT_OPEN: {
        if (field_0x2b4 < 250) {
            field_0x2b4 += 50;
        }
        int temp = field_0x2b2 - field_0x2b4;
        if (temp < -0x1C71) {
            field_0x2b2 = -0x1C71;
            dComIfGp_evmng_cutEnd(mEvtStaffId);
        } else {
            field_0x2b2 = temp;
        }
        calcMtx();
        if (field_0x2ad != 0) {
            angle = current.angle.y + 0x7FFF - field_0x2b2;
        } else {
            angle = current.angle.y + 0x7FFF + field_0x2b2;
        }
        player->setPlayerPosAndAngle(&field_0x2c4, angle);
        break;
    }
    case ACT_STOP_OPEN:
        if (field_0x2b6) {
            if (field_0x2b4 < 400) {
                field_0x2b4 += 40;
            }
            int temp = field_0x2b0 - field_0x2b4;
            if (temp < -0x3F65) {
                field_0x2b0 = -0x3F65;
                dComIfGp_evmng_cutEnd(mEvtStaffId);
                field_0x2b6 = 0;
                fopAcM_seStart(this, JA_SE_OBJ_MJ_GATE_BAR_STOP, 0);
            } else {
                field_0x2b0 = temp;
            }
            calcMtx();
        } else {
            dComIfGp_evmng_cutEnd(mEvtStaffId);
        }
        break;
    case ACT_ADJUSTMENT: {
        angle = player->shape_angle.y;
        s16 r4 = current.angle.y + 0x7FFF;
        cLib_addCalcAngleS2(&angle, r4, 10, 0x800);
        goal = player->current.pos;
        goal.x = goal.x*0.9f + field_0x2c4.x*0.1f;
        goal.z = goal.z*0.9f + field_0x2c4.z*0.1f;
        player->setPlayerPosAndAngle(&goal, angle);
        if (mAdjustmentTimer > 0) {
            mAdjustmentTimer--;
        } else {
            dComIfGp_evmng_cutEnd(mEvtStaffId);
        }
        break;
    }
    default:
        dComIfGp_evmng_cutEnd(mEvtStaffId);
        break;
    }
}

/* 00000F78-000010CC       .text checkArea__10daMbdoor_cFv */
BOOL daMbdoor_c::checkArea() {
    daPy_py_c* player = daPy_getPlayerActorClass();
    cXyz relPos1 = player->current.pos - home.pos;
    cXyz relPos2 = relPos1;
    relPos2.x = relPos1.z * cM_ssin(current.angle.y) - relPos1.x * cM_scos(current.angle.y);
    relPos2.z = relPos1.z * cM_scos(current.angle.y) + relPos1.x * cM_ssin(current.angle.y);
    
    if (relPos2.z > 160.0f) {
        return FALSE;
    }
    if (relPos2.x > 200.0f || relPos2.x < -200.0f) {
        return FALSE;
    }
    if (fopAcM_seenActorAngleY(player, this) > 0x2000) {
        return FALSE;
    }
    
    if (relPos2.x > 0.0f) {
        field_0x2ad = 1;
    } else {
        field_0x2ad = 0;
    }
    
    return TRUE;
}

/* 000010CC-00001198       .text checkUnlock__10daMbdoor_cFv */
BOOL daMbdoor_c::checkUnlock() {
    int swbit = getSwbit();
    switch (getType()) {
    case 0:
        return dComIfGs_isSwitch(swbit, fopAcM_GetRoomNo(this));
    case 1:
        if (!fopAcM_myRoomSearchEnemy(fopAcM_GetRoomNo(this))) {
            if (field_0x2bc > 0) {
                field_0x2bc--;
            } else {
                if (swbit != 0xFF) {
                    dComIfGs_onSwitch(swbit, fopAcM_GetRoomNo(this));
                }
                return TRUE;
            }
        }
    }
    return FALSE;
}

/* 00001198-000011BC       .text daMbdoor_actionWait__FP10daMbdoor_c */
static BOOL daMbdoor_actionWait(daMbdoor_c* i_this) {
    i_this->calcMtx();
    return TRUE;
}

/* 000011BC-0000121C       .text daMbdoor_actionLockWait__FP10daMbdoor_c */
static BOOL daMbdoor_actionLockWait(daMbdoor_c* i_this) {
    if (i_this->checkUnlock()) {
        i_this->setAction(2);
        fopAcM_orderOtherEvent(i_this, "MBDOOR_STOP_OPEN");
    }
    return TRUE;
}

/* 0000121C-000012AC       .text daMbdoor_actionLockOff__FP10daMbdoor_c */
static BOOL daMbdoor_actionLockOff(daMbdoor_c* i_this) {
    if (i_this->eventInfo.checkCommandDemoAccrpt()) {
        i_this->mEvtStaffId = dComIfGp_evmng_getMyStaffId("MBDOOR");
        i_this->demoProc();
        i_this->setAction(3);
    } else {
        fopAcM_orderOtherEvent(i_this, "MBDOOR_STOP_OPEN");
    }
    return TRUE;
}

/* 000012AC-00001324       .text daMbdoor_actionLockDemo__FP10daMbdoor_c */
static BOOL daMbdoor_actionLockDemo(daMbdoor_c* i_this) {
    if (dComIfGp_evmng_endCheck("MBDOOR_STOP_OPEN")) {
        dComIfGp_event_reset();
        i_this->setAction(4);
    } else {
        i_this->demoProc();
    }
    return TRUE;
}

/* 00001324-000013E4       .text daMbdoor_actionCloseWait__FP10daMbdoor_c */
static BOOL daMbdoor_actionCloseWait(daMbdoor_c* i_this) {
    if (i_this->eventInfo.checkCommandDoor()) {
        i_this->mEvtStaffId = dComIfGp_evmng_getMyStaffId("MBDOOR");
        i_this->demoProc();
        i_this->setAction(5);
        dComIfG_Bgsp()->Release(i_this->mpBgW);
        i_this->field_0x2ac = false;
    } else {
        if (i_this->checkArea()) {
            i_this->eventInfo.setEventName("MBDOOR_OPEN");
            i_this->eventInfo.onCondition(dEvtCnd_CANDOOR_e);
        }
    }
    return TRUE;
}

/* 000013E4-00001408       .text daMbdoor_actionOpen__FP10daMbdoor_c */
static BOOL daMbdoor_actionOpen(daMbdoor_c* i_this) {
    i_this->demoProc();
    return TRUE;
}

BOOL daMbdoor_c::draw() {
    g_env_light.settingTevStruct(TEV_TYPE_BG0, &current.pos, &tevStr);
    
    g_env_light.setLightTevColorType(mpFuModel, &tevStr);
    mDoExt_modelUpdateDL(mpFuModel);
    
    g_env_light.setLightTevColorType(mpLModel, &tevStr);
    mDoExt_modelUpdateDL(mpLModel);
    
    g_env_light.setLightTevColorType(mpRModel, &tevStr);
    mDoExt_modelUpdateDL(mpRModel);
    
    g_env_light.setLightTevColorType(mpToModel, &tevStr);
    mDoExt_modelUpdateDL(mpToModel);
    
    return TRUE;
}

/* 00001408-000014BC       .text daMbdoor_Draw__FP10daMbdoor_c */
static BOOL daMbdoor_Draw(daMbdoor_c* i_this) {
    return i_this->draw();
}

BOOL daMbdoor_c::execute() {
    static ActionFunc* l_action[] = {
        &daMbdoor_actionWait,
        &daMbdoor_actionLockWait,
        &daMbdoor_actionLockOff,
        &daMbdoor_actionLockDemo,
        &daMbdoor_actionCloseWait,
        &daMbdoor_actionOpen,
    };
    
    dDemo_actor_c* demoActor = dComIfGp_demo_getActor(demoActorID);
    if (demoActor) {
        field_0x2ad = 0;
        if (demoActor->checkEnable(dDemo_actor_c::ENABLE_ROTATE_e)) {
            field_0x2b2 = demoActor->getRatate()->y;
            field_0x2b0 = demoActor->getRatate()->z;
        }
        calcMtx();
    } else {
        (l_action[mCurActionIdx])(this);
    }
    return TRUE;
}

/* 000014BC-00001558       .text daMbdoor_Execute__FP10daMbdoor_c */
static BOOL daMbdoor_Execute(daMbdoor_c* i_this) {
    return i_this->execute();
}

/* 00001558-00001560       .text daMbdoor_IsDelete__FP10daMbdoor_c */
static BOOL daMbdoor_IsDelete(daMbdoor_c* i_this) {
    UNUSED(i_this);
    return TRUE;
}

/* 00001560-000015D4       .text daMbdoor_Delete__FP10daMbdoor_c */
static BOOL daMbdoor_Delete(daMbdoor_c* i_this) {
#if VERSION == VERSION_DEMO
    if (i_this->field_0x2ac)
#else
    if (i_this->heap)
#endif
    {
        dComIfG_Bgsp()->Release(i_this->mpBgW);
    }
    dComIfG_resDelete(&i_this->mPhs, i_this->getArcName());
    i_this->~daMbdoor_c();
    return TRUE;
}

/* 000015D4-000015F4       .text daMbdoor_Create__FP10fopAc_ac_c */
static cPhs_State daMbdoor_Create(fopAc_ac_c* i_this) {
    return ((daMbdoor_c*)i_this)->create();
}

static actor_method_class l_daMbdoor_Method = {
    (process_method_func)daMbdoor_Create,
    (process_method_func)daMbdoor_Delete,
    (process_method_func)daMbdoor_Execute,
    (process_method_func)daMbdoor_IsDelete,
    (process_method_func)daMbdoor_Draw,
};

actor_process_profile_definition g_profile_MBDOOR = {
    /* Layer ID     */ fpcLy_CURRENT_e,
    /* List ID      */ 0x0003,
    /* List Prio    */ fpcPi_CURRENT_e,
    /* Proc Name    */ fpcNm_MBDOOR_e,
    /* Proc SubMtd  */ &g_fpcLf_Method.base,
    /* Size         */ sizeof(daMbdoor_c),
    /* Size Other   */ 0,
    /* Parameters   */ 0,
    /* Leaf SubMtd  */ &g_fopAc_Method.base,
    /* Draw Prio    */ fpcDwPi_MBDOOR_e,
    /* Actor SubMtd */ &l_daMbdoor_Method,
    /* Status       */ fopAcStts_UNK4000_e | fopAcStts_UNK40000_e,
    /* Group        */ fopAc_ACTOR_e,
    /* Cull Type    */ fopAc_CULLBOX_6_e,
};
