/******************************************************************************
*	File		-	world.cpp
*	Author		-	Joey Pollack
*	Date		-	2022/01/12 (y/m/d)
*	Mod Date	-	2022/01/20 (y/m/d)
*	Description	-	Manages a game "world". A world is made up of regions which
*                   are subdivisions of the world. Each region contains: a set 
*                   of images for the maps layers, a list of objects that spawn
*                   in this region and static collision data.
******************************************************************************/

#include "world.h"
#include <utils/logger.h>
#include <assets/types/script.h>
#include <assets/types/image.h>
#include <renderer/orthographic_camera.h>
#include "entity.h"

namespace lunarium
{
    // World::World(Camera* pCam, Sizei region_size, Sizei world_size)
    //     : mpCamera(pCam), mRegionSize(region_size), mpWorldScript(nullptr), mWorldSize(world_size)
    // {
    //     mActiveRegion = { 0, 0 };
    // }

    World::World(std::string name)
        : mUUID(UUID::GetNewID()), mName(name), mpActiveCamera(nullptr)
    {

    }

    void World::OnLoad()
    {
        // TODO: Call OnLoad in the world script and on each region script
    }
    
    void World::OnUnload()
    {
        // TODO: Call OnUnLoad in the world script and on each region script
    }
    
    void World::Update(float dt)
    {
        // TODO: Update all entities
    }
    
    void World::Render(lunarium::IGraphics* pGraphics) const
    {
        OrthographicCamera* pCam = mpActiveCamera;
        
        if (!pCam)
        {
            // Get a camera component from an entity
        }

        // Get ViewProject matrix
        // Render each entity that has a renderable component and a transform
    }    

    
    void World::SetActiveCamera(OrthographicCamera* pCam)
    {
        mpActiveCamera = pCam;
    }

    entt::registry* World::GetEntityRegistry()
    {
        return &mECSRegistry;
    }

    LUUID World::CreateEntity()
    {
        //Logger::Error(LogCategory::GAME_SYSTEM, "World::CreateEntity not implemented!");
        Entity* new_ent = new Entity(*this);

        if (mEntitiesByUUID.find(new_ent->GetUUID()) != mEntitiesByUUID.end())
        {
            Logger::Warn(LogCategory::GAME_SYSTEM, "UUID collision when creating new entity! UUID: %d", new_ent->GetUUID());
        }

        mEntitiesByUUID[new_ent->GetUUID()] = new_ent;
        mEntities.push_back(new_ent);
        return new_ent->GetUUID();
    }

    
    Entity* World::GetEntity(LUUID id)
    {
        if (mEntitiesByUUID.find(id) == mEntitiesByUUID.end())
        {
            Logger::Warn(LogCategory::GAME_SYSTEM, "Entity with id: %d not found.", id);
        }
        return mEntitiesByUUID[id];
    }

    unsigned int World::GetNumEntities() const
    {
        return mEntities.size();
    }
    
    std::vector<Entity*>::iterator World::EntitiesBegin()
    {
        return mEntities.begin();
    }

    bool World::EntitiesIsEnd(std::vector<Entity*>::iterator& iter)
    {
        return iter == mEntities.end();
    }

    /////////////////////////////////////////////////////////////////////
    //				SERIALIZING
    /////////////////////////////////////////////////////////////////////
    OpRes World::Serialize(nlohmann::ordered_json& node)
    {
        #if !BUILD_NO_EDITOR // Only does this when this is an editor build

        node["UUID"] = mUUID;

        node["NumberOfEntities"] = mEntities.size();
        auto& ents = node["Entities"];
        for (int i = 0; i < mEntities.size(); i++)
        {
            nlohmann::ordered_json e;
            mEntities[i]->Serialize(e).LogIfFailed(LogCategory::GAME_SYSTEM);
            ents.emplace_back(e);
        }

        #endif
        return OpRes::OK();
    }

    OpRes World::Deserialize(nlohmann::ordered_json& node)
    {
        #if !BUILD_NO_EDITOR // Only does this when this is an editor build

        mUUID = node["UUID"].get<u64>();
        int num_entities = node["NumberOfEntities"].get<int>();
        auto& ents = node["Entities"];
        for (auto it = ents.begin(); it != ents.end(); ++it)
        {
            auto& ent = (*it);
            Entity* new_ent = new Entity(*this);

            if (!new_ent->IsValidNode(ent))
            {
                delete new_ent;
                return OpRes::Fail("Invalid entity node");
            }

            new_ent->Deserialize(ent).LogIfFailed(LogCategory::GAME_SYSTEM);
            mEntities.push_back(new_ent);
        }

        #endif
        return OpRes::OK();
    }
    
    bool World::IsValidNode(nlohmann::ordered_json& node)
    {
        if (node["UUID"].is_null()) { return false; }
        if (!node["UUID"].is_number()) { return false; }

        if (node["NumberOfEntities"].is_null()) { return false; }
        if (!node["NumberOfEntities"].is_number()) { return false; }

        if (node["Entities"].is_null()) { return false; }


        return false;
    }
    
    nlohmann::ordered_json World::AsJSON()
    {
        #if !BUILD_NO_EDITOR // Only does this when this is an editor build
        nlohmann::ordered_json node;

        Serialize(node).LogIfFailed(LogCategory::GAME_SYSTEM);

        return node;
        #endif

        return nlohmann::ordered_json();
    }

}