/******************************************************************************
*	File		-	component_api.cpp
*	Author		-	Joey Pollack
*	Date		-	2023/01/05 (y/m/d)
*	Mod Date	-	2023/01/05 (y/m/d)
*	Description	-	static methods for all component related foreign classes
******************************************************************************/

#include "component_api.h"

#include  <world/components.h>
#include  <world/world.h>
#include  <world/entity.h>

namespace lunarium
{
    World* ComponentAPI::mpWorld = nullptr;
    void ComponentAPI::Initialize(World* pWorld, WrenState& script_state)
    {
        mpWorld = pWorld;
        SetupForeignClassVelocityComp(script_state);
        SetupForeignClassBlockOutComp(script_state);
    }

    /////////////////////////////////////////////////////////////////////
    //				VELOCITY COMPONENT
    /////////////////////////////////////////////////////////////////////
    void ComponentAPI::SetupForeignClassVelocityComp(WrenState& script_state)
    {
        script_state.RegisterForeignClass({"Components", "VelocityComponent", sizeof(VelocityComponent*), 
                                            {(WrenForeignMethodFn)&ComponentAPI::VelocityCompAllocate, &ComponentAPI::VelocityCompFinalise}});

        script_state.RegisterForeignMethod({"Components", "VelocityComponent", false, "GetVelocityX()",&ComponentAPI::GetVelocityXFM});
        script_state.RegisterForeignMethod({"Components", "VelocityComponent", false, "GetVelocityY()",(WrenForeignMethodFn)&ComponentAPI::GetVelocityYFM});
        script_state.RegisterForeignMethod({"Components", "VelocityComponent", false, "SetVelocity(_,_)", (WrenForeignMethodFn)&ComponentAPI::SetVelocityFM});
    }

    void ComponentAPI::VelocityCompAllocate(WrenVM* vm)
    {
        VelocityComponent** vcp = (VelocityComponent**)wrenSetSlotNewForeign(vm, 0, 0, sizeof(VelocityComponent*));
        std::string str_entity_id = wrenGetSlotString(vm, 1);
        LUUID entity_id = std::stoull(str_entity_id);
        Entity* pEnt = mpWorld->GetEntity(entity_id);
        if (!pEnt->HasComponent<VelocityComponent>())
        {
            Logger::Error(LogCategory::SCRIPTING, "Cannot allocate foreign class VelocityComponent - the entity does not have the component");
            (*vcp) = nullptr;
        }

        (*vcp) = &pEnt->GetComponent<VelocityComponent>();
    }

    void ComponentAPI::VelocityCompFinalise(void* data)
    {
        // Nothing to clean up
    }

    void ComponentAPI::GetVelocityXFM(WrenVM* vm)
    {
        VelocityComponent** vcp = (VelocityComponent**)wrenGetSlotForeign(vm, 0);
        wrenEnsureSlots(vm, 1);
        wrenSetSlotDouble(vm, 0, (*vcp)->Velocity.x);
    }

    void ComponentAPI::GetVelocityYFM(WrenVM* vm)
    {
        VelocityComponent** vcp = (VelocityComponent**)wrenGetSlotForeign(vm, 0);
        wrenEnsureSlots(vm, 1);
        wrenSetSlotDouble(vm, 0, (*vcp)->Velocity.y);
    }

    void ComponentAPI::SetVelocityFM(WrenVM* vm)
    {
        VelocityComponent** vcp = (VelocityComponent**)wrenGetSlotForeign(vm, 0);
        double x = wrenGetSlotDouble(vm, 1);
        double y = wrenGetSlotDouble(vm, 2);

        (*vcp)->Velocity.x = x;
        (*vcp)->Velocity.y = y;
    }

    /////////////////////////////////////////////////////////////////////
    //				BLOCKOUT COMPONENT
    /////////////////////////////////////////////////////////////////////
    void ComponentAPI::SetupForeignClassBlockOutComp(WrenState& script_state)
    {
        script_state.RegisterForeignClass({"Components", "BlockOutComponent", sizeof(BlockOutComponent*), 
                                            {(WrenForeignMethodFn)&ComponentAPI::BlockOutCompAllocate, &ComponentAPI::BlockOutCompFinalise}});

        script_state.RegisterForeignMethod({"Components", "BlockOutComponent", false, "GetColor()",&ComponentAPI::GetColorFM});
        script_state.RegisterForeignMethod({"Components", "BlockOutComponent", false, "GetSize()",(WrenForeignMethodFn)&ComponentAPI::GetSizeFM});
        script_state.RegisterForeignMethod({"Components", "BlockOutComponent", false, "SetColor(_,_,_,_)", (WrenForeignMethodFn)&ComponentAPI::SetColorFM});
        script_state.RegisterForeignMethod({"Components", "BlockOutComponent", false, "SetSize(_,_)", (WrenForeignMethodFn)&ComponentAPI::SetSizeFM});
    }

    void ComponentAPI::BlockOutCompAllocate(WrenVM* vm)
    {
        BlockOutComponent** vcp = (BlockOutComponent**)wrenSetSlotNewForeign(vm, 0, 0, sizeof(BlockOutComponent*));
        std::string str_entity_id = wrenGetSlotString(vm, 1);
        LUUID entity_id = std::stoull(str_entity_id);
        Entity* pEnt = mpWorld->GetEntity(entity_id);
        if (!pEnt->HasComponent<BlockOutComponent>())
        {
            Logger::Error(LogCategory::SCRIPTING, "Cannot allocate foreign class BlockOutComponent - the entity does not have the component");
            (*vcp) = nullptr;
        }

        (*vcp) = &pEnt->GetComponent<BlockOutComponent>();
    }

    void ComponentAPI::BlockOutCompFinalise(void* data)
    {
        // Nothing to clean up
    }

    void ComponentAPI::GetColorFM(WrenVM* vm)
    {
        BlockOutComponent** vcp = (BlockOutComponent**)wrenGetSlotForeign(vm, 0);
        wrenEnsureSlots(vm, 5);
        wrenSetSlotDouble(vm, 1, (*vcp)->Color.r);
        wrenSetSlotDouble(vm, 2, (*vcp)->Color.g);
        wrenSetSlotDouble(vm, 3, (*vcp)->Color.b);
        wrenSetSlotDouble(vm, 4, (*vcp)->Color.a);
        wrenSetSlotNewList(vm, 0);
        wrenInsertInList(vm, 0, 0, 4);
        wrenInsertInList(vm, 0, 0, 3);
        wrenInsertInList(vm, 0, 0, 2);
        wrenInsertInList(vm, 0, 0, 1);
    }
    

    void ComponentAPI::GetSizeFM(WrenVM* vm)
    {
        BlockOutComponent** vcp = (BlockOutComponent**)wrenGetSlotForeign(vm, 0);
        wrenEnsureSlots(vm, 3);
        wrenSetSlotDouble(vm, 1, (*vcp)->Size.x);
        wrenSetSlotDouble(vm, 2, (*vcp)->Size.y);
        wrenSetSlotNewList(vm, 0);
        wrenInsertInList(vm, 0, 0, 2);
        wrenInsertInList(vm, 0, 0, 1);
    }

    void ComponentAPI::SetColorFM(WrenVM* vm)
    {
        BlockOutComponent** vcp = (BlockOutComponent**)wrenGetSlotForeign(vm, 0);
        (*vcp)->Color.r = wrenGetSlotDouble(vm, 1);
        (*vcp)->Color.g = wrenGetSlotDouble(vm, 2);
        (*vcp)->Color.b = wrenGetSlotDouble(vm, 3);
        (*vcp)->Color.a = wrenGetSlotDouble(vm, 4);
    }

    void ComponentAPI::SetSizeFM(WrenVM* vm)
    {
        BlockOutComponent** vcp = (BlockOutComponent**)wrenGetSlotForeign(vm, 0);
        (*vcp)->Size.x = wrenGetSlotDouble(vm, 1);
        (*vcp)->Size.y = wrenGetSlotDouble(vm, 2);
    }

}