#include "ef_effect_control.h"

#include "m_common_data.h"
#include "m_player_lib.h"
#include "m_rcp.h"
#include "sys_matrix.h"

static void eDig_Hole_init(xyz_t pos, int prio, s16 angle, GAME* game, u16 item_name, s16 arg0, s16 arg1);
static void eDig_Hole_ct(eEC_Effect_c* effect, GAME* game, void* ct_arg);
static void eDig_Hole_mv(eEC_Effect_c* effect, GAME* game);
static void eDig_Hole_dw(eEC_Effect_c* effect, GAME* game);

eEC_PROFILE_c iam_ef_dig_hole = {
    // clang-format off
    &eDig_Hole_init,
    &eDig_Hole_ct,
    &eDig_Hole_mv,
    &eDig_Hole_dw,
    eEC_IGNORE_DEATH,
    eEC_NO_CHILD_ID,
    eEC_DEFAULT_DEATH_DIST,
    // clang-format on
};

typedef struct dig_hole_init_data_s {
    s16 type;
    s16 angle_y;
    s16 goal_angle_x;
    s16 timer;
} eDig_Hole_init_data_c;

static void eDig_Hole_init(xyz_t pos, int prio, s16 angle, GAME* game, u16 item_name, s16 arg0, s16 arg1) {
    GAME_PLAY* play = (GAME_PLAY*)game;
    PLAYER_ACTOR* player = GET_PLAYER_ACTOR(play);

    pos.x += player->actor_class.world.position.x + sin_s(angle) * 30.0f;
    pos.z += player->actor_class.world.position.z + cos_s(angle) * 30.0f;
    pos.x /= 2.0f;
    pos.z /= 2.0f;

    eEC_CLIP->make_effect_proc(eEC_EFFECT_DIG_HOLE, pos, NULL, game, &angle, item_name, prio, arg0, arg1);
}

static void eDig_Hole_ct(eEC_Effect_c* effect, GAME* game, void* ct_arg) {
    static xyz_t fill_hole_pos_tbl[] = {
        { 0.0f, 0.0f, -21.0f },
        { 11.0f, 0.0f, -14.0f },
        { -3.0f, 0.0f, -8.0f },
    };

    static eDig_Hole_init_data_c init_data[] = {
        { 0, DEG2SHORT_ANGLE2(180.0f), DEG2SHORT_ANGLE2(179.0f), 36 },
        { 0, DEG2SHORT_ANGLE2(60.0f), DEG2SHORT_ANGLE2(179.0f), 36 },
        { 0, DEG2SHORT_ANGLE2(300.0f) + 1, DEG2SHORT_ANGLE2(179.0f), 36 },
        { 1, DEG2SHORT_ANGLE2(270.0f), DEG2SHORT_ANGLE2(179.0f), 58 },
        { 1, DEG2SHORT_ANGLE2(270.0f), DEG2SHORT_ANGLE2(179.0f), 46 },
        { 1, DEG2SHORT_ANGLE2(270.0f), DEG2SHORT_ANGLE2(150.0f), 34 },
    };

    s16* angle_p = (s16*)ct_arg;

    effect->effect_specific[0] = *angle_p;
    effect->effect_specific[4] = init_data[effect->arg1].type;
    effect->effect_specific[0] += init_data[effect->arg1].angle_y;
    effect->effect_specific[3] = init_data[effect->arg1].goal_angle_x;
    effect->effect_specific[2] = init_data[effect->arg1].timer;
    effect->effect_specific[1] = 0;

    effect->scale = ZeroVec;

    if (effect->effect_specific[4] == 1) {
        xyz_t pos = fill_hole_pos_tbl[effect->arg1 - 3];

        eEC_CLIP->vector_rotate_y_proc(&pos, SHORTANGLE2RAD(effect->effect_specific[0]));
        effect->position.x += pos.x;
        effect->position.y += 10.0f;
        effect->position.z += pos.z;

        eEC_CLIP->effect_make_proc(eEC_EFFECT_DIG_MUD, effect->position, effect->prio, effect->effect_specific[0], game,
                                   effect->item_name, effect->arg0, 6);
    } else {
        effect->position.x += 5.0f * sin_s(effect->effect_specific[0]);
        effect->position.y += 10.0f;
        effect->position.z += 5.0f * cos_s(effect->effect_specific[0]);
    }

    if (effect->arg0 == mCoBG_ATTRIBUTE_WAVE) {
        int i;
        s16 mizutama_arg1;
        s16 mizutama_angle = *angle_p;
        s16 angle;
        xyz_t pos = effect->position;

        pos.y = 21.0f;

        if (effect->arg1 <= 2) {
            mizutama_arg1 = 0x4000 | (effect->arg1 << 1);
        } else {
            mizutama_arg1 = 0x4000 | (5 << 1);
        }

        for (i = 0; i < 2; i++) {
            pos.x = effect->position.x + RANDOM2_F(10.0f);
            pos.z = effect->position.z + RANDOM2_F(10.0f);

            if (effect->arg1 > 2) {
                angle = mizutama_angle + (s16)(int)(RANDOM2_F(150.0f) * 182.04445f);
            } else {
                angle = mizutama_angle + (s16)(int)(RANDOM2_F(90.0f) * 182.04445f);
            }

            mizutama_angle = angle;
            eEC_CLIP->effect_make_proc(eEC_EFFECT_MIZUTAMA, pos, effect->prio, mizutama_angle, game, effect->item_name,
                                       effect->arg0, mizutama_arg1);
        }
    }

    effect->timer = effect->effect_specific[2];
}

static void eDig_Hole_mv(eEC_Effect_c* effect, GAME* game) {
    if (effect->timer > (effect->effect_specific[2] - 6)) {
        effect->scale.x += 0.00108333334f;
        effect->scale.y = effect->scale.z = effect->scale.x;
    } else if (effect->timer < 6 && effect->effect_specific[4] == 1) {
        effect->scale.x -= 0.00108333334f;
        effect->scale.y = effect->scale.z = effect->scale.x;
    } else if (effect->effect_specific[4] == 1 || effect->timer <= (effect->effect_specific[2] - 8)) {
        add_calc_short_angle2(&effect->effect_specific[1], effect->effect_specific[3], 1.0f - sqrtf(0.8f), 1820, 91);
    }

    if (effect->timer == (effect->effect_specific[2] - 8)) {
        f32 speed;

        if (effect->arg1 == 0 || effect->arg1 == 1 || effect->arg1 == 2) {
            speed = 0.15f;
        } else {
            speed = 0.75f;
        }

        effect->velocity.y = 1.0f;
        effect->velocity.x = speed * sin_s(effect->effect_specific[0]);
        effect->velocity.z = speed * cos_s(effect->effect_specific[0]);

        effect->acceleration.y = -0.1f;
    }

    if (effect->effect_specific[4] == 0 || effect->timer > (effect->effect_specific[2] - 30)) {
        xyz_t_add(&effect->velocity, &effect->acceleration, &effect->velocity);
        xyz_t_add(&effect->position, &effect->velocity, &effect->position);
    }
}

extern Gfx ef_anahori01_00_model[];
extern Gfx ef_anahori01_01_model[];
extern Gfx ef_anahori01_02_model[];

static void eDig_Hole_dw(eEC_Effect_c* effect, GAME* game) {
    OPEN_DISP(game->graph);

    _texture_z_light_fog_prim_xlu(game->graph);
    Matrix_translate(effect->position.x, effect->position.y, effect->position.z, 0);
    Matrix_RotateY(effect->effect_specific[0], 1);
    Matrix_RotateX(effect->effect_specific[1], 1);
    Matrix_scale(effect->scale.x, effect->scale.y, effect->scale.z, 1);

    gSPMatrix(NEXT_POLY_XLU_DISP, _Matrix_to_Mtx_new(game->graph), G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW);

    if (effect->effect_specific[4] == 0) {
        u8 a = (int)eEC_CLIP->calc_adjust_proc(effect->timer, 0, 10, 0.0f, 255.0f);

        gDPSetPrimColor(NEXT_POLY_XLU_DISP, 0, 255, 255, 255, 255, a);
    } else {
        gDPSetPrimColor(NEXT_POLY_XLU_DISP, 0, 255, 255, 255, 255, 255);
    }

    switch (effect->arg0) {
        case mCoBG_ATTRIBUTE_GRASS0:
        case mCoBG_ATTRIBUTE_GRASS1:
        case mCoBG_ATTRIBUTE_GRASS2:
            if (effect->effect_specific[4] == 0) {
                gSPDisplayList(NEXT_POLY_XLU_DISP, ef_anahori01_00_model);
            } else {
                gSPDisplayList(NEXT_POLY_XLU_DISP, ef_anahori01_01_model);
            }
            break;
        case mCoBG_ATTRIBUTE_WAVE:
        case mCoBG_ATTRIBUTE_SAND:
            gSPDisplayList(NEXT_POLY_XLU_DISP, ef_anahori01_02_model);
            break;
        default:
            gSPDisplayList(NEXT_POLY_XLU_DISP, ef_anahori01_01_model);
            break;
    }

    CLOSE_DISP(game->graph);
}
