#pragma once
#include <cstdint>
#include <Redacted3D/types/vertex.h>
#include <Redacted3D/types/texture.h>
#include <J3ML/LinearAlgebra/Matrix4x4.h>
#include <J3ML/LinearAlgebra/Vector3.h>
#include <Redacted3D/engine/collision.h>
//#include <archive.h>

using J3ML::LinearAlgebra::Matrix4x4;
using J3ML::LinearAlgebra::Vector3;

typedef Vector3 Direction;

///Base entity type.
class Entity {
private:
    ///Pitch Yaw Roll, The orientation of the entity in the world,
    Vector3 angle = {0,0,0};
    ///Loads the texture for the entity.

    ///The scale it should be rendered at.
    Vector3 scale = {1.0f, 1.0f, 1.0f};
protected:
    ///If the entity has a parent entity, It's here.
    Entity* parent;
    ///Entity list of child entities.
    std::vector<Entity*> children;

///Loads the geometry for it.
void loadGeometry(const std::string& file);
void loadTexture(const std::string& file);
void loadMultiTexture(const std::vector<std::string>& files);
void loadMotionTexture(const std::vector<std::string>& files, u16 frameDelta, bool doAnim = true);

public:
    std::string name; //Entity name. TODO remove this.
    bool alive;

    virtual std::vector<std::string> GetEntityUUIDList() const { return std::vector<std::string> { "" }; }

    template <class T>
    void SerializeMemberData(T& archive)
    {
        archive & uuid.c_str() & name.c_str() & alive;
    }

    template <class T>
    void Serialize(T& archive)
    {
        SerializeMemberData(archive);
        archive & GetEntityUUIDList();
    }
    ///Whether an entity is solid. Entities are solid by default.
    bool collidable = true;

    ///Returns the texture for the entity. Loads it if it's not in the list.
    virtual Texture* getTexture();
    ///Returns the geometry for the entity. Loads it if it's not in the list.
    virtual VertexArray* getGeometry();
    ///Returns the AABB for the entity.
    AABB getAABB();
    ///Returns the minimal enclosing sphere for the entity.
    Sphere getSphere();
    ///Returns the OBB of the entity.
    OBB getOBB();
    ///Returns the entity list of the entities children.
    std::vector<Entity*> GetChildren();
    ///Sets a given entity as this entities parent.
    virtual void SetParent(Entity* parent);
    bool IsDescendantOf(Entity* ancestor);
    bool IsAncestorOf(Entity* descendant);
    std::vector<Entity*> GetDescendants();
    std::vector<Entity*> GetAncestors();
    virtual Entity* GetParent() const { return parent;}
    Entity* FindFirstChild(std::string search_name);
    [[nodiscard]] Entity* GetFamilyTreeRoot() const;
    Entity *Add(Entity *newChild);
    void setScale(const float& multiplier);
    [[nodiscard]] Vector3 getScale() const;

    // TODO: Constrain to DerivedEntityType
    template <typename T>
    T* FindFirstChildOfType() const
    {
        for (auto& child : children) {
            T* p = dynamic_cast<T>(child);

            if (p != nullptr)
                return p;
        }
        return nullptr;
    }

    void DescendantAdded(Entity* ent) {}
    void DescendantRemoved(Entity* ent) {}
    void AncestorAdded(Entity* ent) {}
    void AncestorRemoved(Entity* ent) {}


protected:
    Matrix4x4 coordinates;
    J3ML::LinearAlgebra::Position position; /// X Y Z
public:
    std::string uuid;
    u32 ticksAlive; //At 64tps it'd take 776 days to overflow.
    [[nodiscard]] Position GetPos() const;
    void SetPos(const Position& rhs);
    Matrix4x4 GetMatrix() const;
    void SetMatrix(const Matrix4x4& rhs);
    Direction getAngleDirection();
    Vector3 getAngle();
    void setAngle(const Vector3& a);
    void setAngle(float pitch, float yaw, float roll);
    ///Removes an entity from the list, Checks if the assets are being used by any other entity. Removes them from their lists & deletes. Then deletes the entity.
    void erase();
    bool draw = true;
    bool occluded();
    bool isCollidingWith(Entity* entity);
    virtual void pre_render()  {}
    virtual void post_render() {}
    virtual void jglRenderPass() {}
    ///The default rendering routine. Works for 99% of cases.
    virtual void render();
    virtual void update(float elapsed) {}
    virtual void ticc(int tics)
    {
        ticksAlive++;
    }

    Entity();

    Entity(const Entity& rhs) = default; // Boilerplate: Copy Constructor
    Entity(Entity&& rhs)      = default; // Boilerplate: Move Constructor
    ~Entity() = default;
};


