#include "DeerStudio/EditorEngine/API/Entity.h"
#include "DeerStudio/EditorEngine/DockPanelObject.h"
#include "DeerStudio/EditorEngine.h"

#include "DeerRender/Components.h"

#include "Deer/Enviroment.h"
#include "Deer/Scene.h"

#define GET_ENTITY(id) Scene::environment.getEntity(id) 
#define GET_MESH_COMPONENT(id) Scene::environment.getEntity(id).getComponent<MeshComponent>()
#define GET_SHADER_COMPONENT(id) Scene::environment.getEntity(id).getComponent<ShaderComponent>()
#define GET_CAMERA_COMPONENT(id) Scene::environment.getEntity(id).getComponent<CameraComponent>()

#define ASSERT_ENTITY(func, ret) if (!assertEntity(func)) ret;
#define ASSERT_MESH_COMPONENT(func, ret) if (!assertMeshComponent(func)) ret;
#define ASSERT_SHADER_COMPONENT(func, ret) if (!assertShaderComponent(func)) ret;
#define ASSERT_CAMERA_COMPONENT(func, ret) if (!assertCameraComponent(func)) ret;


namespace Deer {
    namespace EditorEngine {
        EntityHandleStruct getRoot() {
            return EntityStruct(0);
        }
        
        void constructEntityStruct(int id, void* memory) {
            new (memory)EntityStruct(id);
        }

        int EntityStruct::getId() {
            return entityId;
        }

        bool EntityHandleStruct::assertEntity(const char* funcName) {
            if (!Scene::environment.entityExists(entityId)) {
                DEER_UI_ENGINE_ERROR("Error, invalid entity calling {0}, entityId : {1}", funcName, entityId);
                if (currentDockPanelExecution)
                    currentDockPanelExecution->invalidate();
                return false;
            }
            return true;
        }

        std::string EntityStruct::getName() {
            ASSERT_ENTITY("getName()", return "NULL");

            return GET_ENTITY(entityId)
                .getComponent<TagComponent>()
                .tag;
        }


        void EntityStruct::setName(std::string& name) {
            ASSERT_ENTITY("setName()", return);

            GET_ENTITY(entityId)
                .getComponent<TagComponent>()
                .tag = name;
        }

        void EntityStruct::destroy() {
            ASSERT_ENTITY("destroy()", return);

            GET_ENTITY(entityId)
                .destroy();
        }

        bool EntityStruct::isRoot() {
            ASSERT_ENTITY("isRoot()", return false);

            return entityId == 0;
        }

        bool EntityStruct::exists() {
            ASSERT_ENTITY("exists()", return false);

            return Scene::environment
                .entityExists(entityId);
        }

        void EntityStruct::setParent(EntityHandleStruct parent_struct) {
            ASSERT_ENTITY("setParent()", return);

            Entity& parent = GET_ENTITY(parent_struct.entityId);
            
            GET_ENTITY(entityId)
                .setParent(parent);
        }

        EntityHandleStruct EntityStruct::getParent() {
            ASSERT_ENTITY("getParent()", return *this);

            Entity& self = GET_ENTITY(entityId);
            if (self.isRoot())
                return *this;
            
            return EntityStruct(self.getParentId());
        }

        bool EntityStruct::isDescendantOf(EntityHandleStruct parent_struct) {
            ASSERT_ENTITY("isDescendantOf()", return false);

            Entity& parent = GET_ENTITY(parent_struct.entityId);
            
            return GET_ENTITY(entityId)
                .isDescendantOf(parent);
        }

        bool EntityStruct::opEquals(const EntityHandleStruct& other) {
            ASSERT_ENTITY("opEquals()", return false);

            return entityId == other.entityId;
        }

        EntityHandleStruct EntityStruct::getSelf() {
            ASSERT_ENTITY("getSelf()", return *this);

            return *this;
        }

        glm::vec3 TransformComponentStruct::getPosition() {
            ASSERT_ENTITY("getPosition()", return glm::vec3());

            return GET_ENTITY(entityId).getComponent<TransformComponent>().position;
        }

        glm::vec3 TransformComponentStruct::getScale() {
            ASSERT_ENTITY("getScale()", return glm::vec3());

            return GET_ENTITY(entityId).getComponent<TransformComponent>().scale;
        }

        glm::vec3 TransformComponentStruct::getRotation() {
            ASSERT_ENTITY("getRotation()", return glm::vec3());

            return GET_ENTITY(entityId).getComponent<TransformComponent>().getEulerAngles();
        }

        void TransformComponentStruct::setPosition(glm::vec3 value) {
            ASSERT_ENTITY("setPosition()", return);

            GET_ENTITY(entityId).getComponent<TransformComponent>().position = value;
        }

        void TransformComponentStruct::setScale(glm::vec3 value) {
            ASSERT_ENTITY("setScale()", return);

            GET_ENTITY(entityId).getComponent<TransformComponent>().scale = value;
        }

        void TransformComponentStruct::setRotation(glm::vec3 value) {
            ASSERT_ENTITY("setRotation()", return);

            GET_ENTITY(entityId).getComponent<TransformComponent>().setEulerAngles(value);
        }

        int EntityChildArrayStruct::getChildCount() {
            ASSERT_ENTITY("getChildCount()", return 0);

            return GET_ENTITY(entityId)
                .getComponent<RelationshipComponent>()
                .childCount;
        }

        bool MeshComponentStruct::assertMeshComponent(const char* funcName) {
            if (!assertEntity(funcName)) return false;
            Entity& ent = Scene::environment.getEntity(entityId);

            if (!ent.hasComponent<MeshComponent>()) {
                DEER_UI_ENGINE_ERROR("Error, calling MeshComponent.{0} entity {1} with id {2} has no MeshComponent",
                    funcName,
                    ent.getComponent<TagComponent>().tag.c_str(),
                    entityId
                );
            
                if (currentDockPanelExecution)
                    currentDockPanelExecution->invalidate();
                return false;
            }

            return true;
        }

        EntityHandleStruct EntityChildArrayStruct::getChild(int i) {
            ASSERT_ENTITY("getChild()", return *this);

            RelationshipComponent& rc = GET_ENTITY(entityId)
                .getComponent<RelationshipComponent>();

            if (i < 0 || i >= rc.childCount) {
                DEER_UI_ENGINE_ERROR("Error while executing EntityChild.getChild(..), id {0} is invalid for child count of {1}", i, rc.childCount);
                if (currentDockPanelExecution)
                    currentDockPanelExecution->invalidate();

                return EntityStruct(0);
            }

            return EntityStruct(rc.getChildrenId(i));
        }

        EntityHandleStruct EntityStruct::createChild(std::string& name) {
            ASSERT_ENTITY("createChild()", return *this);

            Entity& me = GET_ENTITY(entityId);

            Entity& newEnt = Scene::environment
                .createEntity(name);

            newEnt.setParent(me);

            return EntityStruct(newEnt.getId());
        }

        EntityHandleStruct EntityStruct::getMeshComponent() {
            ASSERT_ENTITY("getMeshComponent()", return *this);

            Entity& self = GET_ENTITY(entityId);
            
            if (!self.hasComponent<MeshComponent>()) {
                DEER_CORE_ERROR("Error, entity {0} with id {1} does not have MeshComponent",
                    GET_ENTITY(entityId).getComponent<TagComponent>().tag.c_str(),
                    entityId
                );

                if (currentDockPanelExecution)
                    currentDockPanelExecution->invalidate();
            }

            return *this;
        }

        EntityHandleStruct EntityStruct::createMeshComponent() {
            ASSERT_ENTITY("createMeshComponent()", return *this);

            Entity& self = GET_ENTITY(entityId);
            
            if (!self.hasComponent<MeshComponent>()) {
                self.addComponent<MeshComponent>();
            }

            return *this;
        }

        bool EntityStruct::hasMeshComponent() {
            ASSERT_ENTITY("hasMeshComponent()", return false);

            Entity& self = GET_ENTITY(entityId);

            return self.hasComponent<MeshComponent>();
        }

        void EntityStruct::removeMeshComponent() {
            ASSERT_ENTITY("removeMeshComponent()", return);

            Entity& self = GET_ENTITY(entityId);

            if (self.hasComponent<MeshComponent>()) {
               self.removeComponent<MeshComponent>();
            }
        }

        EntityHandleStruct EntityStruct::getCameraComponent() {
            ASSERT_ENTITY("getCameraComponent()", return *this);

            Entity& self = GET_ENTITY(entityId);
            
            if (!self.hasComponent<CameraComponent>()) {
                DEER_CORE_ERROR("Error, entity {0} with id {1} does not have Camera Component",
                    GET_ENTITY(entityId).getComponent<TagComponent>().tag.c_str(),
                    entityId
                );

                if (currentDockPanelExecution)
                    currentDockPanelExecution->invalidate();
            }

            return *this;
        }

        EntityHandleStruct EntityStruct::createCameraComponent() {
            ASSERT_ENTITY("createCameraComponent()", return *this);

            Entity& self = GET_ENTITY(entityId);
            
            if (!self.hasComponent<CameraComponent>()) {
                self.addComponent<CameraComponent>();
            }

            return *this;
        }

        bool EntityStruct::hasCameraComponent() {
            ASSERT_ENTITY("hasCameraComponent()", return false);

            Entity& self = GET_ENTITY(entityId);

            return self.hasComponent<CameraComponent>();
        }

        void EntityStruct::removeCameraComponent() {
            ASSERT_ENTITY("removeMeshComponent()", return);

            Entity& self = GET_ENTITY(entityId);

            if (self.hasComponent<CameraComponent>()) {
               self.removeComponent<CameraComponent>();
            }
        }

        EntityHandleStruct EntityStruct::getShaderComponent() {
            ASSERT_ENTITY("getShaderComponent()", return *this);

            Entity& self = GET_ENTITY(entityId);
            
            if (!self.hasComponent<ShaderComponent>()) {
                DEER_CORE_ERROR("Error, entity {0} with id {1} does not have Shader Component",
                    GET_ENTITY(entityId).getComponent<TagComponent>().tag.c_str(),
                    entityId
                );

                if (currentDockPanelExecution)
                    currentDockPanelExecution->invalidate();
            }

            return *this;
        }

        EntityHandleStruct EntityStruct::createShaderComponent() {
            ASSERT_ENTITY("createShaderComponent()", return *this);

            Entity& self = GET_ENTITY(entityId);
            
            if (!self.hasComponent<ShaderComponent>()) {
                self.addComponent<ShaderComponent>();
            }

            return *this;
        }

        bool EntityStruct::hasShaderComponent() {
            ASSERT_ENTITY("hasShaderComponent()", return false);

            Entity& self = GET_ENTITY(entityId);
            return self.hasComponent<ShaderComponent>();
        }

        void EntityStruct::removeShaderComponent() {
            ASSERT_ENTITY("removeShaderComponent()", return);

            Entity& self = GET_ENTITY(entityId);

            if (self.hasComponent<ShaderComponent>()) {
               self.removeComponent<ShaderComponent>();
            }
        }

        bool MeshComponentStruct::isActive() {
            ASSERT_MESH_COMPONENT("isActive()", return false);
            return GET_MESH_COMPONENT(entityId).isActive();
        }

        void MeshComponentStruct::setActive(bool value) {
            ASSERT_MESH_COMPONENT("setActive(bool)", return);
            
            GET_MESH_COMPONENT(entityId).setActive(value);
        }

        bool MeshComponentStruct::hasMesh() {
            ASSERT_MESH_COMPONENT("hasMesh()", return false);

            return GET_MESH_COMPONENT(entityId).hasMesh();
        }

        void MeshComponentStruct::clear() {
            ASSERT_MESH_COMPONENT("clear()", return);

            GET_MESH_COMPONENT(entityId).clear();
        }

        std::string MeshComponentStruct::getMesh() {
            ASSERT_MESH_COMPONENT("getMesh()", return "NULL");

            return GET_MESH_COMPONENT(entityId).getMeshName();
        }

        void MeshComponentStruct::setMesh(std::string& name) {
            ASSERT_MESH_COMPONENT("setMesh()", return);

            uint16_t meshId = MeshManager::loadModel(name);
            GET_MESH_COMPONENT(entityId).setMesh(meshId);
        }

        bool ShaderComponentStruct::hasShader() {
            ASSERT_ENTITY("hasShader()", return false);

            return GET_SHADER_COMPONENT(entityId).hasShader();
        }

        bool ShaderComponentStruct::assertShaderComponent(const char* funcName) {
            if (!assertEntity(funcName)) return false;
            Entity& ent = Scene::environment.getEntity(entityId);

            if (!ent.hasComponent<ShaderComponent>()) {
                DEER_UI_ENGINE_ERROR("Error, calling ShaderComponent.{0} entity {1} with id {2} has no ShaderComponent",
                    funcName,
                    ent.getComponent<TagComponent>().tag.c_str(),
                    entityId
                );
            
                if (currentDockPanelExecution)
                    currentDockPanelExecution->invalidate();
                return false;
            }

            return true;
        }

        void ShaderComponentStruct::clear() {
            ASSERT_SHADER_COMPONENT("clear()", return);

            GET_SHADER_COMPONENT(entityId).clear();
        }

        std::string ShaderComponentStruct::getShader() {
            ASSERT_SHADER_COMPONENT("getShader()", return "NULL");

            return GET_SHADER_COMPONENT(entityId).getName();
        }

        void ShaderComponentStruct::setShader(std::string& name) {
            ASSERT_SHADER_COMPONENT("getShader()", return);

            uint16_t shaderId = ShaderManager::loadShader(name);
            GET_SHADER_COMPONENT(entityId).setShader(shaderId);
        }
        

        bool CameraComponentStruct::assertCameraComponent(const char* funcName) {
            if (!assertEntity(funcName)) return false;
            Entity& ent = Scene::environment.getEntity(entityId);

            if (!ent.hasComponent<CameraComponent>()) {
                DEER_UI_ENGINE_ERROR("Error, calling CameraComponent.{0} entity {1} with id {2} has no CameraComponent",
                    funcName,
                    ent.getComponent<TagComponent>().tag.c_str(),
                    entityId
                );
            
                if (currentDockPanelExecution)
                    currentDockPanelExecution->invalidate();
                return false;
            }

            return true;
        }

        float CameraComponentStruct::getFov() {
            ASSERT_CAMERA_COMPONENT("getFov()", return 0);

            return GET_CAMERA_COMPONENT(entityId).fov / 3.141f * 180.0f;
        }

        float CameraComponentStruct::getAspectRation() {
            ASSERT_CAMERA_COMPONENT("getAspectRation()", return 0);

            return GET_CAMERA_COMPONENT(entityId).aspect;
        }

        float CameraComponentStruct::getNearZ() {
            ASSERT_CAMERA_COMPONENT("getNearZ()", return 0);

            return GET_CAMERA_COMPONENT(entityId).nearZ;
        }

        float CameraComponentStruct::getFarZ() {
            ASSERT_CAMERA_COMPONENT("getFarZ()", return 0);

            return GET_CAMERA_COMPONENT(entityId).farZ;
        }

        void CameraComponentStruct::setFov(float v) {
            ASSERT_CAMERA_COMPONENT("getFarZ()", return);

            GET_CAMERA_COMPONENT(entityId).fov = v * 3.141f / 180.0f;
        }
        void CameraComponentStruct::setAspectRation(float v) {
            ASSERT_CAMERA_COMPONENT("setAspectRation()", return);

            GET_CAMERA_COMPONENT(entityId).aspect = v;
        }
        void CameraComponentStruct::setNearZ(float v) {
            ASSERT_CAMERA_COMPONENT("setNearZ()", return);

            GET_CAMERA_COMPONENT(entityId).nearZ = v;
        }
        void CameraComponentStruct::setFarZ(float v) {
            ASSERT_CAMERA_COMPONENT("setFarZ()", return);

            GET_CAMERA_COMPONENT(entityId).farZ = v;
        }


    }
}