/******************************************************************************
*	File		-	project.cpp
*	Author		-	Joey Pollack
*	Date		-	2021/11/09 (y/m/d)
*	Mod Date	-	2021/11/09 (y/m/d)
*	Description	-	Manage data for a game project
******************************************************************************/

#include "project.h"
#include <editor/editor.h>
#include <utils/logger.h>
#include "contents/content_manager.h"
#include <nlohmann/json.hpp>
#include <fstream>

namespace lunarium { namespace editor 
{
    Project::Project()
        : mIsLoaded(false)
    {

    }

    OpRes Project::GenerateProject(std::string name, std::filesystem::path location)
    {
        if (mIsLoaded)
        {
            return OpRes::Fail("A project is already loaded. Unload the current project to generate a new one.");
        }

        mName = name;
        mLocation = location;
        mLocation /= std::filesystem::path(mName).stem();

        if (!std::filesystem::exists(mLocation))
        {
            std::filesystem::create_directory(mLocation);
        }

        if (std::filesystem::exists(mLocation / mName))
        {
            return OpRes::Fail("A project with that name (%s) already exists in this location: %s", mName.c_str(), mLocation.string().c_str());
        }


        std::filesystem::path proj_doc = mLocation;
        proj_doc /= std::filesystem::path(mName);

        nlohmann::json j;
        auto& p = j["Project"];
        p["Name"] = mName;
        std::ofstream ofs = std::ofstream(proj_doc.c_str());
        if (!ofs.is_open())
        {
            return OpRes::Fail("Failed to create project file");
        }

        ofs << std::setw(4) << j;
        ofs.close();
        
        std::filesystem::create_directory(mLocation / std::filesystem::path("engine"));
        std::filesystem::create_directory(mLocation / std::filesystem::path("contents"));
        std::filesystem::create_directory(mLocation / std::filesystem::path("contents/assets"));

        mpContentManager = &ContentManager::GetInstance();
        if (Failed(mpContentManager->Load(this).LogIfFailed(Editor::LogCat)))
        {
            return OpRes::Fail("Could not load project in content manager");
        }

        mIsLoaded = true;
        return OpRes::OK();
    }

    bool Project::IsLoaded() const
    {
        return mIsLoaded;
    }

    OpRes Project::LoadProject(std::filesystem::path location)
    {
        if (!std::filesystem::exists(location))
        {
            return OpRes::Fail("Project file does not exist");
        }

        mLocation = location.parent_path();
        mName = location.filename().string();

        if (Failed(ContentManager::GetInstance().Load(this).LogIfFailed(Editor::LogCat)))
        {
            return OpRes::Fail("Could not load project: %s", location.string().c_str());
        }

        mIsLoaded = true;
        return OpRes::OK();
    }

    void Project::UnloadCurrentProject()
    {
        mName = "";
        mLocation = "";
        mIsLoaded = false;
    }

    void Project::SaveProject()
    {
        ContentManager::GetInstance().Save().LogIfFailed(Editor::LogCat);
    }
    
    const std::string& Project::GetName() const
    {
        return mName;
    }

    std::filesystem::path Project::GetRootDirectory() const
    {
        return mLocation;
    }

    std::filesystem::path Project::GetContentDirectory() const
    {
        return mLocation / "contents";
    }

    
    std::filesystem::path Project::GetAssetDirectory() const
    {
        return GetContentDirectory() / "assets";
    }

    std::filesystem::path Project::MakeRelativeToRoot(std::filesystem::path abs_path, bool include_root) const
    {
        return MakeRelativeTo(abs_path, mLocation, include_root);
    }

    
    std::filesystem::path Project::MakeRelativeToAssets(std::filesystem::path abs_path, bool include_root) const
    {
        return MakeRelativeTo(abs_path, GetAssetDirectory(), include_root);
    }

    
    std::filesystem::path Project::MakeRelativeTo(std::filesystem::path abs_path, std::filesystem::path rel_to, bool include_root) const
    {
        // Validate the passed in path
       if (!abs_path.is_absolute())
       {
            if (abs_path.filename() == std::filesystem::path(*rel_to.begin()))
            {
                // This path is already relative to the project root
                return abs_path;
            }

            Logger::Warn(Editor::LogCat, "Relative path passed into Project::MakeRelativeToRoot: %s", abs_path.string().c_str());
            return "";
       }

        std::filesystem::path new_path;
        if (include_root) new_path = rel_to.filename();

        for (auto iter = abs_path.begin(); iter != abs_path.end(); iter++)
        {
            if (rel_to.string().find((*iter).string()) == std::string::npos)
            {
                new_path /= (*iter);
            }
        }

        return new_path;
    }
    
}}