#pragma once

#include <state/gamestate.hpp>
#include <object/entity.hpp>
#include <utils.hpp>
#include <thread>
#include <mutex>
#include <chrono>
#include <functional>
#include <object/camera.hpp>
#include <game/input.hpp>
#include <unordered_map>
#include <memory>
#include <type_traits>

namespace Game {
    using clock = std::chrono::steady_clock;

    enum class GameStateEnum {
        RUNNING,
        PAUSED,
        STOPPED
    };

    enum class SharedDataType {
        STRING,
        INT,
        FLOAT,
        BOOL
    };
    
    class GameManager {
        public:
            GameManager() { LOG("Created GameManager"); };
            DISABLE_COPY_AND_MOVE(GameManager);
            ~GameManager() = default;

            // Start the game logic slave thread, which will update the gamestate and entities; Run as jthread
            void run(std::stop_token stopToken);
            void setTargetUpdatesPerSecond(int target) { mTargetUpdatesPerSecond = target; }
            int getTargetUpdatesPerSecond() { return mTargetUpdatesPerSecond; }
            float getLastDelta() { return mLastDelta; }
            
            template<typename T>
            static void setSharedData(const std::string& key, T data);
            template<typename T>
            static T getSharedData(const std::string& key);
            static void removeSharedData(const std::string& key, SharedDataType type);

            static GameStateEnum getCurrentGameState() { return mCurrentGameState; }
            static void setCurrentGameState(GameStateEnum newState) { mCurrentGameState = newState; }

        private:
            int mTargetUpdatesPerSecond = TARGET_UPDATE_RATE;
            clock::time_point mLastUpdate;
            static std::unordered_map<std::string, std::string> mSharedStrings;
            static std::unordered_map<std::string, int> mSharedInts;
            static std::unordered_map<std::string, float> mSharedFloats;
            static std::unordered_map<std::string, bool> mSharedBools;
            static GameStateEnum mCurrentGameState;
            float mLastDelta = 0.f;
    };

    template<typename T>
    void GameManager::setSharedData(const std::string& key, T data) {
        if constexpr (std::is_same_v<T, std::string>) {
            mSharedStrings[key] = data;
        } else if constexpr (std::is_same_v<T, int>) {
            mSharedInts[key] = data;
        } else if constexpr (std::is_same_v<T, float>) {
            mSharedFloats[key] = data;
        } else if constexpr (std::is_same_v<T, bool>) {
            mSharedBools[key] = data;
        }
    }

    template<typename T>
    T GameManager::getSharedData(const std::string& key) {
        if constexpr (std::is_same_v<T, std::string>) {
            auto it = mSharedStrings.find(key);
            if (it != mSharedStrings.end()) {
                return it->second;
            } else {
                return "";
            }
        } else if constexpr (std::is_same_v<T, int>) {
            auto it = mSharedInts.find(key);
            if (it != mSharedInts.end()) {
                return it->second;
            } else {
                return 0;
            }
        } else if constexpr (std::is_same_v<T, float>) {
            auto it = mSharedFloats.find(key);
            if (it != mSharedFloats.end()) {
                return it->second;
            } else {
                return 0.0f;
            }
        } else if constexpr (std::is_same_v<T, bool>) {
            auto it = mSharedBools.find(key);
            if (it != mSharedBools.end()) {
                return it->second;
            } else {
                return false;
            }
        }

        static_assert(
            std::is_same_v<T, std::string> || std::is_same_v<T, int> ||
            std::is_same_v<T, float> || std::is_same_v<T, bool>,
            "Unsupported type for shared data"
        );

        return T{};
    }
}