#pragma once

#include <Redacted3D/types/vertex.h>
#include <Redacted3D/types/texture.h>
#include <Redacted3D/types/shader.h>
#include <Redacted3D/types/entity/entity.h>
#include <J3ML/LinearAlgebra/Vector3.h>

using J3ML::LinearAlgebra::Vector3;
using J3ML::LinearAlgebra::Vector2;

///Forward declaration of Camera.
class Camera;

// TODO: Move data to Entity / or rename to DataModelEntry
struct ByteArray {
    std::vector<u8> bytes;
};

class Serializable {
    virtual ByteArray Serialize() const;
    virtual Serializable Deserialize(const ByteArray& data);
};


// A wrapper around a Tree Hierarchy Data Model.
class DataModel : public Entity {
public:
    Entity * GetParent() const override {return nullptr;}
    std::vector<Entity> GetFlatEntityList();

    template <class T>
    void Serialize(T& archive) {
        Entity::SerializeMemberData(archive);
        archive & GetFlatEntityList();
    }

    void SetParent(Entity *parent) override {
        throw std::runtime_error("Cannot set parent of Hierarchy Root!");
    }
    DataModel() : Entity() {}
    [[nodiscard]] int getEntityCount() const;
    [[nodiscard]] int getMobyCount() const;
};

class World : public DataModel {
private:
    Camera* activeCamera = nullptr;
    Vector3 globalLightColor = {0, 0, 0};
    Vector4 globalFogColor = {0, 0, 0, 0};
    Vector2 globalFogRange = {0, 0};
    GLenum globalFogMode = 0; ///There's Linear, GL_EXP, GL_EXP2.
    GLfloat globalFogDensity = 0.0f;

public:
    World() : DataModel() {}
    void setAmbientLightColor(GLfloat red, GLfloat green, GLfloat blue);
    void setGlobalFogColor(GLfloat red, GLfloat green, GLfloat blue, GLfloat alpha);
    void setGlobalFogRange(GLfloat start, GLfloat end);
    void setGlobalFogDensity(GLfloat density);
    void setGlobalFogMode(GLenum mode);
    void setActiveCamera(Camera* camera);

    [[nodiscard]] GLenum getGlobalFogMode() const;
    Vector4 getGlobalFogColor();
    Vector2 getGlobalFogRange();
    Vector3 getAmbientLightColor();
    [[nodiscard]] GLfloat getGlobalFogDensity() const;
    Camera* getActiveCamera();

    Vector3 upVector = {0, 1, 0};
    std::string name;
    std::vector<VertexArray*> geometryList;
    std::vector<Texture*> textureList;
    std::vector<Shader> shaderList;

};
