#include "stdafx.h"
#include "EvilFart.hpp"
#include "Abe.hpp"
#include "Collisions.hpp"
#include "stdlib.hpp"
#include "Events.hpp"
#include "Game.hpp"
#include "ThrowableTotalIndicator.hpp"
#include "Particle.hpp"
#include "Sound/Midi.hpp"
#include "Explosion.hpp"
#include "Sfx.hpp"
#include "Function.hpp"

struct Colour final
{
    s16 r, g, b;
};

constexpr Colour greenFart = {32, 128, 32};
constexpr Colour redFart = {128, 38, 32};

EvilFart* EvilFart::ctor_422E30()
{
    ctor_408240(0);
    SetVTable(this, 0x544BE0);

    SetType(AETypes::eEvilFart_45);

    const AnimRecord& rec = AnimRec(AnimId::Fart);
    u8** ppRes = Add_Resource_4DC130(ResourceManager::Resource_Animation, rec.mResourceId);
    Animation_Init_424E10(rec.mFrameTableOffset, rec.mMaxW, rec.mMaxH, ppRes, 1, 1);

    Add_Resource_4DC130(ResourceManager::Resource_Animation, AEResourceID::kExplo2ResID);
    Add_Resource_4DC130(ResourceManager::Resource_Animation, AEResourceID::kAbeblowResID);

    field_DC_bApplyShadows &= ~1u;

    field_CC_sprite_scale = sActiveHero_5C1B68->field_CC_sprite_scale;

    field_D6_scale = sActiveHero_5C1B68->field_D6_scale;
    if (field_D6_scale == 1)
    {
        field_20_animation.field_C_render_layer = Layer::eLayer_SligGreeterFarts_33;
    }
    else
    {
        field_20_animation.field_C_render_layer = Layer::eLayer_SligGreeterFarts_Half_14;
    }

    if (sActiveHero_5C1B68->field_20_animation.field_4_flags.Get(AnimFlags::eBit5_FlipX))
    {
        field_B8_xpos = sActiveHero_5C1B68->field_B8_xpos + (FP_FromInteger(12) * field_CC_sprite_scale);
    }
    else
    {
        field_B8_xpos = sActiveHero_5C1B68->field_B8_xpos - (FP_FromInteger(12) * field_CC_sprite_scale);
    }

    field_BC_ypos = (field_CC_sprite_scale * FP_FromInteger(22)) + sActiveHero_5C1B68->field_BC_ypos;

    FP hitX = {};
    FP hitY = {};
    PathLine* pLine = nullptr;
    if (sCollisions_DArray_5C1128->Raycast_417A60(
            field_B8_xpos - FP_FromInteger(3),
            field_BC_ypos,
            field_B8_xpos + FP_FromInteger(3),
            field_BC_ypos,
            &pLine,
            &hitX,
            &hitY,
            field_D6_scale != 0 ? 0x20006 : 0x40060))
    {
        field_B8_xpos = sActiveHero_5C1B68->field_B8_xpos;
    }

    field_20_animation.field_4_flags.Set(AnimFlags::eBit15_bSemiTrans);

    field_114_flags.Clear(Flags_114::e114_Bit4_bPossesed);
    field_114_flags.Set(Flags_114::e114_Bit3_Can_Be_Possessed);
    field_114_flags.Set(Flags_114::e114_Bit8_bInvisible);

    ResetFartColour();

    field_124_state = FartStates::eIdle_0;
    field_118_bBlowUp = 0;

    field_C4_velx = FP_FromInteger(0);
    field_C8_vely = FP_FromInteger(0);

    field_11A_bPossesed = 0;

    field_20_animation.field_B_render_mode = TPageAbr::eBlend_1;
    field_11C_alive_timer = 220;

    return this;
}

BaseGameObject* EvilFart::VDestructor(s32 flags)
{
    return vdtor_4230D0(flags);
}

void EvilFart::VUpdate()
{
    vUpdate_423100();
}

s16 EvilFart::VTakeDamage_408730(BaseGameObject* pFrom)
{
    return VTakeDamage_423B70(pFrom);
}

void EvilFart::VPossessed_408F70()
{
    vOnPossesed_423DA0();
}

s32 EvilFart::VGetSaveState(u8* pSaveBuffer)
{
    return vGetSaveState_4283F0(reinterpret_cast<EvilFart_State*>(pSaveBuffer));
}

s32 CC EvilFart::CreateFromSaveState_4281C0(const u8* pBuffer)
{
    auto pState = reinterpret_cast<const EvilFart_State*>(pBuffer);

    ResourceManager::LoadResourceFile_49C170("EVILFART.BAN", nullptr);
    ResourceManager::LoadResourceFile_49C170("EXPLO2.BAN", nullptr);
    ResourceManager::LoadResourceFile_49C170("ABEBLOW.BAN", nullptr);

    auto pFart = ae_new<EvilFart>();
    pFart->ctor_422E30();

    if (pState->field_2C.Get(EvilFart_State::eBit1_bControlled))
    {
        sControlledCharacter_5C1B8C = pFart;
    }

    pFart->field_B8_xpos = pState->field_C_xpos;
    pFart->field_BC_ypos = pState->field_10_ypos;

    pFart->field_C4_velx = pState->field_14_velx;
    pFart->field_C8_vely = pState->field_18_vely;

    pFart->field_C0_path_number = pState->field_8_path_number;
    pFart->field_C2_lvl_number = pState->field_A_lvl_number;
    pFart->field_CC_sprite_scale = pState->field_1C_sprite_scale;

    pFart->field_D0_r = pState->field_2_r;
    pFart->field_D2_g = pState->field_4_g;
    pFart->field_D4_b = pState->field_6_b;

    pFart->field_20_animation.field_92_current_frame = pState->field_20_anim_cur_frame;
    pFart->field_20_animation.field_E_frame_change_counter = pState->field_22_frame_change_counter;

    pFart->field_6_flags.Set(BaseGameObject::eDrawable_Bit4, pState->field_25_bDrawable & 1);
    pFart->field_20_animation.field_4_flags.Set(AnimFlags::eBit3_Render, pState->field_24_bAnimRender & 1);

    if (IsLastFrame(&pFart->field_20_animation))
    {
        pFart->field_20_animation.field_4_flags.Set(AnimFlags::eBit18_IsLastFrame);
    }

    pFart->field_120_level = pState->field_26_level;
    pFart->field_11E_path = pState->field_28_path;
    pFart->field_122_camera = pState->field_2A_camera;
    pFart->field_118_bBlowUp = pState->field_2C.Get(EvilFart_State::eBit2_bBlowUp);
    pFart->field_11C_alive_timer = pState->field_2E_alive_timer;
    pFart->field_124_state = pState->field_30_state;
    pFart->field_128_timer = pState->field_34_timer;
    pFart->field_12C_back_to_abe_timer = pState->field_38_timer;
    return sizeof(EvilFart_State);
}

s32 EvilFart::vGetSaveState_4283F0(EvilFart_State* pState)
{
    pState->field_0_type = AETypes::eEvilFart_45;

    pState->field_C_xpos = field_B8_xpos;
    pState->field_10_ypos = field_BC_ypos;
    pState->field_14_velx = field_C4_velx;
    pState->field_18_vely = field_C8_vely;

    pState->field_8_path_number = field_C0_path_number;
    pState->field_A_lvl_number = field_C2_lvl_number;
    pState->field_1C_sprite_scale = field_CC_sprite_scale;

    pState->field_2_r = field_D0_r;
    pState->field_4_g = field_D2_g;
    pState->field_6_b = field_D4_b;

    pState->field_2C.Set(EvilFart_State::eBit1_bControlled, sControlledCharacter_5C1B8C == this);
    pState->field_20_anim_cur_frame = field_20_animation.field_92_current_frame;
    pState->field_22_frame_change_counter = field_20_animation.field_E_frame_change_counter;

    pState->field_25_bDrawable = field_6_flags.Get(BaseGameObject::eDrawable_Bit4);
    pState->field_24_bAnimRender = field_20_animation.field_4_flags.Get(AnimFlags::eBit3_Render);

    pState->field_26_level = field_120_level;
    pState->field_28_path = field_11E_path;
    pState->field_2A_camera = field_122_camera;
    pState->field_2C.Set(EvilFart_State::eBit2_bBlowUp, field_118_bBlowUp & 1);
    pState->field_2E_alive_timer = field_11C_alive_timer;
    pState->field_30_state = field_124_state;
    pState->field_34_timer = field_128_timer;
    pState->field_38_timer = field_12C_back_to_abe_timer;
    return sizeof(EvilFart_State);
}

void EvilFart::InputControlFart_423BB0()
{
    const FP kFartSpeed = FP_FromDouble(0.2);
    const u32 pressedKeys = sInputObject_5BD4E0.field_0_pads[sCurrentControllerIndex_5C1BBE].field_0_pressed;

    if (sInputKey_Right_5550D0 & pressedKeys)
    {
        if (field_C4_velx < FP_FromInteger(4))
        {
            field_C4_velx += kFartSpeed;
        }
    }

    if (sInputKey_Left_5550D4 & pressedKeys)
    {
        if (field_C4_velx > FP_FromInteger(-4))
        {
            field_C4_velx -= kFartSpeed;
        }
    }

    if (sInputKey_Down_5550DC & pressedKeys)
    {
        if (field_C8_vely < FP_FromInteger(4))
        {
            field_C8_vely += kFartSpeed;
        }
    }

    if (sInputKey_Up_5550D8 & pressedKeys)
    {
        if (field_C8_vely > FP_FromInteger(-4))
        {
            field_C8_vely -= kFartSpeed;
        }
    }

    if (!(pressedKeys & sInputKey_Right_5550D0) && !(pressedKeys & sInputKey_Left_5550D4))
    {
        if (field_C4_velx > FP_FromInteger(0))
        {
            field_C4_velx -= kFartSpeed;
        }

        if (field_C4_velx < FP_FromInteger(0))
        {
            field_C4_velx += kFartSpeed;
        }
    }

    if (!(pressedKeys & sInputKey_Up_5550D8) && !(pressedKeys & sInputKey_Down_5550DC))
    {
        if (field_C8_vely > FP_FromInteger(0))
        {
            field_C8_vely -= kFartSpeed;
        }

        if (field_C8_vely < FP_FromInteger(0))
        {
            field_C8_vely += kFartSpeed;
        }
    }
}

void EvilFart::vOnPossesed_423DA0()
{
    field_114_flags.Set(Flags_114::e114_Bit4_bPossesed);
    field_20_animation.field_4_flags.Set(AnimFlags::eBit15_bSemiTrans);

    field_11C_alive_timer = 900;

    field_20_animation.field_B_render_mode = TPageAbr::eBlend_1;

    field_120_level = gMap_5C3030.field_0_current_level;
    field_11E_path = gMap_5C3030.field_2_current_path;
    field_122_camera = gMap_5C3030.field_4_current_camera;

    sControlledCharacter_5C1B8C = this;

    field_124_state = FartStates::eFlying_1;
    field_11A_bPossesed = 1;

    ResetFartColour();
}

void EvilFart::ResetFartColour()
{
    field_D0_r = greenFart.r;
    field_D2_g = greenFart.g;
    field_D4_b = greenFart.b;
}

s16 EvilFart::VTakeDamage_423B70(BaseGameObject* pFrom)
{
    if (field_6_flags.Get(BaseGameObject::eDead_Bit3))
    {
        return 0;
    }

    if (pFrom->Type() == AETypes::eElectricWall_39)
    {
        field_11C_alive_timer = 0;
    }

    return 1;
}

void EvilFart::vUpdate_423100()
{
    if (Event_Get_422C00(kEventDeathReset))
    {
        field_6_flags.Set(BaseGameObject::eDead_Bit3);
    }

    if (sActiveHero_5C1B68->field_106_current_motion != eAbeMotions::Motion_86_HandstoneBegin_45BD00)
    {
        field_11C_alive_timer--;
    }

    if ((sActiveHero_5C1B68->field_106_current_motion != eAbeMotions::Motion_86_HandstoneBegin_45BD00) && field_11C_alive_timer + 1 <= 0)
    {
        if (!field_118_bBlowUp)
        {
            BlowUp();
            if (field_124_state == FartStates::eIdle_0)
            {
                field_6_flags.Set(BaseGameObject::eDead_Bit3);
            }
            else
            {
                field_20_animation.field_4_flags.Clear(AnimFlags::eBit3_Render);
                field_118_bBlowUp = 1;
                field_12C_back_to_abe_timer = sGnFrame_5C1B84 + 35;
            }
        }
    }

    if (field_118_bBlowUp && static_cast<s32>(sGnFrame_5C1B84) > field_12C_back_to_abe_timer)
    {
        sControlledCharacter_5C1B8C = sActiveHero_5C1B68;
        field_6_flags.Set(BaseGameObject::eDead_Bit3);
        gMap_5C3030.SetActiveCam_480D30(field_120_level, field_11E_path, field_122_camera, CameraSwapEffects::eInstantChange_0, 0, 0);
    }

    // Show the count to the boom
    if (field_11C_alive_timer < 251 && !(field_11C_alive_timer % 50))
    {
        if (field_11C_alive_timer > 0)
        {
            if (!field_118_bBlowUp)
            {
                auto pIndicatorMem = ae_new<ThrowableTotalIndicator>();
                if (pIndicatorMem)
                {
                    pIndicatorMem->ctor_431CB0(
                        field_B8_xpos,
                        field_BC_ypos - (field_CC_sprite_scale * FP_FromInteger(50)),
                        field_20_animation.field_C_render_layer,
                        field_20_animation.field_14_scale,
                        field_11C_alive_timer / 50,
                        1);
                }

                field_BC_ypos = field_BC_ypos - (field_CC_sprite_scale * FP_FromInteger(50));
                Mudokon_SFX_457EC0(MudSounds::eFart_7, 0, 10 * (300 - field_11C_alive_timer), this);
                field_BC_ypos += field_CC_sprite_scale * FP_FromInteger(50);
            }
        }
    }

    if (field_124_state == FartStates::eIdle_0)
    {
        CalculateFartColour();
        return;
    }

    if (field_124_state == FartStates::eFlying_1)
    {
        if (FP_GetExponent(field_C4_velx) || FP_GetExponent(field_C8_vely))
        {
            if (!(sGnFrame_5C1B84 % 3))
            {
                FP velocityToUse = {};

                FP directedVelY = field_C8_vely;
                if (directedVelY < FP_FromInteger(0))
                {
                    directedVelY = -directedVelY;
                }

                FP directedVelX = field_C4_velx;
                if (directedVelX < FP_FromInteger(0))
                {
                    directedVelX = -field_C4_velx;
                }

                if (directedVelX <= directedVelY)
                {
                    if (field_C8_vely >= FP_FromInteger(0))
                    {
                        velocityToUse = field_C8_vely;
                    }
                    else
                    {
                        velocityToUse = -field_C8_vely;
                    }
                }
                else if (field_C4_velx >= FP_FromInteger(0))
                {
                    velocityToUse = field_C4_velx;
                }
                else
                {
                    velocityToUse = -field_C4_velx;
                }

                New_Smoke_Particles_426C70(
                    field_B8_xpos * field_CC_sprite_scale,
                    (field_BC_ypos - FP_FromInteger(55)) * field_CC_sprite_scale,
                    FP_FromDouble(0.5) * field_CC_sprite_scale,
                    3,
                    static_cast<u8>(field_D0_r),
                    static_cast<u8>(field_D2_g),
                    0x20u);


                if (field_130_sound_channels)
                {
                    SND_Stop_Channels_Mask_4CA810(field_130_sound_channels);
                }

                Mudokon_SFX_457EC0(MudSounds::eFart_7, 50, FP_GetExponent(velocityToUse * FP_FromInteger(250)) - 2000, nullptr);
                field_130_sound_channels = 0; // TODO OG BUG ?? v32;
            }
        }
        else
        {
            if (field_130_sound_channels)
            {
                SND_Stop_Channels_Mask_4CA810(field_130_sound_channels);
                field_130_sound_channels = 0;
            }
            if (!(sGnFrame_5C1B84 % 30) && !Math_RandomRange_496AB0(0, 1))
            {
                Mudokon_SFX_457EC0(MudSounds::eFart_7, 50, Math_RandomRange_496AB0(-1500, -2000), nullptr);
            }
        }

        InputControlFart_423BB0();
        SetActiveCameraDelayedFromDir_408C40();

        FP x2Offset = {};
        if (field_C4_velx < FP_FromInteger(0))
        {
            x2Offset = FP_FromInteger(-3);
        }
        else
        {
            FP_FromInteger(3);
        }

        FP y2Offset = {};
        if (field_C8_vely < FP_FromInteger(0))
        {
            y2Offset = FP_FromInteger(-3);
        }
        else
        {
            y2Offset = FP_FromInteger(3);
        }

        PathLine* pLine = nullptr;
        FP hitX = {};
        FP hitY = {};
        if (sCollisions_DArray_5C1128->Raycast_417A60(
                field_B8_xpos,
                field_BC_ypos - (field_CC_sprite_scale * FP_FromInteger(54)),
                x2Offset + field_B8_xpos + field_C4_velx,
                y2Offset + field_BC_ypos + field_C8_vely - (field_CC_sprite_scale * FP_FromInteger(54)),
                &pLine,
                &hitX,
                &hitY,
                field_D6_scale != 0 ? 0x20006 : 0x40060))
        {
            field_C4_velx = FP_FromInteger(0);
        }
        else
        {
            field_B8_xpos += field_C4_velx;
        }

        if (sCollisions_DArray_5C1128->Raycast_417A60(
                field_B8_xpos,
                field_BC_ypos - (field_CC_sprite_scale * FP_FromInteger(54)),
                field_B8_xpos + field_C4_velx + x2Offset,
                y2Offset + field_BC_ypos + field_C8_vely - (field_CC_sprite_scale * FP_FromInteger(54)),
                &pLine,
                &hitX,
                &hitY,
                field_D6_scale != 0 ? 131081 : 262288))
        {
            field_C8_vely = FP_FromInteger(0);
        }
        else
        {
            field_BC_ypos += field_C8_vely;
        }

        if (!Input_IsChanting_45F260())
        {
            field_11A_bPossesed = 0;
        }

        field_20_animation.field_4_flags.Set(AnimFlags::eBit15_bSemiTrans);

        field_20_animation.field_B_render_mode = TPageAbr::eBlend_1;
        if (field_C4_velx == FP_FromInteger(0) && field_C8_vely == FP_FromInteger(0))
        {
            if (Input_IsChanting_45F260())
            {
                if (!field_11A_bPossesed)
                {
                    field_124_state = FartStates::eDechanting_2;
                    field_128_timer = sGnFrame_5C1B84 + 15;
                    field_12C_back_to_abe_timer = sGnFrame_5C1B84 + 50;
                    SFX_Play_46FA90(SoundEffect::PossessEffect_17, 0);
                }
            }
        }

        CalculateFartColour();
        return;
    }

    if (field_124_state == FartStates::eDechanting_2)
    {
        if (!Input_IsChanting_45F260())
        {
            field_124_state = FartStates::eFlying_1;
            return;
        }

        if (!(sGnFrame_5C1B84 % 4))
        {
            if (field_118_bBlowUp)
            {
                return;
            }

            const FP yposOffset = (field_CC_sprite_scale * FP_FromInteger(Math_RandomRange_496AB0(-20, 10)));
            const FP xposOffset = (field_CC_sprite_scale * FP_FromInteger(Math_RandomRange_496AB0(-20, 20)));
            New_TintChant_Particle_426BE0(
                xposOffset + field_B8_xpos,
                yposOffset + field_BC_ypos - (field_CC_sprite_scale * FP_FromInteger(54)),
                field_CC_sprite_scale,
                Layer::eLayer_0);
        }

        if (!field_118_bBlowUp && static_cast<s32>(sGnFrame_5C1B84) > field_128_timer)
        {
            BlowUp();

            field_20_animation.field_4_flags.Clear(AnimFlags::eBit3_Render);
            field_118_bBlowUp = 1;
        }
        return;
    }
}

void EvilFart::BlowUp()
{
    auto pExplosionMem2 = ae_new<Explosion>();
    if (pExplosionMem2)
    {
        pExplosionMem2->ctor_4A1200(
            field_B8_xpos,
            field_BC_ypos - (field_CC_sprite_scale * FP_FromInteger(50)),
            field_CC_sprite_scale,
            0);
    }
}

void EvilFart::CalculateFartColour()
{
    FP scaledValue;
    if (field_124_state == FartStates::eIdle_0)
    {
        scaledValue = FP_FromInteger(field_11C_alive_timer) / FP_FromInteger(220);
    }
    else
    {
        scaledValue = FP_FromInteger(field_11C_alive_timer) / FP_FromInteger(900);
    }
    // Linear change from greenFart to redFart
    field_D0_r = FP_GetExponent(FP_FromInteger(redFart.r) - (scaledValue * FP_FromInteger(redFart.r - greenFart.r)));
    field_D2_g = FP_GetExponent(FP_FromInteger(redFart.g) + (scaledValue * FP_FromInteger(greenFart.g - redFart.g)));
}

void EvilFart::dtor_423D80()
{
    SetVTable(this, 0x544BE0);
    dtor_4080B0();
}

EvilFart* EvilFart::vdtor_4230D0(s32 flags)
{
    dtor_423D80();
    if (flags & 1)
    {
        ae_delete_free_495540(this);
    }
    return this;
}
