#include "InventorySync.h"

#include "Client.h"

#include "Hook/Packets.h"

auto InventorySync::onPacketSendAfter2(Packet* packet, bool* cancel) -> void {
    auto clientinstance = Client::clientinstance;
    if (!clientinstance) return;

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

    auto loopbackpacketsender = clientinstance->getLoopbackPacketSender();
    if (!loopbackpacketsender) return;

    if (packet->getID() == PacketID::Interact) {
        auto interact = static_cast<InteractPacket*>(packet);
        if (interact->getAction() == InteractPacket::Action::OpenInventory) {
            inventoryOpen = true;
            auto packet = Packet::CreatePacket(PacketID::ContainerOpen);
            auto cop = (ContainerOpenPacket*)packet.get();
            cop->setContainerID(ContainerID::Inventory);
            cop->setType(SharedTypes::Legacy::ContainerType::Inventory);
            cop->setPos(glm::vec3(0, 0, 0));

            ActorUniqueID id;
            id.rawID = -1;
            cop->setEntityUniqueID(id);

            if (Hooks::Packets::packethandlerdispatcherinstance && Hooks::Packets::networkindetifier && Hooks::Packets::neteventcallback) {
                Hooks::Packets::removeactor_receive(Hooks::Packets::packethandlerdispatcherinstance, Hooks::Packets::networkindetifier, Hooks::Packets::neteventcallback, packet);
            }

            inventoryOpen = true;
            return;
        }
    }

    if (packet->getID() == PacketID::ContainerClose) {
        auto containerclose = reinterpret_cast<ContainerClosePacket*>(packet);
        if (sendingClosePacket) return;
        if (!inventoryOpen) return;
        if (containerclose->getContainerID() == ContainerID::None || containerclose->getContainerID() == ContainerID::Inventory) {
            std::lock_guard<std::mutex> lock(queueMutex);
            inventoryOpen = false;
            while (!containerQueue.empty()) {
                {
                    auto packet = Packet::CreatePacket(PacketID::ContainerClose);
                    auto containerclose = reinterpret_cast<ContainerClosePacket*>(packet.get());
                    containerclose->setContainerID(containerQueue.front());
                    containerclose->setServerInitiatedClose(false);
                    sendingClosePacket = true;
                    loopbackpacketsender->sendToServer(containerclose);
                    sendingClosePacket = false;
                }
                containerQueue.pop();
            }
            *cancel = true;
        }
        else {
            std::lock_guard<std::mutex> lock(queueMutex);
            if (!containerQueue.empty() && containerclose->getContainerID() == containerQueue.front()) {
                containerQueue.pop();
            }
        }
    }
}

auto InventorySync::onPacketReceive(Packet* packet, bool* cancel) -> void {
    auto clientinstance = Client::clientinstance;
    if (!clientinstance) return;

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

    auto loopbackpacketsender = clientinstance->getLoopbackPacketSender();
    if (!loopbackpacketsender) return;

    if (packet->getID() == PacketID::ContainerOpen) {
        auto containeropen = reinterpret_cast<ContainerOpenPacket*>(packet);
        if (containeropen->getType() == SharedTypes::Legacy::ContainerType::Inventory) {
            inventoryOpenServerside = true;
            if (!inventoryOpen) {
                auto packet = Packet::CreatePacket(PacketID::ContainerClose);
                auto containerclose = reinterpret_cast<ContainerClosePacket*>(packet.get());
                containerclose->setContainerID(containeropen->getContainerID());
                containerclose->setServerInitiatedClose(false);
                sendingClosePacket = true;
                loopbackpacketsender->sendToServer(containerclose);
                sendingClosePacket = false;
            }
            else {
                std::lock_guard<std::mutex> lock(queueMutex);
                containerQueue.push(containeropen->getContainerID());
            }
            *cancel = true;
        }
    }
    if (packet->getID() == PacketID::ContainerClose) {
        if (inventoryOpenServerside) {
            inventoryOpenServerside = false;
        }
    }
}

auto InventorySync::onClient() -> void {
    auto clientinstance = Client::clientinstance;
    if (!clientinstance) return;

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

    auto loopbackpacketsender = clientinstance->getLoopbackPacketSender();
    if (!loopbackpacketsender) return;

    if (inventoryOpen != inventoryOpenServerside) {
        std::lock_guard<std::mutex> lock(queueMutex);
        inventoryOpen = false;
        while (!containerQueue.empty()) {
            {
                auto packet = Packet::CreatePacket(PacketID::ContainerClose);
                auto containerclose = reinterpret_cast<ContainerClosePacket*>(packet.get());
                containerclose->setContainerID(containerQueue.front());
                containerclose->setServerInitiatedClose(false);
                sendingClosePacket = true;
                loopbackpacketsender->sendToServer(containerclose);
                sendingClosePacket = false;
            }
            containerQueue.pop();
        }
    }
}

auto InventorySync::onClient4() -> void {
	name = Datas::Language::get().module.player.inventorysync.name;
	description = Datas::Language::get().module.player.inventorysync.description;
}
