/******************************************************************************
*	File		-	state.cpp
*	Author		-	Joey Pollack
*	Date		-	2021/08/30 (y/m/d)
*	Mod Date	-	2021/08/30 (y/m/d)
*	Description	-	Stores all of the settings for the engine.
******************************************************************************/

#include "state.h"
#include <utils/helpers.h>
#include <utils/logger.h>
#include <nlohmann/json.hpp>

#include <fstream>

namespace lunarium
{
    State State::CreateEmpty()
    {
        State s;
        s.DataDirectory = "";
        s.Display.FullScreenResolution.Width = 0;
        s.Display.FullScreenResolution.Height = 0;
        s.Display.Renderer = RenderSystem::OPENGL;
        s.Display.IsFullScreen = false;
        s.Display.VSyncEnabled = false;
        s.Display.WindowedSize.Width = 0;
        s.Display.WindowedSize.Height = 0;
        s.Display.WindowStartPosition.X = 0;
        s.Display.WindowStartPosition.Y = 0;
        s.Interface.MainFont = "";
        s.Mode = RunMode::MODE_TEST;

        return s;
    }

    State State::CreateDefault()
    {

        State s;
        s.DataDirectory = "data/";
        Sizei size = System::GetScreenResolution();
        s.Display.FullScreenResolution.Width = size.Width;
        s.Display.FullScreenResolution.Height = size.Height;
        s.Display.Renderer = RenderSystem::OPENGL;
        s.Display.IsFullScreen = false;
        s.Display.VSyncEnabled = true;
        s.Display.WindowedSize.Width = 800;
        s.Display.WindowedSize.Height = 600;
        s.Display.WindowStartPosition.X = 100;
        s.Display.WindowStartPosition.Y = 100;
        s.Interface.MainFont = "";
        s.Mode = RunMode::MODE_TEST;

        s.LoadedFile = "default_state.xml";

        return s;
    }

    OpRes State::CreateFromFile(std::string filename, State& state)
    {     
        std::ifstream ifs = std::ifstream(filename.c_str());

        if (!ifs.is_open())
        {
            return OpRes::Fail((std::string("Could not open file: ") + filename).c_str());
        }

        // std::string buffer;
        // char raw_buf[256];
        // while (true)
        // {
        //     ifs.getline(raw_buf, 256);
        //     if (ifs.eof())
        //         break;

        //     buffer += raw_buf;
        // }

        // nlohmann::json j = nlohmann::json::parse(buffer.c_str());

        nlohmann::json j;
        ifs >> j;
        ifs.close();

        if (j["State"].is_null())
        {
            return OpRes::Fail((filename + " does not contain a State element").c_str());
        }

        if (!j["State"]["DataDirectory"].is_null())
        {
            state.DataDirectory = j["State"]["DataDirectory"].get<std::string>();
        }
        else
        {
            state.DataDirectory = "data/";
        }

        if (!j["TEST_NOT_HERE"].is_null())
        {
            Logger::Warn(LogCategory::CORE, "WHAT?");
        }

        if (!j["State"]["Mode"].is_null())
        {
            std::string mode = j["State"]["Mode"].get<std::string>();
            if ("test" == mode)
            {
                state.Mode = RunMode::MODE_TEST;
            }
            else if ("editor" == mode)
            {
                state.Mode = RunMode::MODE_EDITOR;
            }
            else if ("game" == mode)
            {
                state.Mode = RunMode::MODE_GAME;
            }
            else
            {
                Logger::Warn(LogCategory::CORE,
                        "Unknown Run Mode in state file: %s, mode: %s, defaulting to MODE_TEST", filename.c_str(), mode.c_str());
                state.Mode = RunMode::MODE_TEST;
            }
        }
        else
        {
            Logger::Warn(LogCategory::CORE,
                    "Can not find Run Mode in state file: %s, defaulting to MODE_TEST", filename.c_str());
            state.Mode = RunMode::MODE_TEST;
        }

        if (!j["State"]["Display"].is_null())
        {
            auto d = j["State"]["Display"];
            if (!d["FullScreenResolution"].is_null() && !d["FullScreenResolution"]["Width"].is_null() && !d["FullScreenResolution"]["Height"].is_null())
            {
                state.Display.FullScreenResolution.Width = d["FullScreenResolution"]["Width"].get<int>();
                state.Display.FullScreenResolution.Height = d["FullScreenResolution"]["Height"].get<int>();
            }
            else
            {
                Sizei size = System::GetScreenResolution();
                state.Display.FullScreenResolution.Width = size.Width;
                state.Display.FullScreenResolution.Height = size.Height;
            }

            if (!d["WindowedSize"].is_null() && !d["WindowedSize"]["Width"].is_null() && !d["WindowedSize"]["Height"].is_null())
            {
                state.Display.WindowedSize.Width = d["WindowedSize"]["Width"].get<int>();
                state.Display.WindowedSize.Height = d["WindowedSize"]["Height"].get<int>();    
            }
            else
            {
                state.Display.WindowedSize.Width = 800;
                state.Display.WindowedSize.Height = 600;
            }

            if (!d["WindowStartPosition"].is_null() && !d["WindowStartPosition"]["X"].is_null() && !d["WindowStartPosition"]["Y"].is_null())
            {
                state.Display.WindowStartPosition.X = d["WindowStartPosition"]["X"].get<int>();
                state.Display.WindowStartPosition.Y = d["WindowStartPosition"]["Y"].get<int>();    
            }
            else
            {
                state.Display.WindowStartPosition.X = 100;
                state.Display.WindowStartPosition.Y = 100;
            }

            if (!d["Renderer"].is_null())
            {
                std::string renderer = d["Renderer"].get<std::string>();
                if (String::StringToLower(renderer) == "opengl")
                {
                    state.Display.Renderer = RenderSystem::OPENGL;
                }
                else if (String::StringToLower(renderer) == "vulkan")
                {
                    state.Display.Renderer = RenderSystem::VULKAN;
                }
                else
                {
                    state.Display.Renderer = RenderSystem::UNKNOWN;
                    Logger::Warn(LogCategory::CORE, "Unknown Renderer in state file: %s, renderer: %s", filename.c_str(), renderer.c_str());
                }
            }
            else
            {
                state.Display.Renderer = RenderSystem::OPENGL;
            }

            if (!d["IsFullScreen"].is_null())
            {
                state.Display.IsFullScreen = d["IsFullScreen"].get<bool>();
            }
            else
            {
                state.Display.IsFullScreen = false;
            }

            if (!d["VSyncEnabled"].is_null())
            {
                state.Display.VSyncEnabled = d["VSyncEnabled"].get<bool>();
            }
            else
            {
                state.Display.VSyncEnabled = true;
            }
        }
        else
        {
            Sizei size = System::GetScreenResolution();
            state.Display.FullScreenResolution.Width = size.Width;
            state.Display.FullScreenResolution.Height = size.Height;
            state.Display.Renderer = RenderSystem::OPENGL;
            state.Display.IsFullScreen = false;
            state.Display.VSyncEnabled = true;
            state.Display.WindowedSize.Width = 800;
            state.Display.WindowedSize.Height = 600;
            state.Display.WindowStartPosition.X = 100;
            state.Display.WindowStartPosition.Y = 100;
        }

       if (!j["State"]["Interface"].is_null())
       {
           auto i = j["State"]["Interface"];
           if (!i["MainFont"].is_null())
           {
                state.Interface.MainFont = i["MainFont"].get<std::string>();
           }
           else
           {
                state.Interface.MainFont = "";
           }
       }

        state.LoadedFile = filename;

        return OpRes::OK();
    }

    
    OpRes State::SaveToFile()
    {
        if (LoadedFile == "")
        {
            LoadedFile = "UNNAMED_STATE.xml";
        }
        Logger::Info(LogCategory::CORE, "Saving state file: %s", LoadedFile.c_str());
        return SaveToFile(LoadedFile);
    }

    OpRes State::SaveToFile(std::string filename)
    {
        nlohmann::json j;
        auto& s = j["State"];
        s["DataDirectory"] = DataDirectory;

        const char* modes[] = { "game", "editor", "test" };
        s["Mode"] = modes[Mode];

        auto& d = s["Display"];
        const char* names[] = { "opengl", "vulkan", "unknown" };
        d["FullScreenResolution"]["Width"]  = Display.FullScreenResolution.Width;
        d["FullScreenResolution"]["Height"] = Display.FullScreenResolution.Height;
        d["WindowedSize"]["Width"]          = Display.WindowedSize.Width;
        d["WindowedSize"]["Height"]         = Display.WindowedSize.Height;
        d["WindowStartPosition"]["X"]       = Display.WindowStartPosition.X;
        d["WindowStartPosition"]["Y"]       = Display.WindowStartPosition.Y;
        d["Renderer"]                       = names[Display.Renderer];
        d["IsFullScreen"]                   = Display.IsFullScreen;
        d["VSyncEnabled"]                   = Display.VSyncEnabled;

        auto& i = s["Interface"];
        i["MainFont"] = Interface.MainFont;

        std::ofstream ofs = std::ofstream(filename.c_str());
        if (!ofs.is_open())
        {
            return OpRes::Fail("Could not save state file: %s - failed to open/create file", filename.c_str());
        }

        ofs << std::setw(4) << j;
        ofs.close();

        return OpRes::OK();
    }
}