#include "z_en_syateki_itm.h"
#include "terminal.h"
#include "overlays/actors/ovl_En_Syateki_Man/z_en_syateki_man.h"
#include "overlays/actors/ovl_En_Ex_Ruppy/z_en_ex_ruppy.h"
#include "overlays/actors/ovl_En_G_Switch/z_en_g_switch.h"

#define FLAGS ACTOR_FLAG_4

typedef enum {
    SYATEKI_ROUND_GREEN_APPEAR,
    SYATEKI_ROUND_BLUE_SEQUENTIAL,
    SYATEKI_ROUND_GREEN_THROW,
    SYATEKI_ROUND_BLUE_SIMUL,
    SYATEKI_ROUND_RED_LEFT,
    SYATEKI_ROUND_RED_RIGHT,
    SYATEKI_ROUND_MAX
} EnSyatekItemRound;

void EnSyatekiItm_Init(Actor* thisx, PlayState* play2);
void EnSyatekiItm_Destroy(Actor* thisx, PlayState* play);
void EnSyatekiItm_Update(Actor* thisx, PlayState* play);

void EnSyatekiItm_Idle(EnSyatekiItm* this, PlayState* play);
void EnSyatekiItm_StartRound(EnSyatekiItm* this, PlayState* play);
void EnSyatekiItm_SpawnTargets(EnSyatekiItm* this, PlayState* play);
void EnSyatekiItm_CheckTargets(EnSyatekiItm* this, PlayState* play);
void EnSyatekiItm_CleanupGame(EnSyatekiItm* this, PlayState* play);
void EnSyatekiItm_EndGame(EnSyatekiItm* this, PlayState* play);

ActorInit En_Syateki_Itm_InitVars = {
    /**/ ACTOR_EN_SYATEKI_ITM,
    /**/ ACTORCAT_PROP,
    /**/ FLAGS,
    /**/ OBJECT_GAMEPLAY_KEEP,
    /**/ sizeof(EnSyatekiItm),
    /**/ EnSyatekiItm_Init,
    /**/ EnSyatekiItm_Destroy,
    /**/ EnSyatekiItm_Update,
    /**/ NULL,
};

static Vec3f sGreenAppearHome = { 0.0f, -10.0f, -270.0f };
static Vec3f sBlueSeqHome1 = { -220.0f, 66.0f, -320.0f };
static Vec3f sBlueSeqHome2 = { 260.0f, 66.0f, -320.0f };
static Vec3f sGreenThrowHome = { 0.0f, -10.0f, -270.0f };
static Vec3f sBlueSimulHome1 = { -220.0f, 66.0f, -320.0f };
static Vec3f sBlueSimulHome2 = { 260.0f, 66.0f, -320.0f };
static Vec3f sRedLeftHome1 = { 260.0f, 100.0f, -320.0f };
static Vec3f sRedLeftHome2 = { 360.0f, 100.0f, -320.0f };
static Vec3f sRedRightHome1 = { -230.0f, 94.0f, -360.0f };
static Vec3f sRedRightHome2 = { -400.0f, 94.0f, -360.0f };
static Vec3f sGreenAppearFinal = { 0.0f, 53.0f, -270.0f };
static Vec3f sBlueSeqFinal1 = { -60.0f, 63.0f, -320.0f };
static Vec3f sBlueSeqFinal2 = { 60.0f, 63.0f, -320.0f };
static Vec3f sGreenThrowFinal = { 0.0f, 0.0f, 0.0f };
static Vec3f sBlueSimulFinal1 = { -60.0f, 63.0f, -320.0f };
static Vec3f sBlueSimulFinal2 = { 60.0f, 63.0f, -320.0f };
static Vec3f sRedLeftFinal1 = { -230.0f, 0.0f, 0.0f };
static Vec3f sRedLeftFinal2 = { -230.0f, 0.0f, 0.0f };
static Vec3f sRedRightFinal1 = { 260.0f, 0.0f, 0.0f };
static Vec3f sRedRightFinal2 = { 260.0f, 0.0f, 0.0f };
static s16 sTargetColors[] = { 0, 1, 0, 1, 2, 2 };
static s16 sRupeeTypes[] = { 0, 1, 1, 0, 1, 1, 4, 4, 4, 4 };
static Vec3f sRupeePos[] = {
    { -40.0f, 0.0f, -90.0f }, { -20.0f, 0.0f, -90.0f }, { 0.0f, 0.0f, -90.0f },   { 20.0f, 0.0f, -90.0f },
    { 40.0f, 0.0f, -90.0f },  { -40.0f, 0.0f, -60.0f }, { -20.0f, 0.0f, -60.0f }, { 0.0f, 0.0f, -60.0f },
    { 20.0f, 0.0f, -60.0f },  { 40.0f, 0.0f, -60.0f },
};

void EnSyatekiItm_Init(Actor* thisx, PlayState* play2) {
    PlayState* play = play2;
    EnSyatekiItm* this = (EnSyatekiItm*)thisx;
    s32 i;

    this->man = (EnSyatekiMan*)Actor_SpawnAsChild(&play->actorCtx, &this->actor, play, ACTOR_EN_SYATEKI_MAN, 140.0f,
                                                  0.0f, 255.0f, 0, -0x4000, 0, 0);
    if (this->man == NULL) {
        // "Spawn error"
        PRINTF(VT_FGCOL(GREEN) "☆☆☆☆☆ エラー原 ☆☆☆☆ \n" VT_RST);
        Actor_Kill(&this->actor);
        return;
    }
    for (i = 0; i < 10; i++) {
        this->markers[i] = (EnExRuppy*)Actor_SpawnAsChild(&play->actorCtx, &this->actor, play, ACTOR_EN_EX_RUPPY,
                                                          sRupeePos[i].x, sRupeePos[i].y, sRupeePos[i].z, 0, 0, 0, 4);
        if (this->markers[i] == NULL) {
            // "Second spawn error"
            PRINTF(VT_FGCOL(YELLOW) "☆☆☆☆☆ エラー原セカンド ☆☆☆☆ \n" VT_RST);
            Actor_Kill(&this->actor);
            return;
        }
        this->markers[i]->colorIdx = sRupeeTypes[i];
    }
    this->actionFunc = EnSyatekiItm_Idle;
}

void EnSyatekiItm_Destroy(Actor* thisx, PlayState* play) {
}

void EnSyatekiItm_Idle(EnSyatekiItm* this, PlayState* play) {
    s32 i;
    Player* player = GET_PLAYER(play);

    if (this->signal == ENSYATEKI_START) {
        player->actor.world.pos.x = -12.0f;
        player->actor.world.pos.y = 20.0f;
        player->actor.world.pos.z = 182.0f;
        player->yaw = player->actor.world.rot.y = player->actor.shape.rot.y = 0x7F03;
        player->actor.world.rot.x = player->actor.shape.rot.x = player->actor.world.rot.z = player->actor.shape.rot.z =
            0;
        func_8008EF44(play, 15);
        this->roundNum = this->hitCount = 0;
        for (i = 0; i < 6; i++) {
            this->roundFlags[i] = false;
        }
        for (i = 0; i < 10; i++) {
            this->markers[i]->galleryFlag = false;
        }
        this->actionFunc = EnSyatekiItm_StartRound;
    }
}

void EnSyatekiItm_StartRound(EnSyatekiItm* this, PlayState* play) {
    s32 i;
    s32 j;
    Player* player = GET_PLAYER(play);

    if (this->unkTimer == 0) {
        if (LINK_IS_ADULT) {
            for (i = 0, j = 0; i < SYATEKI_ROUND_MAX; i++) {
                if (this->roundFlags[i]) {
                    j++;
                }
            }
            if (j >= SYATEKI_ROUND_MAX) {
                player->actor.freezeTimer = 10;
                this->signal = ENSYATEKI_END;
                this->actionFunc = EnSyatekiItm_CleanupGame;
                return;
            }
            i = Rand_ZeroFloat(5.99f);
            while (this->roundFlags[i]) {
                i = Rand_ZeroFloat(5.99f);
                if (1) {}
            }
            this->roundNum = i + 1;
            this->roundFlags[i] = true;
        } else {
            this->roundNum++;
            if (this->roundNum > SYATEKI_ROUND_MAX) {
                player->actor.freezeTimer = 10;
                this->signal = ENSYATEKI_END;
                this->actionFunc = EnSyatekiItm_CleanupGame;
                return;
            }
        }

        this->timer = (this->roundNum == 1) ? 50 : 30;

        Sfx_PlaySfxCentered(NA_SE_SY_FOUND);
        this->actionFunc = EnSyatekiItm_SpawnTargets;
    }
}

void EnSyatekiItm_SpawnTargets(EnSyatekiItm* this, PlayState* play) {
    Player* player = GET_PLAYER(play);
    Vec3f zeroVec = { 0.0f, 0.0f, 0.0f };
    s32 i;
    s32 roundIdx;

    if (play->shootingGalleryStatus == -1) {
        player->actor.freezeTimer = 10;
        this->signal = ENSYATEKI_END;
        this->actionFunc = EnSyatekiItm_CleanupGame;
        return;
    }
    if (this->timer == 0) {
        for (i = 0; i < 2; i++) {
            Math_Vec3f_Copy(&this->targetHome[i], &zeroVec);
            Math_Vec3f_Copy(&this->targetFinal[i], &zeroVec);
            this->targets[i] = NULL;
        }
        this->numTargets = 2;
        this->curMarkers[0] = this->curMarkers[1] = NULL;
        roundIdx = this->roundNum - 1;

        switch (roundIdx) {
            case SYATEKI_ROUND_GREEN_APPEAR:
                Math_Vec3f_Copy(&this->targetHome[0], &sGreenAppearHome);
                Math_Vec3f_Copy(&this->targetFinal[0], &sGreenAppearFinal);
                this->curMarkers[0] = this->markers[0];
                this->numTargets = 1;
                break;
            case SYATEKI_ROUND_BLUE_SEQUENTIAL:
                Math_Vec3f_Copy(&this->targetHome[0], &sBlueSeqHome1);
                Math_Vec3f_Copy(&this->targetHome[1], &sBlueSeqHome2);
                Math_Vec3f_Copy(&this->targetFinal[0], &sBlueSeqFinal1);
                Math_Vec3f_Copy(&this->targetFinal[1], &sBlueSeqFinal2);
                this->curMarkers[0] = this->markers[1];
                this->curMarkers[1] = this->markers[2];
                break;
            case SYATEKI_ROUND_GREEN_THROW:
                Math_Vec3f_Copy(&this->targetHome[0], &sGreenThrowHome);
                Math_Vec3f_Copy(&this->targetFinal[0], &sGreenThrowFinal);
                this->curMarkers[0] = this->markers[3];
                this->numTargets = 1;
                break;
            case SYATEKI_ROUND_BLUE_SIMUL:
                Math_Vec3f_Copy(&this->targetHome[0], &sBlueSimulHome1);
                Math_Vec3f_Copy(&this->targetHome[1], &sBlueSimulHome2);
                Math_Vec3f_Copy(&this->targetFinal[0], &sBlueSimulFinal1);
                Math_Vec3f_Copy(&this->targetFinal[1], &sBlueSimulFinal2);
                this->curMarkers[0] = this->markers[4];
                this->curMarkers[1] = this->markers[5];
                break;
            case SYATEKI_ROUND_RED_LEFT:
                Math_Vec3f_Copy(&this->targetHome[0], &sRedLeftHome1);
                Math_Vec3f_Copy(&this->targetHome[1], &sRedLeftHome2);
                Math_Vec3f_Copy(&this->targetFinal[0], &sRedLeftFinal1);
                Math_Vec3f_Copy(&this->targetFinal[1], &sRedLeftFinal2);
                this->curMarkers[0] = this->markers[6];
                this->curMarkers[1] = this->markers[7];
                break;
            case SYATEKI_ROUND_RED_RIGHT:
                Math_Vec3f_Copy(&this->targetHome[0], &sRedRightHome1);
                Math_Vec3f_Copy(&this->targetHome[1], &sRedRightHome2);
                Math_Vec3f_Copy(&this->targetFinal[0], &sRedRightFinal1);
                Math_Vec3f_Copy(&this->targetFinal[1], &sRedRightFinal2);
                this->curMarkers[0] = this->markers[8];
                this->curMarkers[1] = this->markers[9];
                break;
        }

        for (i = 0; i < this->numTargets; i++) {
            this->targets[i] = (EnGSwitch*)Actor_SpawnAsChild(
                &play->actorCtx, &this->actor, play, ACTOR_EN_G_SWITCH, this->targetHome[i].x, this->targetHome[i].y,
                this->targetHome[i].z, 0, 0, 0, (ENGSWITCH_TARGET_RUPEE << 0xC) | 0x3F);
            if (this->targets[i] == NULL) {
                // "Rupee spawn error"
                PRINTF(VT_FGCOL(GREEN) "☆☆☆☆☆ ルピーでエラー原 ☆☆☆☆ \n" VT_RST);
                Actor_Kill(&this->actor);
                return;
            }
            this->targets[i]->index = i;
            this->targets[i]->colorIdx = sTargetColors[roundIdx];
            Math_Vec3f_Copy(&this->targets[i]->targetPos, &this->targetFinal[i]);
            switch (roundIdx) {
                case SYATEKI_ROUND_BLUE_SEQUENTIAL:
                    if (i == 1) {
                        this->targets[i]->delayTimer = 60;
                    }
                    break;
                case SYATEKI_ROUND_GREEN_THROW:
                    this->targets[i]->actor.velocity.y = 15.0f;
                    this->targets[i]->actor.gravity = -1.0f;
                    this->targets[i]->moveMode = GSWITCH_THROW;
                    break;
                case SYATEKI_ROUND_RED_LEFT:
                    this->targets[i]->actor.velocity.x = -5.0f;
                    this->targets[i]->moveMode = GSWITCH_LEFT;
                    break;
                case SYATEKI_ROUND_RED_RIGHT:
                    this->targets[i]->actor.velocity.x = 7.0f;
                    this->targets[i]->moveMode = GSWITCH_RIGHT;
                    break;
            }
        }
        this->targetState[0] = this->targetState[1] = ENSYATEKIHIT_NONE;
        this->actionFunc = EnSyatekiItm_CheckTargets;
    }
}

void EnSyatekiItm_CheckTargets(EnSyatekiItm* this, PlayState* play) {
    Player* player = GET_PLAYER(play);
    s32 i;
    s16 j;

    if (play->shootingGalleryStatus == -1) {
        player->actor.freezeTimer = 10;
        this->signal = ENSYATEKI_END;
        this->actionFunc = EnSyatekiItm_CleanupGame;
    } else {
        for (i = 0, j = 0; i < 2; i++) {
            if (this->targetState[i] != ENSYATEKIHIT_NONE) {
                if (this->targetState[i] == ENSYATEKIHIT_HIT) {
                    this->curMarkers[i]->galleryFlag = true;
                }
                j++;
            }
        }
        if (j == this->numTargets) {
            this->actionFunc = EnSyatekiItm_StartRound;
        }
    }
}

void EnSyatekiItm_CleanupGame(EnSyatekiItm* this, PlayState* play) {
    s32 i;

    for (i = 0; i < 2; i++) {
        if ((this->targetState[i] == ENSYATEKIHIT_NONE) && (this->targets[i] != NULL)) {
            Actor_Kill(&this->targets[i]->actor);
        }
    }
    this->actionFunc = EnSyatekiItm_EndGame;
}

void EnSyatekiItm_EndGame(EnSyatekiItm* this, PlayState* play) {
    Player* player = GET_PLAYER(play);

    player->actor.freezeTimer = 10;
    if (this->signal == ENSYATEKI_RESULTS) {
        this->signal = ENSYATEKI_NONE;
        this->actionFunc = EnSyatekiItm_Idle;
    }
    if (this->signal == ENSYATEKI_START) {
        // "1 frame attack and defense!"
        PRINTF(VT_FGCOL(RED) "☆☆☆☆☆ １フレームの攻防！ ☆☆☆☆ \n" VT_RST);
        PRINTF(VT_FGCOL(RED) "☆☆☆☆☆ １フレームの攻防！ ☆☆☆☆ \n" VT_RST);
        PRINTF(VT_FGCOL(RED) "☆☆☆☆☆ １フレームの攻防！ ☆☆☆☆ \n" VT_RST);
        PRINTF(VT_FGCOL(RED) "☆☆☆☆☆ １フレームの攻防！ ☆☆☆☆ \n" VT_RST);
        PRINTF(VT_FGCOL(RED) "☆☆☆☆☆ １フレームの攻防！ ☆☆☆☆ \n" VT_RST);
        PRINTF(VT_FGCOL(RED) "☆☆☆☆☆ １フレームの攻防！ ☆☆☆☆ \n" VT_RST);
        PRINTF(VT_FGCOL(RED) "☆☆☆☆☆ １フレームの攻防！ ☆☆☆☆ \n" VT_RST);
        PRINTF(VT_FGCOL(RED) "☆☆☆☆☆ １フレームの攻防！ ☆☆☆☆ \n" VT_RST);
        PRINTF(VT_FGCOL(RED) "☆☆☆☆☆ １フレームの攻防！ ☆☆☆☆ \n" VT_RST);
        PRINTF(VT_FGCOL(RED) "☆☆☆☆☆ １フレームの攻防！ ☆☆☆☆ \n" VT_RST);
        this->signal = ENSYATEKI_NONE;
        this->actionFunc = EnSyatekiItm_Idle;
    }
}

void EnSyatekiItm_Update(Actor* thisx, PlayState* play) {
    s32 pad;
    EnSyatekiItm* this = (EnSyatekiItm*)thisx;

    this->actionFunc(this, play);

    if (this->timer != 0) {
        this->timer--;
    }
    if (this->unkTimer != 0) {
        this->unkTimer--;
    }

    if (OOT_DEBUG && BREG(0) != 0) {
        DebugDisplay_AddObject(this->actor.world.pos.x, this->actor.world.pos.y, this->actor.world.pos.z,
                               this->actor.world.rot.x, this->actor.world.rot.y, this->actor.world.rot.z, 1.0f, 1.0f,
                               1.0f, 255, 0, 0, 255, 4, play->state.gfxCtx);
    }
}
