#include "Animations.h"
#include "Client.h"

#include "ClickGui.h"

#include "Utils/MoveUtils.h"
#include "Utils/ChatUtils.h"

auto Animations::onViewPerspectiveAfter(int* perspective) -> void {
    perspective_ = *perspective;
}

auto Animations::onRenderActorBefore(Actor* _this, glm::vec3* camera, glm::vec3* position) -> void {
    if (cameraspeed == 0.f) return;

    glm::vec3 velocity = Utils::Move::getVelocity(static_cast<Player*>(_this));

    static glm::vec3 smoothvelocity = velocity;
    if (regetvelocity) {
        smoothvelocity = velocity;
        regetvelocity = false;
    }

    smoothvelocity.x = Utils::Math::animate(velocity.x, smoothvelocity.x, Client::deltaTime * cameraspeed);
    smoothvelocity.y = Utils::Math::animate(velocity.y, smoothvelocity.y, (Client::deltaTime * 4.f) * cameraspeed);
    smoothvelocity.z = Utils::Math::animate(velocity.z, smoothvelocity.z, Client::deltaTime * cameraspeed);

    if (perspective_ != 0) {
        glm::vec3 newPos = *position;
        newPos.x = newPos.x - (smoothvelocity.x * 1.5f);
        newPos.y = newPos.y - ((smoothvelocity.y + 0.0784f) * 2.f);
        newPos.z = newPos.z - (smoothvelocity.z * 1.5f);

        *position = newPos;
    }
}

auto Animations::onMouse(char button, char action, bool* cancel) -> void {
    if (button == 2) {
        guarding = static_cast<int>(action) == 1;
    }
    if (button == 1) {
        if (guarding && mode == 3)
            queue = true;
    }
}

auto Animations::onSwingDuration(int* duration) -> void {
    if (guarding && mode == 3) {
        *duration = 0;
    }
    else if (guarding && mode == 6) {
        *duration = 7 * (3 * swingspeed * 50);
    }
    else {
        *duration = 7 * (3 * swingspeed);
    }
}

auto Animations::onBobHurt(glm::mat4* mat, bool* cancel) -> void {
    ClientInstance* clientinstance = Client::clientinstance;
    if (!clientinstance) return;

    Player* localplayer = clientinstance->getLocalPlayer();
    if (!localplayer) return;

    PlayerInventory* playerinventory = localplayer->getPlayerInventory();
    if (!playerinventory) return;

    Inventory* inventory = playerinventory->getInventory();
    if (!inventory) return;

    glm::mat4& matrix = *mat;
    matrix = glm::translate<float>(matrix, glm::vec3(x, y, z));

    auto clickguiMod = Client::findMod<ClickGui>(Datas::Language::get().module.render.clickgui.name.c_str());
    if (clickguiMod && clickguiMod->enabled) return;

    if (Client::screenName != _("hud_screen")) {
        if (patchedflux) {
            DWORD oldProtect;
            VirtualProtect(flux_, 5, PAGE_EXECUTE_READWRITE, &oldProtect);
            memcpy(flux_, ogBytes_flux, 5);
            VirtualProtect(flux_, 5, oldProtect, &oldProtect);

            patchedflux = false;
        }

        if (patchedviewbob) {
            DWORD oldProtect;
            VirtualProtect(viewbobbing_, 6, PAGE_EXECUTE_READWRITE, &oldProtect);
            memcpy(viewbobbing_, ogBytes_viewbobbing, 6);
            VirtualProtect(viewbobbing_, 6, oldProtect, &oldProtect);

            patchedviewbob = false;
        }

        *swingangle_ = -1.25664f;

        return;
    }

    if (guarding) {
        int itemslot = playerinventory->getHandSlot();
        if (inventory->getItemStack(itemslot)->getItem()) {
            guarding = (*inventory->getItemStack(itemslot)->getItem())->getName().find(_("_sword")) != std::string::npos;
        }
        else guarding = false;
    }

    if (guarding) {
        if (mode == 2) {
            *swingangle_ = -0.f;
        }
        if (mode == 4) {
            *swingangle_ = -0.72832f;
        }
        if (mode == 6) {
            *swingangle_ = (-1.25664f * 100.f);
        }
    }
    else
        *swingangle_ = -1.25664f;

    if (guardonly ? guarding : true) {
        if (!patchedflux) {
            DWORD oldProtect;
            VirtualProtect(flux_, 5, PAGE_EXECUTE_READWRITE, &oldProtect);
            memcpy(ogBytes_flux, flux_, 5);
            VirtualProtect(flux_, 5, oldProtect, &oldProtect);

            VirtualProtect(flux_, 5, PAGE_EXECUTE_READWRITE, &oldProtect);
            memcpy(flux_, _("\x90\x90\x90\x90\x90"), 5);
            VirtualProtect(flux_, 5, oldProtect, &oldProtect);

            patchedflux = true;
        }
    }
    else if (guardonly ? !guarding : false) {
        if (patchedflux) {
            DWORD oldProtect;
            VirtualProtect(flux_, 5, PAGE_EXECUTE_READWRITE, &oldProtect);
            memcpy(flux_, ogBytes_flux, 5);
            VirtualProtect(flux_, 5, oldProtect, &oldProtect);
            patchedflux = false;
        }
    }

    if (guarding) {
        switch (mode) {
        case 0: //1.7
            matrix = glm::translate<float>(matrix, glm::vec3(0.32222223281, 0.1, 0.0));
            matrix = glm::translate<float>(matrix, glm::vec3(-0.1f, 0.15f, -0.2f));
            matrix = glm::translate<float>(matrix, glm::vec3(-0.24F, 0.25f, -0.20F));
            matrix = glm::rotate<float>(matrix, -1.85F, glm::vec3(0.0F, 1.0F, 0.0F));
            matrix = glm::rotate<float>(matrix, 1.375F, glm::vec3(4.0F, 0.0F, 0.0F));
            matrix = glm::rotate<float>(matrix, 60.0F, glm::vec3(0.0F, 1.0F, 0.0F));
            break;
        case 1: //Old 1.7
            matrix = glm::translate<float>(matrix, glm::vec3(0.42222223281, 0.0, -0.16666666269302368));
            matrix = glm::translate<float>(matrix, glm::vec3(-0.1f, 0.15f, -0.2f));
            matrix = glm::translate<float>(matrix, glm::vec3(-0.24F, 0.25f, -0.20F));
            matrix = glm::rotate<float>(matrix, -1.98F, glm::vec3(0.0F, 1.0F, 0.0F));
            matrix = glm::rotate<float>(matrix, 1.30F, glm::vec3(4.0F, 0.0F, 0.0F));
            matrix = glm::rotate<float>(matrix, 60.0F, glm::vec3(0.0F, 1.0F, 0.0F));
            break;
        case 2: //Push
            matrix = glm::translate<float>(matrix, glm::vec3(0.32222223281, 0.1, 0.0));
            matrix = glm::translate<float>(matrix, glm::vec3(-0.1f, 0.15f, -0.2f));
            matrix = glm::translate<float>(matrix, glm::vec3(-0.24F, 0.25f, -0.20F));
            matrix = glm::rotate<float>(matrix, -1.85F, glm::vec3(0.0F, 1.0F, 0.0F));
            matrix = glm::rotate<float>(matrix, 1.375F, glm::vec3(4.0F, 0.0F, 0.0F));
            matrix = glm::rotate<float>(matrix, 60.0F, glm::vec3(0.0F, 1.0F, 0.0F));
            break;
        case 3: //Vanilla
            matrix = glm::translate<float>(matrix, glm::vec3(0.32222223281, 0.1, 0.0));
            matrix = glm::translate<float>(matrix, glm::vec3(-0.1f, 0.15f, -0.2f));
            matrix = glm::translate<float>(matrix, glm::vec3(-0.24F, 0.25f, -0.20F));
            matrix = glm::rotate<float>(matrix, -1.85F, glm::vec3(0.0F, 1.0F, 0.0F));
            matrix = glm::rotate<float>(matrix, 1.375F, glm::vec3(4.0F, 0.0F, 0.0F));
            matrix = glm::rotate<float>(matrix, 60.0F, glm::vec3(0.0F, 1.0F, 0.0F));
            break;
        case 4: //Exhi
            matrix = glm::translate<float>(matrix, glm::vec3(0.32222223281, 0.1, 0.0));
            matrix = glm::translate<float>(matrix, glm::vec3(-0.1f, 0.15f, -0.2f));
            matrix = glm::translate<float>(matrix, glm::vec3(-0.24F, 0.25f, -0.20F));
            matrix = glm::rotate<float>(matrix, -1.85F, glm::vec3(0.0F, 1.0F, 0.0F));
            matrix = glm::rotate<float>(matrix, 1.375F, glm::vec3(4.0F, 0.0F, 0.0F));
            matrix = glm::rotate<float>(matrix, 60.0F, glm::vec3(0.0F, 1.0F, 0.0F));
            break;
        case 5: //Sigma
            matrix = glm::translate<float>(matrix, glm::vec3(0.32222223281, -0.017, 0.025));
            matrix = glm::translate<float>(matrix, glm::vec3(-0.1f, 0.15f, -0.2f));
            matrix = glm::translate<float>(matrix, glm::vec3(-0.24F, 0.25f, -0.20F));
            matrix = glm::rotate<float>(matrix, -1.85F, glm::vec3(0.0F, 1.0F, 0.0F));
            matrix = glm::rotate<float>(matrix, 1.375F, glm::vec3(4.0F, 0.0F, 0.0F));
            matrix = glm::rotate<float>(matrix, 60.0F, glm::vec3(0.0F, 1.0F, 0.0F));
            break;
        case 6: //Old Push
            matrix = glm::translate<float>(matrix, glm::vec3(0.32222223281, -0.017, 0.025));
            matrix = glm::translate<float>(matrix, glm::vec3(-0.1f, 0.15f, -0.2f));
            matrix = glm::translate<float>(matrix, glm::vec3(-0.24F, 0.25f, -0.20F));
            matrix = glm::rotate<float>(matrix, -1.85F, glm::vec3(0.0F, 1.0F, 0.0F));
            matrix = glm::rotate<float>(matrix, 1.375F, glm::vec3(4.0F, 0.0F, 0.0F));
            matrix = glm::rotate<float>(matrix, 60.0F, glm::vec3(0.0F, 1.0F, 0.0F));
            break;
        }
    }
    else if (queue) {
        queue = false;
    }

}

auto Animations::onDimensionChanged() -> void {
    regetvelocity = true;
}

auto Animations::onClient() -> void {
    if (!enabled) return;

    static bool back = false;
    if (!switchanimation) {
        back = true;
        Client::disableSwitchAnim = true;
    }
    else if (back) {
        Client::disableSwitchAnim = false;
        back = false;
    }

    if (minimalbobbing && !patchedviewbob) {
        DWORD oldProtect;
        VirtualProtect(viewbobbing_, 6, PAGE_EXECUTE_READWRITE, &oldProtect);
        memcpy(viewbobbing_, _("\x90\x90\x90\x90\x90\x90"), 6);
        VirtualProtect(viewbobbing_, 6, oldProtect, &oldProtect);
        patchedviewbob = true;
    }
    else if (!minimalbobbing && patchedviewbob) {
        DWORD oldProtect;
        VirtualProtect(viewbobbing_, 6, PAGE_EXECUTE_READWRITE, &oldProtect);
        memcpy(viewbobbing_, ogBytes_viewbobbing, 6);
        VirtualProtect(viewbobbing_, 6, oldProtect, &oldProtect);

        patchedviewbob = false;
    }
}

auto Animations::onDisabled() -> void {
    if (patchedflux) {
        DWORD oldProtect;
        VirtualProtect(flux_, 5, PAGE_EXECUTE_READWRITE, &oldProtect);
        memcpy(flux_, ogBytes_flux, 5);
        VirtualProtect(flux_, 5, oldProtect, &oldProtect);
        patchedflux = false;
    }

    if (patchedviewbob) {
        DWORD oldProtect;
        VirtualProtect(viewbobbing_, 6, PAGE_EXECUTE_READWRITE, &oldProtect);
        memcpy(viewbobbing_, ogBytes_viewbobbing, 6);
        VirtualProtect(viewbobbing_, 6, oldProtect, &oldProtect);
        patchedviewbob = false;
    }

    if (swingangle_)
        *swingangle_ = -1.25664f;
}

auto Animations::getMode() -> std::string {
    switch (mode) {
    case 0:
        return _("1.7");
    case 1:
        return _("1.7 Old");
    case 2:
        return _("Push");
    case 3:
        return _("Vanilla");
    case 4:
        return _("Exhi");
    case 5:
        return _("Sigma");
    case 6:
        return _("Nigger");
    }

    return _("Unknown");
}
