/******************************************************************************
*	File		-	world.h
*	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.
******************************************************************************/

#ifndef LUNARIUM_WORLD_H_
#define LUNARIUM_WORLD_H_

#include <core/common_defs.h>
#include <core/types.h>
#include <assets/serializing/json_serializable.h>
#include <scripting/wren_state.h>
#include <scripting/wren_script.h>
#include <utils/op_res.h>
#include <entt/entt.hpp>
#include "components.h"

#include <vector>
#include <map>

struct WrenHandle;
class b2World;

namespace lunarium
{
    class Renderer2D;
    class Image;
    class Script;
    class OrthographicCamera;
    //class GameObject;
    class Entity;
    class FrameBuffer;
}

namespace lunarium 
{

    struct WorldState
    {
        nlohmann::ordered_json State;
    };

    struct EntityIterator
    {
        Entity* mEntity;
        int Index;
    };

    class World : public JSONSerializable
    {
    public:
        struct Region
        {
            bool IsActive;
            lunarium::Script* RegionScript;
            Vec2i Coords;
            std::vector<Entity*> Entities;
            std::vector<lunarium::Image*> mLayers;
        };

        // NOTE: This RunMode may not be necessary. 
        enum class RunMode
        {
            EDITOR,
            EDITOR_PLAY,
            PLAY,
            PAUSE,
        };

    public:     // INTERFACE

        //World(Camera* pCam, Sizei region_size, Sizei world_size);
        World(std::string name);

        void OnLoad();
        void OnUnload();
        void Update(float dt);
        void Render(lunarium::Renderer2D* pGraphics);

        void SetActiveCamera(OrthographicCamera* pCam);
        void SetRunMode(RunMode mode);
        RunMode GetRunMode();

        entt::registry* GetEntityRegistry();
        LUUID CreateEntity();
        void RemoveEntity(LUUID id);
        Entity* GetEntity(LUUID id);
        unsigned int GetNumEntities() const;
        std::vector<Entity*>::iterator EntitiesBegin();
        bool EntitiesIsEnd(std::vector<Entity*>::iterator& iter);

        WorldState GetState();
        void ResetState(WorldState& state);
        std::string GetName() const;
        void SetName(std::string name);
        glm::vec2 GetGravity() const;
        void SetGravity(glm::vec2 gravity);
        float GetPhysicsScaleFactor() const;
        void SetPhysicsScaleFactor(float factor);

        // Serializing
        [[nodiscard]] virtual OpRes Serialize(nlohmann::ordered_json& node);
        [[nodiscard]] virtual OpRes Deserialize(nlohmann::ordered_json& node);
        [[nodiscord]] virtual bool IsValidNode(nlohmann::ordered_json& node);
        [[nodiscard]] virtual nlohmann::ordered_json AsJSON();


    private:
        LUUID mUUID;
        std::string mName;
        RunMode mMode;
        bool mDrawDebugObjects;
       

        // State critical Data
        entt::registry mECSRegistry;
        std::vector<Entity*> mEntities;
        std::map<LUUID, Entity*> mEntitiesByUUID;
        FrameBuffer* mFrameBuffer;
        OrthographicCamera* mpActiveCamera;
        b2World* mpPhysicsWorld;
        struct 
        {
            struct
            {
                float PhysicsScaleFactor;
                glm::vec3 Gravity;
            } Physics;
        } WorldSettings;
        //

        // TODO: Move these into a TileMap class?
        // This would allow worlds to support non-tile based levels/worlds
        Sizei mRegionSize;      // in tiles
        Sizei mWorldSize;       // num regions ex. 10x10
        Vec2i mActiveRegion;


        // TEST STUFF
        //bool mGetAssetsFromEditor;  // This is for testing until we get a proper asset manager

    
        
    private: // HELPERS
        void InitScriptState();
        void RenderEditor(lunarium::Renderer2D* pGraphics) const;
        glm::mat4 GetParentTransform(LUUID parent);
        void RenderBlockouts(lunarium::Renderer2D* pGraphics);
        void RenderSprites(lunarium::Renderer2D* pGraphics);
        void RenderDebugObjects(lunarium::Renderer2D* pGraphics);
    };
}

#endif  // LUNARIUM_WORLD_H_