#pragma once

#include "Vector3.hpp"
#include "Quaternion.hpp"
#include "Matrix4x4.hpp"
#include "uuid.h"
#include "Types.hpp"
#include <list>
#include <string>
#include <unordered_map>

namespace TSE
{
    class BehaviourScript;
    class Layer;

    class Transformable
    {
        public:
            Vector3 position;
            Vector3 scale;
            Quaternion rotation;
            string name;
            bool _enabled = true;
            uuids::uuid id;

        private:
            std::unordered_multimap<string, BehaviourScript*> components;
            std::vector<Transformable*> children;
            Transformable* parent = nullptr;

            inline static std::unordered_map<uuids::uuid, Transformable*> objectEntries = {};
        
        public:
            Transformable();
            Transformable(uuids::uuid id);
            Transformable(const string& name);
            Transformable(const string& name, uuids::uuid id);
            ~Transformable();

            Vector3 forward() const;
            Vector3 right() const;
            Vector3 up() const;

            Vector3 GetPosition() const;
            void SetPosition(const Vector3& pos);
            Quaternion GetRotation() const;
            void SetRotation(const Quaternion& rot);
            Vector3 GetScale() const;
            void SetScale(const Vector3& scale);
            string GetName() const;
            uuids::uuid GetId() const;
            Vector3 GetEuler();
            void SetEuler(const Vector3& euler);
            int GetComponentCount();
            Matrix4x4 GetLocalMatrix() const;
            Matrix4x4 GetGlobalMatrix() const;
            Vector3 GetGlobalPosition() const;

            Vector3 GlobalToLocalPosition(const Vector3& global) const;
            Vector3 LocalToGlobalPosition(const Vector3& local) const;

            void LookAt_2D(const Vector3& globalTarget);

            void SetParent(Transformable* parent);
            Transformable* GetParent() const;
            const std::vector<Transformable*>& GetChildren() const;
            bool IsMyChild(Transformable* other);

            void MoveUp(Layer* l = nullptr);
            void MoveDown(Layer* l = nullptr);

            bool IsEnabled() const;
            void SetEnabled(bool v);

            BehaviourScript* AddBehaviourScript(BehaviourScript* script);
            BehaviourScript* GetBehaviourScript(const string& name) const;
            BehaviourScript* GetBehaviourScriptAt(const int i) const;
            std::vector<BehaviourScript*> GetAllBehaviourScripts(const string& name) const;
            void RemoveBehaviourScript(BehaviourScript* script);
            bool HasBehaviourScript(const string& name) const;

            void Update();

            static void Delete(Transformable* t);
            static void Delete(const uuids::uuid id);
            static void HardDelete(Transformable* t, bool onlyThis = false);
            static void HardDelete(const uuids::uuid id, bool onlyThis = false);
            static void DeleteAll();

            static bool Exists(const uuids::uuid id);

            bool operator==(const Transformable& other) const;
            bool operator!=(const Transformable& other) const;

        private:
            void OnEnable();
            void OnDisable();

        public:

            static int GetTansformableCount();
            static Transformable* GetTansformableAt(int i);
            static Transformable* Find(string name);
            static Transformable* Find(uuids::uuid id);
    };
}