/******************************************************************************
*	File		-	coreAPI.cpp
*	Author		-	Joey Pollack
*	Date		-	2021/09/23 (y/m/d)
*	Mod Date	-	2021/09/23 (y/m/d)
*	Description	-	class with all static methods that expose core functionality to LUA
*                   Need an init function that takes a reference to a LUA state so that it
*                   can expose the static functions to LUA.
******************************************************************************/

#include "coreAPI.h"
#include "wren_state.h"
#include <core/core.h>
#include <platform/key_codes.h>
#include <utils/logger.h>
#include <utils/helpers.h>

namespace lunarium
{
    CoreAPI* CoreAPI::mpInstance = nullptr;
    WrenScript CoreAPI::mScript;
    CoreAPI& CoreAPI::GetInstance()
    {
        if (mpInstance == nullptr)
        {
            mpInstance = new CoreAPI;
        }

        return *mpInstance;
    }

    void CoreAPI::FreeInstance()
    {
        delete mpInstance;
        mpInstance = nullptr;
    }

    OpRes CoreAPI::Initialize(WrenState& sman)
    {
        mCat = sman.GetLogCat();

        // Register Methods
        sman.RegisterForeignMethod({"CoreAPI", "Core", true, "IsKeyDown(_)", (ForeignMethod)&CoreAPI::IsKeyDown});

        // Generate Code
        std::string keys_script = GenerateWrenKeyCodes();
        sman.RunSnippet("KeyCodes", keys_script);

            // DEBUG - SAVE GENERATED SCRIPT TO FILE FOR REVIEW
            File::WriteTextFile("key_codes.wren", keys_script);


        // Load Main Script
        mScript.SetModuleName("CoreAPI");
        std::string code = File::ReadTextFile("core_api.wren");  // This will eventually move to internal data
        mScript.SetScriptCode(code);
        sman.RunScript(&mScript);

        // Get Method Handles

       // return OpRes::Fail("CoreAPI::Initialize not implemented yet!");
       return OpRes::OK();
    }
    
    std::string CoreAPI::GenerateWrenKeyCodes()
    {
        std::string key_codes = "class KeyCodes {\n";

        for (auto iter = KeyCodeMap.begin(); iter != KeyCodeMap.end(); iter++)
        {
            key_codes += "\tstatic ";
            key_codes += iter->first;
            key_codes += "() { ";
            key_codes += std::to_string((u32)iter->second);
            key_codes += " }\n";
        }

        key_codes += "}";

        return key_codes;
    }

    ////////////////////////////////////////////////////////////
    //				API
    ////////////////////////////////////////////////////////////

    void CoreAPI::SetWindowSize(int w, int h)
    {
        Core::MainWindow().Resize(w, h);
    }

    void CoreAPI::Log(int level, const char* msg)
    {
        switch (level)
        {
            case LogLevel::FATAL_ERROR: { Logger::Fatal(mpInstance->mCat, msg); break; }
            case LogLevel::ERROR:       { Logger::Error(mpInstance->mCat, msg); break; }
            case LogLevel::WARNING:     { Logger::Warn(mpInstance->mCat, msg); break; }
            case LogLevel::INFO:        { Logger::Info(mpInstance->mCat, msg); break; }
            case LogLevel::DEBUG:       { Logger::Debug(mpInstance->mCat, msg); break; }
            default:                    { Logger::Trace(mpInstance->mCat, msg); break; }
        }
    }
    
    void CoreAPI::IsKeyDown(WrenVM* vm)
    {
        u32 key_code = (u32)wrenGetSlotDouble(vm, 1);
        bool result = Core::Input().IsKeyDown((KeyCode)key_code, true);
        wrenSetSlotBool(vm, 0, result);
    }
}