#include "common.h"
#include "effects_internal.h"

extern Gfx D_09000940_38AC90[];
extern Gfx D_09000A00_38AD50[];
extern Gfx D_09000A20_38AD70[];
extern Gfx D_09000A40_38AD90[];
extern Gfx D_09000A60_38ADB0[];
extern Gfx D_09000A80_38ADD0[];

Gfx* D_E0086AA0[] = {
    D_09000A00_38AD50, D_09000A20_38AD70, D_09000A40_38AD90,
    D_09000A60_38ADB0, D_09000A80_38ADD0
};

Gfx* D_E0086AB4[] = { D_09000940_38AC90, D_09000940_38AC90, D_09000940_38AC90 };

u8 D_E0086AC0[] = { 0x0D, 0x0D, 0x0D, 0x00 };

u8 D_E0086AC4[] = {
    0x40, 0x68, 0x90, 0xA4, 0xB8, 0xC0, 0xD0, 0xD8,
    0xE0, 0xE8, 0xF0, 0xF8, 0xFF, 0x00, 0x00, 0x00
};

u8 D_E0086AD4[] = {
    0x0A, 0x14, 0x1C, 0x23, 0x2A, 0x30, 0x35, 0x39,
    0x3C, 0x3E, 0x3F, 0x40, 0x41, 0x00, 0x00, 0x00
};

u8 D_E0086AE4[] = {
    0xFF, 0xFF, 0xC8, 0x82, 0x3C, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00
};

void firework_init(EffectInstance* effect);
void firework_update(EffectInstance* effect);
void firework_render(EffectInstance* effect);
void func_E0086398(EffectInstance* effect);
void func_E00863B4(EffectInstance* effect);

EffectInstance* firework_main(
    s32 arg0,
    f32 arg1,
    f32 arg2,
    f32 arg3,
    f32 arg4,
    s32 arg5
) {
    EffectBlueprint bp;
    EffectInstance* effect;
    FireworkFXData* part;
    s32 numParts = (arg5 * 5 + 20) / 3 + 1;
    s32 i;

    bp.unk_00 = 0;
    bp.init = firework_init;
    bp.update = firework_update;
    bp.renderWorld = firework_render;
    bp.unk_14 = func_E0086398;
    bp.effectID = EFFECT_FIREWORK;

    effect = shim_create_effect_instance(&bp);
    effect->numParts = numParts;
    part = effect->data.firework = shim_general_heap_malloc(numParts * sizeof(*part));
    ASSERT(effect->data.firework != NULL);

    part->unk_00 = arg0;
    part->unk_04 = arg1;
    part->unk_08 = arg2;
    part->unk_0C = arg3;
    part->unk_30 = D_E0086AC0[arg0];
    part->unk_34 = 0;
    part->unk_38 = 0;
    switch (arg0) {
        case 0:
            part->unk_3C = 255;
            part->unk_3D = 255;
            part->unk_3E = 255;
            part->unk_3F = 255;
            part->unk_40 = 255;
            part->unk_41 = 255;
            break;
        case 1:
            part->unk_3C = 255;
            part->unk_3D = 255;
            part->unk_3E = 0;
            part->unk_3F = 255;
            part->unk_40 = 0;
            part->unk_41 = 0;
            break;
        case 2:
        default:
            part->unk_3C = 255;
            part->unk_3D = 255;
            part->unk_3E = 0;
            part->unk_3F = 200;
            part->unk_40 = 255;
            part->unk_41 = 230;
            break;
    }
    part->unk_28 = arg4;

    part++;
    for (i = 1; i < numParts; i++, part++) {
        f32 unk_1C = (i * 360) / (numParts - 1);
        s32 unk_24 = shim_rand_int(359);

        part->unk_04 = 0;
        part->unk_08 = 0;
        part->unk_0C = 0;
        part->unk_10 = 0;
        part->unk_14 = 0;
        part->unk_20 = 0;
        part->unk_00 = 0;
        part->unk_1C = unk_1C;
        part->unk_24 = unk_24;
        part->unk_10 = 0;
        part->unk_2C = 255;
    }

    return effect;
}

void firework_init(EffectInstance* effect) {
}

void firework_update(EffectInstance* effect) {
    FireworkFXData* part = effect->data.firework;
    s32 unk_00 = part->unk_00;
    s32 unk_34;
    s32 idx;
    s32 i;

    part->unk_30--;
    part->unk_34++;

    if (part->unk_30 < 0) {
        shim_remove_effect(effect);
        return;
    }

    unk_34 = part->unk_34;

    if (D_E0086AC0[unk_00] >= unk_34) {
        part->unk_38 = D_E0086AC4[unk_34 - 1] / 256.0f;
    } else {
        part->unk_38 = 1.0f;
    }

    part++;
    for (i = 1; i < effect->numParts; i++, part++) {
        if (D_E0086AC0[unk_00] >= unk_34) {
            idx = unk_34 - 1;
        } else {
            idx = 12;
        }
        part->unk_04 = D_E0086AD4[idx];
        part->unk_08 = 0;
        part->unk_0C = 0;
    }
}

void firework_render(EffectInstance* effect) {
}

void func_E0086398(EffectInstance* effect) {
    func_E00863B4(effect);
}

void func_E00863B4(EffectInstance* effect) {
    FireworkFXData* part = effect->data.firework;
    Gfx* savedGfxPos;
    f32 temp_f32 = part->unk_38 * 5.0f;
    s32 envA;
    s32 temp_s32 = temp_f32;
    s32 primR;
    s32 primG;
    s32 primB;
    s32 envR;
    s32 envG;
    s32 engB;
    s32 primA;
    s32 unk_34 = part->unk_34;
    s32 unk_00 = part->unk_00;
    s32 var_v1;
    Matrix4f sp20;
    Matrix4f sp60;
    s32 i;

    if (temp_s32 < 5) {
        s32 cond;
        f32 unk_28;

        gDPPipeSync(gMainGfxPos++);
        gSPSegment(gMainGfxPos++, 0x09, VIRTUAL_TO_PHYSICAL(effect->graphics->data));

        unk_28 = part->unk_28;

        shim_guPositionF(sp20, 0.0f, -gCameras[gCurrentCameraID].currentYaw, 0.0f, unk_28, part->unk_04, part->unk_08, part->unk_0C);
        shim_guMtxF2L(sp20, &gDisplayContext->matrixStack[gMatrixListPos]);

        gSPMatrix(gMainGfxPos++, &gDisplayContext->matrixStack[gMatrixListPos++], G_MTX_PUSH | G_MTX_MUL | G_MTX_MODELVIEW);

        envA = (temp_f32 - temp_s32) * 256.0f;
        primA = part->unk_2C;
        cond = (temp_s32 >= 4);

        switch (unk_00) {
            case 1:
                if (unk_34 - 1 < 13) {
                    var_v1 = D_E0086AE4[unk_34 - 1];
                } else {
                    var_v1 = 0;
                }
                primR = part->unk_3C;
                primG = (part->unk_3D * var_v1) >> 8;
                primB = (part->unk_3E * var_v1) >> 8;
                envR = (part->unk_3F * var_v1) >> 8;
                envG = (part->unk_40 * var_v1) >> 8;
                engB = (part->unk_41 * var_v1) >> 8;
                break;
            case 2:
                if (unk_34 % 2 == 0) {
                    primR = 0;
                    primG = 0;
                    primB = 0;
                    envR = part->unk_3C;
                    envG = part->unk_3D;
                    engB = part->unk_3E;
                } else {
                    primR = part->unk_3C;
                    primG = part->unk_3D;
                    primB = part->unk_3E;
                    envR = part->unk_3F;
                    envG = part->unk_40;
                    engB = part->unk_41;
                }
                break;
            case 0:
            default:
                primR = part->unk_3C;
                primG = part->unk_3D;
                primB = part->unk_3E;
                envR = part->unk_3F;
                envG = part->unk_40;
                engB = part->unk_41;
                break;
        }

        savedGfxPos = gMainGfxPos++;
        gSPDisplayList(gMainGfxPos++, D_E0086AB4[unk_00]);

        part++;

        if (cond) {
            gDPSetTileSize(gMainGfxPos++, 1, 0, 0, 0, 0);
        }

        for (i = 1; i < effect->numParts; i++, part++) {
            gSPDisplayList(gMainGfxPos++, D_E0086AB4[part->unk_00]);

            shim_guRotateF(sp20, part->unk_1C, 0.0f, 0.0f, 1.0f);
            shim_guRotateF(sp60, part->unk_24, 0.0f, 1.0f, 0.0f);
            shim_guMtxCatF(sp60, sp20, sp20);
            shim_guTranslateF(sp60, part->unk_04, part->unk_08, part->unk_0C);
            shim_guMtxCatF(sp60, sp20, sp20);
            shim_guMtxF2L(sp20, &gDisplayContext->matrixStack[gMatrixListPos]);

            gSPMatrix(gMainGfxPos++, &gDisplayContext->matrixStack[gMatrixListPos++], G_MTX_PUSH | G_MTX_MUL | G_MTX_MODELVIEW);
            gSPDisplayList(gMainGfxPos++, D_E0086AA0[temp_s32]);
            gSPPopMatrix(gMainGfxPos++, G_MTX_MODELVIEW);
        }

        gSPEndDisplayList(gMainGfxPos++);
        gSPBranchList(savedGfxPos, gMainGfxPos);

        gDPSetPrimColor(gMainGfxPos++, 0, 0, primR, primG, primB, primA);
        gDPSetEnvColor(gMainGfxPos++, envR, envG, engB, envA);
        gSPDisplayList(gMainGfxPos++, savedGfxPos + 1);

        shim_guRotateF(sp20, 120.0f, 1.0f, 1.0f, 1.0f);
        shim_guMtxF2L(sp20, &gDisplayContext->matrixStack[gMatrixListPos]);

        gSPMatrix(gMainGfxPos++, &gDisplayContext->matrixStack[gMatrixListPos], G_MTX_NOPUSH | G_MTX_MUL | G_MTX_MODELVIEW);
        gSPDisplayList(gMainGfxPos++, savedGfxPos + 1);
        gSPMatrix(gMainGfxPos++, &gDisplayContext->matrixStack[gMatrixListPos++], G_MTX_NOPUSH | G_MTX_MUL | G_MTX_MODELVIEW);
        gSPDisplayList(gMainGfxPos++, savedGfxPos + 1);
        gSPPopMatrix(gMainGfxPos++, G_MTX_MODELVIEW);
        gDPPipeSync(gMainGfxPos++);
    }
}
