#include "globals.h"
#include "HookTools.hpp"
#include "localplayer.hpp"
#include "init.hpp"
#include "cvwrapper.hpp"
#include "timer.hpp"
#include "hack.hpp"
#if ENABLE_VISUALS
#include "drawing.hpp"
#endif

namespace hacks::tf2::exploits
{
namespace heavy_health
{

enum STEPS
{
    STEP_OFF = -1,
    STEP_START,
    STEP_INIT,
    STEP_INIT_HEAVY,
    STEP_INIT_MELEE,
    STEP_KILL,
    STEP_RANDOMCLASS,
    STEP_HEAVY,
    STEP_PRIMARY,
    STEP_MONITOR,
    STEP_MONITOR_HEAVY
};
static STEPS step               = STEP_OFF;
static unsigned long start_tick = 0;
// Attempts to get the glitch working if it fails
static unsigned loops = 0;
static bool tf_remember_activeweapon_was_false;
static Timer timeout{};
static Timer commands_timer{};

void retry()
{
    if (loops++ < 3)
    {
        timeout.update();
        step = STEP_INIT;
    }
    else
        step = STEP_OFF;
}
void CM()
{
    static auto tf_remember_activeweapon = g_ICvar->FindVar("tf_remember_activeweapon");
    switch (step)
    {
    case STEP_OFF:
        if (tf_remember_activeweapon_was_false)
        {
            tf_remember_activeweapon->SetValue(false);
            tf_remember_activeweapon_was_false = false;
        }
        break;
    case STEP_START:
        if (!tf_remember_activeweapon->GetBool())
        {
            tf_remember_activeweapon->SetValue(true);
            tf_remember_activeweapon_was_false = true;
        }
        loops = 0;
        timeout.update();
    case STEP_INIT:
        // Dead
        if (g_pLocalPlayer->life_state)
        {
            step = STEP_OFF;
            return;
        }
        if (g_pLocalPlayer->clazz != tf_heavy)
        {
            step = STEP_INIT_HEAVY;
            return;
        }
        if (g_pLocalPlayer->weapon_mode != weapon_melee)
        {
            step = STEP_INIT_MELEE;
            return;
        }
        step = STEP_RANDOMCLASS;
        break;
    case STEP_INIT_HEAVY:
        if (g_pLocalPlayer->clazz == tf_heavy && !g_pLocalPlayer->life_state)
        {
            step = STEP_INIT_MELEE;
            return;
        }
        if (timeout.check(5000))
        {
            retry();
            return;
        }
        if (commands_timer.test_and_set(500))
        {
            g_IEngine->ClientCmd_Unrestricted("join_class heavyweapons");
        }
        break;
    case STEP_INIT_MELEE:
        if (g_pLocalPlayer->weapon_mode == weapon_melee)
        {
            step = STEP_KILL;
            return;
        }
        if (timeout.check(5000))
        {
            retry();
            return;
        }
        if (commands_timer.test_and_set(500))
            g_IEngine->ClientCmd_Unrestricted("slot3");
        break;
    case STEP_KILL:
        if (timeout.check(5000))
        {
            retry();
            return;
        }
        if (commands_timer.test_and_set(500))
        {
            if (g_pLocalPlayer->life_state)
            {
                step = STEP_RANDOMCLASS;
                return;
            }
            g_IEngine->ClientCmd_Unrestricted("kill");
        }
        break;
    case STEP_RANDOMCLASS:
        if (g_pLocalPlayer->clazz == tf_demoman && !g_pLocalPlayer->life_state)
        {
            step = STEP_HEAVY;
            return;
        }
        if (timeout.check(5000))
        {
            retry();
            return;
        }
        if (commands_timer.test_and_set(500))
            g_IEngine->ClientCmd_Unrestricted("join_class demoman");
        break;
    case STEP_HEAVY:
        if (commands_timer.test_and_set(1000))
        {
            g_IEngine->ClientCmd_Unrestricted("join_class heavyweapons");
            step = STEP_PRIMARY;
        }
        break;
    case STEP_PRIMARY:
        g_IEngine->ClientCmd_Unrestricted("use *");
        step       = STEP_MONITOR;
        start_tick = tickcount;
        break;
    case STEP_MONITOR:
        if (tickcount - start_tick < 33)
        {
            if (g_pLocalPlayer->clazz == tf_heavy && g_pLocalPlayer->health != 300)
            {
                retry();
            }
        }
        else if (g_pLocalPlayer->clazz == tf_heavy && g_pLocalPlayer->weapon_mode != weapon_melee)
        {
            step = STEP_MONITOR_HEAVY;
        }
        else
            retry();
        break;
    case STEP_MONITOR_HEAVY:
        if (g_pLocalPlayer->weapon_mode == weapon_melee || g_pLocalPlayer->clazz != tf_heavy)
        {
            step = STEP_OFF;
        }
        break;
    }
}
#if ENABLE_VISUALS
void draw()
{
    if (step == STEP_MONITOR_HEAVY)
        AddSideString("Heavy health exploit charging duration: " + std::to_string(int((tickcount - start_tick) * g_GlobalVars->interval_per_tick)), colors::orange);
}
#endif
static CatCommand exploit_heavyhealth("exploit_heavyhealth", "Activates the heavy health exploit", []() { step = STEP_START; });
} // namespace heavy_health

void CreateMove()
{
    heavy_health::CM();
}

#if ENABLE_VISUALS
void Draw()
{
    heavy_health::draw();
}
#endif

static InitRoutine init([]() {
    EC::Register(EC::CreateMove, CreateMove, "cm_exploits");
#if ENABLE_VISUALS
    EC::Register(EC::Draw, Draw, "draw_exploits");
#endif
});
} // namespace hacks::tf2::exploits
