//-----------------------------------------------------------------
// Main Game File
// C++ Source - Game.cpp - version v8_01
//-----------------------------------------------------------------

//-----------------------------------------------------------------
// Include Files
//-----------------------------------------------------------------
#include "Game.h"

#include <filesystem>

//-----------------------------------------------------------------
// Game Member Functions																				
//-----------------------------------------------------------------

Game::Game(const std::string &fileName): m_fileName(fileName) {
    // nothing to create
//    cpr::Response r = cpr::Get(cpr::Url{"http://www.httpbin.org/get"});
//    std::cout << r.text << std::endl;
//    int result = MessageBox(hWnd, "Enter a string?", "Input Request", MB_OKCANCEL);
//    if (result == IDOK) {
//         User pressed OK
//    }

    m_specialKeys["SHIFT"] = VK_SHIFT;
    m_specialKeys["LEFT"] = VK_LEFT;
    m_specialKeys["UP"] = VK_UP;
    m_specialKeys["RIGHT"] = VK_RIGHT;
    m_specialKeys["DOWN"] = VK_DOWN;
    m_specialKeys["ESCAPE"] = VK_ESCAPE;
    m_specialKeys["RETURN"] = VK_RETURN;
}

Game::~Game() {
    // nothing to destroy
}

void Game::Initialize() {
    // Code that needs to execute (once) at the start of the game, before the game window is created

    //Check if the file exists using fs
    if (!std::filesystem::exists(m_fileName)) {
        GAME_ENGINE->MessageBox("File does not exist");
        return;
    }

    try{
        const auto scriptResult = m_state.safe_script_file(m_fileName);
        if (!scriptResult.valid()) {
            //Handle Error
            const sol::error err = scriptResult;
            std::cerr << "Load Script Error: " << err.what();
            GAME_ENGINE->MessageBox(err.what());
        }
    } catch (const sol::error& e) {
        std::cerr << "Error loading script: " << e.what() << std::endl;
        GAME_ENGINE->MessageBox(e.what());
    }


    AbstractGame::Initialize();
//    GAME_ENGINE->SetTitle(_T("Game Engine version 8_01"));

    this->FunctionCall<void>("setup_window");

    // Set the keys that the game needs to listen to
    //Get return value from Lua function
    auto keyList = this->FunctionCall<std::string>("set_keylist");

    //Somewhat dirty
    keyList += char(VK_SHIFT);
    keyList += char(VK_LEFT);
    keyList += char(VK_RIGHT);
    keyList += char(VK_UP);
    keyList += char(VK_DOWN);
    keyList += char(VK_ESCAPE);
    keyList += char(VK_RETURN);

    GAME_ENGINE->SetKeyList(keyList);

//    tstringstream buffer;
//    buffer << _T("W");
//    buffer << (char) VK_LEFT;
//    buffer << (char) VK_RIGHT;
//    GAME_ENGINE->SetKeyList(buffer.str());

}

void Game::Start() {
    // Insert code that needs to execute (once) at the start of the game, after the game window is created
    this->FunctionCall("start");
}

void Game::End() {
    // Insert code that needs to execute when the game ends
    this->FunctionCall("quit");
}

void Game::Paint(RECT rect) const {
    this->FunctionCall("draw");
}

void Game::Tick() {
    this->FunctionCall("update");
}

void Game::MouseButtonAction(bool isLeft, bool isDown, int x, int y, WPARAM wParam) {
    // Insert code for a mouse button action

    /* Example:
    if (isLeft == true && isDown == true) // is it a left mouse click?
    {
        if ( x > 261 && x < 261 + 117 ) // check if click lies within x coordinates of choice
        {
            if ( y > 182 && y < 182 + 33 ) // check if click also lies within y coordinates of choice
            {
                GAME_ENGINE->MessageBox(_T("Clicked."));
            }
        }
    }
    */
}

void Game::MouseWheelAction(int x, int y, int distance, WPARAM wParam) {
    // Insert code for a mouse wheel action
}

void Game::MouseMove(int x, int y, WPARAM wParam) {
    // Insert code that needs to execute when the mouse pointer moves across the game window

    /* Example:
    if ( x > 261 && x < 261 + 117 ) // check if mouse position is within x coordinates of choice
    {
        if ( y > 182 && y < 182 + 33 ) // check if mouse position also is within y coordinates of choice
        {
            GAME_ENGINE->MessageBox("Mouse move.");
        }
    }
    */

}

void Game::CheckKeyboard() {
    // Here you can check if a key is pressed down
    // Is executed once per frame

//    if (GAME_ENGINE->IsKeyDown(_T('W'))) {
//        GAME_ENGINE->MessageBox("W key pressed");
//    }
    /* Example:
    if (GAME_ENGINE->IsKeyDown(_T('L'))) yIcon += xSpeed;
    if (GAME_ENGINE->IsKeyDown(_T('M'))) xIcon += xSpeed;
    if (GAME_ENGINE->IsKeyDown(_T('O'))) yIcon -= ySpeed;
    */
}

void Game::KeyPressed(TCHAR key) {
    // DO NOT FORGET to use SetKeyList() !!

    // Insert code that needs to execute when a key is pressed
    // The function is executed when the key is *released*
    // You need to specify the list of keys with the SetKeyList() function



    //* Example:
//    switch (key)
//    {
//    case _T('K'): case VK_LEFT:
//        GAME_ENGINE->MessageBox("Moving left.");
//        break;
//    case _T('L'): case VK_DOWN:
//        GAME_ENGINE->MessageBox("Moving down.");
//        break;
//    case _T('M'): case VK_RIGHT:
//        GAME_ENGINE->MessageBox("Moving right.");
//        break;
//    case _T('O'): case VK_UP:
//        GAME_ENGINE->MessageBox("Moving up.");
//        break;
//    case VK_ESCAPE:
//        GAME_ENGINE->MessageBox("Escape menu.");
//    }

//    switch(key){
//        case _T('W'):
//            GAME_ENGINE->MessageBox("W key pressed");
//            break;
//    }
}

void Game::CallAction(Caller *callerPtr) {
    // Insert the code that needs to execute when a Caller (= Button, TextBox, Timer, Audio) executes an action
}

void Game::InitialiseBindings() {
    std::cout << "Initialising bindings" << std::endl;
    m_state.open_libraries(sol::lib::base, sol::lib::package, sol::lib::string, sol::lib::table, sol::lib::math, sol::lib::os, sol::lib::io);


    m_state.new_usertype<ColorRGB>(
            "Color",
            sol::constructors<ColorRGB(float, float, float)>(),
            "r", &ColorRGB::r,
            "g", &ColorRGB::g,
            "b", &ColorRGB::b
    );

    m_state.new_usertype<GameEngine>("GameEngine",
                                     "setTitle", &GameEngine::SetTitle,
                                     "setWidth", &GameEngine::SetWidth,
                                     "setHeight", &GameEngine::SetHeight,
                                     "getWidth", &GameEngine::GetWidth,
                                     "getHeight", &GameEngine::GetHeight,
                                     "setFrameRate", &GameEngine::SetFrameRate,
                                     "setWindowPosition", &GameEngine::SetWindowPosition,
                                     "setWindowRegion", &GameEngine::SetWindowRegion,
                                     "setKeyList", &GameEngine::SetKeyList,
                                     "setColor", &GameEngine::SetColorRGB,
                                     "quit", [](){
                                            PostQuitMessage(0);
                                     },
                                     "messageBox", [](GameEngine &gameEngine, const std::string &message) {
                                         gameEngine.MessageBox(message.c_str());
                                     },

                                     "drawBitmap", sol::overload(
                                                sol::resolve<bool(const Bitmap*, int, int) const>(&GameEngine::DrawBitmap),
                                                sol::resolve<bool(const Bitmap*, int, int, RECT) const>(&GameEngine::DrawBitmap)
                                     ),

                                     "getMouseX", &GameEngine::GetMouseX,
                                     "getMouseY", &GameEngine::GetMouseY,
                                     "isMouseLeftDown", &GameEngine::IsMouseLeftDown,
                                     "isMouseRightDown", &GameEngine::IsMouseRightDown,
                                     "getName", &GameEngine::GetTitle,

                                     "drawRect", sol::overload(
                                             sol::resolve<bool(int, int, int, int) const>(&GameEngine::DrawRect),
                                             sol::resolve<bool(float, float, float, float) const>(&GameEngine::DrawRect)
                                     ),
                                     "fillRect", sol::overload(
                                                sol::resolve<bool(int, int, int, int) const>(&GameEngine::FillRect),
                                            sol::resolve<bool(float, float, float, float) const>(&GameEngine::FillRect)
                                     ),
                                     "drawOval", sol::overload(
                                             sol::resolve<bool(int, int, int, int) const>(&GameEngine::DrawOval),
                                                sol::resolve<bool(float, float, float, float) const>(&GameEngine::DrawOval)
                                     ),
                                     "fillOval", sol::overload(
                                             sol::resolve<bool(int, int, int, int) const>(&GameEngine::FillOval),
                                                sol::resolve<bool(float, float, float, float) const>(&GameEngine::FillOval)
                                     ),
                                     "fillScreen", &GameEngine::FillWindowRectRGB,
                                     "drawText", sol::overload(
                                             sol::resolve<int(const std::string &, int, int) const>(&GameEngine::DrawString),
                                                sol::resolve<int(const std::string &, float, float) const>(&GameEngine::DrawString),
                                                [] (GameEngine &gameEngine, int number, int x, int y) {
                                                    gameEngine.DrawString(std::to_string(number), x, y);
                                                },
                                                [] (GameEngine &gameEngine, float number, float x, float y) {
                                                    gameEngine.DrawString(std::to_string(number), x, y);
                                                }
                                     ),
                                     "isKeyDown", [&] (GameEngine &gameEngine, const std::string &key) {
                                         if(key.size() == 1){
                                             return gameEngine.IsKeyDown(_T(key[0]));
                                         } else {
                                             return gameEngine.IsKeyDown(m_specialKeys[key]);
                                         }
                                     },
                                     "getRequest", [](GameEngine &gameEngine, const std::string &url, const std::string& data = "") {
                                            return gameEngine.GetRequest(url, data);
                                     },
                                     "postRequest", [](GameEngine &gameEngine, const std::string &url, const std::string &data) {
                                         return gameEngine.PostRequest(url, data);
                                     },
                                     "GetString", [](GameEngine &gameEngine) {
                                        return std::string(gameEngine.AskString());
                                     }
    );

    m_state.new_usertype<Bitmap>(
            "Bitmap",
            sol::constructors<Bitmap(const tstring&, bool)>(),
            "SetTransparencyColor", [](Bitmap &bitmap, const ColorRGB &color) {
                bitmap.SetTransparencyColor(RGB(color.r, color.g, color.b));
            },
            "SetOpacity", &Bitmap::SetOpacity,
            "Exists", &Bitmap::Exists,
            "GetWidth", &Bitmap::GetWidth,
            "GetHeight", &Bitmap::GetHeight,
            "GetTransparencyColor", &Bitmap::GetTransparencyColor,
            "GetOpacity", &Bitmap::GetOpacity,
            "HasAlphaChannel", &Bitmap::HasAlphaChannel,
            "SaveToFile", &Bitmap::SaveToFile
    );
    m_state.new_usertype<TextBox>(
            "Textbox", sol::constructors<TextBox(const tstring&), TextBox()>(),
            "SetBounds", &TextBox::SetBounds,
            "SetText", &TextBox::SetText,
            "SetFont", &TextBox::SetFont,
            "SetBackcolor", &TextBox::SetBackcolor,
            "SetForecolor", &TextBox::SetForecolor,
            "SetEnabled", &TextBox::SetEnabled,
            "Show", &TextBox::Show,
            "Hide", &TextBox::Hide,
            "GetBounds", &TextBox::GetBounds,
            "GetText", &TextBox::GetText,
            "GetForecolor", &TextBox::GetForecolor
    );

    m_state.set("GameEngine", GAME_ENGINE);
}

//std::string Game::GetRequest(std::string url) {
//    cpr::Response r = cpr::Get(cpr::Url{url});
//    return r.text;
//}

//
//void Game::FunctionCall(const std::string &functionName) const {
//    auto func = m_state[functionName];
//    auto func_return{ func() };
//
//    if (!func_return.valid()) {
//        //Handle Error
//        const sol::error err = func_return;
//        std::cerr << "Function Call Error: " << err.what();
//        GAME_ENGINE->MessageBox(err.what());
//    }
//    //Bram Verhulst - 2024
//}




