/******************************************************************************
*	File		-	renderer2D.h
*	Author		-	Joey Pollack
*	Date		-	2022/07/13 (y/m/d)
*	Mod Date	-	2022/07/13 (y/m/d)
*	Description	-	The main 2D render class
******************************************************************************/

#ifndef LUNARIUM_RENDERER_2D_H_
#define LUNARIUM_RENDERER_2D_H_

#include <core/common_defs.h>
#include "render_common.h"
#include <utils/op_res.h>

#include "text_renderer.h"

#include <glm/glm.hpp>
#include <vector>

namespace lunarium
{
    class OrthographicCamera;
    class Texture;


    class Renderer2D
    {
    public:
        struct FrameStats
        {
            int NumVerts = 0;
            int NumTris = 0;
            int DrawCalls = 0;
        };

        Texture* GetDebugTexture();
        void SetClearColor(Color color);
        Color GetClearColor();

        void ResetFrameStats();
        FrameStats GetFrameStats() const;
        void BeginDraw(OrthographicCamera* pCamera);
        void EndDraw();

        // Draw methods

        /// sub_texture_area is the area of the texture to draw
        /// position is the top-left corner of the quad
        void DrawSprite(Texture* texture, glm::vec2 position, Rectangle sub_texture_area, Color color = Color::White(), float angle = 0.0f, glm::mat4 parent_transform = glm::mat4(1.0f));
        //void DrawSprite(Texture* texture, Rectangle destination, Rectangle sub_texture_area, Color color = Color::White(), float angle = 0.0f);
        
        /// sub_texture_area is the area of the texture to draw
        void DrawQuad(Rectangle quad, Color color, Texture* texture = nullptr, float angle = 0.0f, Rectangle sub_texture_area = Rectangle(-1, -1, -1, -1), glm::mat4 parent_transform = glm::mat4(1.0f));
        void DrawLine(glm::vec2 point_a, glm::vec2 point_b, Color color, float thickness = 1.0f, float angle = 0.0f);
        void DrawBox(Rectangle box, Color color, float thickness = 1.5f, float angle = 0.0f);

        /// Resolution is the number of sides the ellipse will have.
        /// So small numbers can be used to make ngon shapes
        void DrawEllipse(glm::vec2 center_point, glm::vec2 radii, Color color, int resolution = 50, float thickness = 1.5f, float angle = 0.0f);
        
        /// Resolution is the number of sides the ellipse will have.
        /// So small numbers can be used to make ngon shapes
        void DrawEllipseFilled(glm::vec2 center_point, glm::vec2 radii, Color color, int resolution = 50, float angle = 0.0f);
        void DrawString(const char* string, Rectangle bounding_box, Color color = {1.0f, 1.0f, 1.0f, 1.0f}, float scale = 1.0f, int font = -1);

        // DEBUG:
        Texture* GetTextDebugTexture();

    private:
        friend class RenderContext;
        OpRes Initialize();
        void Shutdown();

        enum FlushMode
        {
            Quads = 0x01,
            Lines = 0x02,
            Ellipses = 0x04,

            All = 0xFF
        };

        void Flush(int flush_mode = FlushMode::All);

    private:
        Renderer2D();
        Renderer2D(const Renderer2D&) = delete;
        Renderer2D(const Renderer2D&&) = delete;
        Renderer2D operator=(const Renderer2D&) = delete;

    private: // Helpers
        OpRes InitQuadData();
        OpRes InitLineData();
        OpRes InitEllipseData();
        void ResetDrawingData();

    private: // data

        TextRenderer mTextAPI;
        int mDefaultFont;
  
        Color mClearColor;
        OrthographicCamera* mpCamera;
        FrameStats mFrameStats;

        /////////////////////////////////////////////////////////////////////
        //				QUAD DATA       QUAD DATA
        /////////////////////////////////////////////////////////////////////
        struct QuadData
        {
            bool MarkedForReset = false;
            int NumQuads = 0;
            const int MaxQuads = 10000;
            const int MaxVertices = MaxQuads * 4;
            const int MaxIndices = MaxQuads * 6;
            const int TextureSlots = 32;
            const int ParentSlots = 32;
            std::vector<Texture*> LoadedTextures;
            Texture* WhiteTexture;

            const glm::vec4 vert_pos[4] = {
                    { -0.5f, -0.5f, 0.0f, 1.0f }, 
                    {  0.5f, -0.5f, 0.0f, 1.0f }, 
                    {  0.5f,  0.5f, 0.0f, 1.0f },
                    { -0.5f,  0.5f, 0.0f, 1.0f } 
            };

            const glm::vec2 vert_tex[4] = {
                { 0.0f, 0.0f },
                { 1.0f, 0.0f },
                { 1.0f, 1.0f },
                { 0.0f, 1.0f },
            };

            struct Vertex
            {
                glm::vec3 pos;
                glm::vec2 tex_coord;
                glm::vec4 color;
                float tex_slot;
                glm::vec3 translation;
                float angle;
                glm::vec3 scale;
                float tex_is_grey_scale;    // 0 or 1, this is mostly used for text rendering
                float parent_idx;           // -1 for no parent
            };

            const u32 indices[6] = { 0, 1, 2, 2, 3, 0 };

            // Vertex* RawVertexBuffer;
            // int RawBufferIndex;

            BufferLayout BufferLayout;
            VertexBuffer* VertexBuffer;
            Shader* QuadShader;
            Shader* WireFrameShader;
            std::vector<glm::mat4> Parents;
        } mQuadData;

        /////////////////////////////////////////////////////////////////////
        //				ELLIPSE DATA        ELLIPSE DATA
        /////////////////////////////////////////////////////////////////////
        struct EllipseData
        {
            bool MarkedForReset = false;
            int NumEllipses = 0;
            int NumVerts = 0;
            int NumIndices = 0;
            const int MaxVertices = 60000;
            const int MaxIndices = 60000;

            struct Vertex
            {
                glm::vec2 pos;
                glm::vec4 color;
            };

            Vertex verts[4096];
            u32 indices[4096];

            // Vertex* RawVertexBuffer;
            // int RawBufferIndex;
            // u32* RawIndexBuffer;
            // int RawIndexBufferIndex;

            BufferLayout BufferLayout;
            VertexBuffer* VertexBuffer;
            Shader* Shader;

        } mEllipseData;


        /////////////////////////////////////////////////////////////////////
        //				LINE DATA       LINE DATA
        /////////////////////////////////////////////////////////////////////
        struct LineData
        {
            bool MarkedForReset = false;
            int NumLines = 0;
            const int MaxLines = 30000;
            const int MaxVertices = MaxLines * 2;
            float Thickness = 1.0f;

            struct Vertex
            {
                glm::vec2 pos;
                glm::vec4 color;
            };

            // Vertex* RawVertexBuffer;
            // int RawBufferIndex;

            BufferLayout BufferLayout;
            VertexBuffer* VertexBuffer;
            Shader* Shader;
        } mLineData;


        /// DEBUG STUFF
        u32 mTVAO;
        u32 mTVBO;

    };
}


#endif  // LUNARIUM_RENDERER_2D_H_