#pragma once
#include "../Renderer.hpp"
#include <GL/gl3w.h>
#include <memory>
#include "pass/WorldProgram.hpp"
#include "pass/EntityProgram.hpp"
#include "pass/SkyProgram.hpp"
#include "pass/ColorProgram.hpp"
#include "api/Models.hpp"
#include "api/Images.hpp"

struct windowOptions;
/// OpenGL graphics pipeline
namespace render::gl {

/// OpenGL rendering
class Renderer final: public render::Renderer {
public:
    virtual ~Renderer();

    static bool Load(Window& window, const renderOptions& options, const windowOptions& windOpt);

    glm::vec3 FogColor;
    GLfloat FogDepth;

    glm::mat4 getProjectionMatrix() const {
        return ProjectionMatrix;
    }
    glm::mat4 getViewMatrix() const {
        return ViewMatrix;
    }

    GLuint getTextureAtlas() const {
        return TextureAtlas->getId();
    }
    GLuint getNormalAtlas() const {
        return NormalAtlas->getId();
    }
    GLuint getHOSAtlas() const {
        return HOSAtlas->getId();
    }
    GLuint getSkyTexture() const {
        return Skybox->getId();
    }

    void beginFrame() override;
    std::function<size_t(render::LodModel *const, glm::mat4, glm::vec4, float)> beginWorldPass(bool solid) override;
    std::function<size_t(render::Model *const, const std::vector<glm::mat4>&)> beginEntityPass() override;
    std::function<size_t(glm::mat4, world::action::Shape, glm::vec4)> beginIndicatorPass() override;
    void postProcess() override;
    void endFrame() override;
    void swapBuffer(Window&) override;

    void setClearColor(glm::vec4) override;

    /// Apply camera matrices
    void lookFrom(const Camera&) override;
    void reloadShaders(const pass::VoxelProgram::options &) override;
    void reloadTextures(const std::string &, float mipMapLOD, int anisotropy) override;
    void setFillMode(bool wireframe) override;
    void setVSync(bool vSync) override;

    void loadUI(Window &) override;

    static _FORCE_INLINE_ Renderer *Get() { return static_cast<Renderer*>(render::Renderer::Get()); }

private:
    Renderer(const renderOptions &options);

    GLuint VertexArrayID;

    std::unique_ptr<pass::WorldProgram> WorldPass;
    std::unique_ptr<pass::EntityProgram> EntityPass;
    std::unique_ptr<pass::SkyProgram> SkyPass;
    std::unique_ptr<pass::ColorProgram> IndicatorPass;
    Shape IndicatorCubeBuffer;
    Shape IndicatorSphereBuffer;

    glm::mat4 ProjectionMatrix;
    glm::mat4 ViewMatrix;

    std::unique_ptr<TextureArray> TextureAtlas;
    std::unique_ptr<TextureArray> NormalAtlas;
    std::unique_ptr<TextureArray> HOSAtlas;
    std::unique_ptr<TextureCube> Skybox;

    /// Draw skybox
    bool SkyEnable;

    void loadTextures(const std::string &, float mipMapLOD, int anisotropy);
    void unloadTextures();
};
}