#pragma once
#ifndef RTRTEXT_H
#define RTRTEXT_H
#define STB_IMAGE_IMPLEMENTATION

//////////////
// Includes //
/////////////
// C++ general includes
#include <string>
#include <iostream>
#include <utility>
#include <iostream>

// OpenGL Includes
#include "rtrSetupGLFWandGLAD.h" //rtrGLFWGLAD::
#include "rtrShader.h" //rtrShader::
#include "rtrVAO.h"
#include <glm/glm.hpp>
#include <glm/gtc/matrix_transform.hpp>
#include <glm/gtc/type_ptr.hpp>
#include <map>
#include <glad/glad.h>
#include <GLFW/glfw3.h>
#include "stb_image.h"

//#include "stb_image.h"
//#include"rtrTextureLoading.h" //rtrTexture
//#include"rtrCamera.h"
//#include"rtrMaterialsAndLighting.h"


//FreeType Includes
#include <ft2build.h>
#include FT_FREETYPE_H
namespace rtrText
{
    struct freeTypeCharacter
    {
        unsigned int TextureID;  //
        glm::ivec2 Size;         //
        glm::ivec2 Bearing;      //
        unsigned int Advance;    //
    };

    class freeTypeFont
    {
        public:
            unsigned int VAO, VBO;
            std::map<char, freeTypeCharacter> Characters;
            glm::mat4 ProjectionMatrix;
            unsigned int size;
            std::string fontPath;

        public:
            freeTypeFont(std::string fontPathInput , const unsigned int fontHeightInput): fontPath(fontPathInput), size(fontHeightInput)
            {
                // Create Freetype Library Object
                FT_Library freeTypeLib;
                if (FT_Init_FreeType(&freeTypeLib)) // returns non-zero int in case of error
                {
                    std::cout << "ERROR::Could not initialize FreeType Lib " << std::endl;
                }

                // Create a freetype face i.e. load a font
                FT_Face face;
                if (FT_New_Face(freeTypeLib, fontPath.c_str(), 0, &face))
                {
                    std::cout << "ERROR::Failed to load font face" << std::endl;
                }

                // Set fontsize. Arguments: 1.font face, 2.width, 3. height (width = 0 adapts automatically to height)
                FT_Set_Pixel_Sizes(face, 0, fontHeightInput);

                // Set UNPCK_ALIGNMENT to 1 (allow texture to not have length multiple of 4 byte
                // OpenGL usually requires a texture to be a multiple of 4bytes (4channels , RGBA, per pixel, each cahnnel 1 byte)
                // Our glyph texture is grayscale so 1 byte per pixel (GL_RED)
                glPixelStorei(GL_UNPACK_ALIGNMENT, 1);

                // load 128 standard characters with freeType into a map of Character structs for easy access
                for (unsigned char c = 0; c < 128; c++)
                {
                    // FT_LOAD_RENDER: create 8-bit grayscale bitmap image of char c, later access via face->glyph->bitmap.
                    if (FT_Load_Char(face, c, FT_LOAD_RENDER))
                    {
                        std::cout << "ERROR::FREETYTPE: Failed to load Glyph" << std::endl;
                        continue;
                    }

                    //Create texture from glyph bitmap
                    unsigned int texId;
                    glGenTextures(1, &texId);
                    glBindTexture(GL_TEXTURE_2D, texId);
                    glTexImage2D(GL_TEXTURE_2D, 0, GL_RED, face->glyph->bitmap.width, face->glyph->bitmap.rows, 0, GL_RED, GL_UNSIGNED_BYTE, face->glyph->bitmap.buffer);

                    // Configure texture
                    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
                    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
                    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
                    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);

                    // create Character struc
                    freeTypeCharacter character = {
                        texId,                                                            // texture ID
                        glm::ivec2(face->glyph->bitmap.width, face->glyph->bitmap.rows),  // size
                        glm::ivec2(face->glyph->bitmap_left, face->glyph->bitmap_top),    // bearing
                        face->glyph->advance.x                                            //advance
                    };
                    std::pair<char, freeTypeCharacter> mapEntry;
                    mapEntry.first = c;
                    mapEntry.second = character;
                    Characters.insert(mapEntry);
                }

                // Free FreeTypes's resources
                FT_Done_Face(face);

                FT_Done_FreeType(freeTypeLib);

                // Enable blending for text rendering
                glEnable(GL_BLEND);
                glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);

                // Create VAO_Body for drawing
                float Vertices[24] = {};
                const unsigned int dimensinsOfAttributes[2] = { 2,2 };
                rtrVAO::createVAOwithoutEBO(VAO, VBO, Vertices, 6, 2, dimensinsOfAttributes, GL_DYNAMIC_DRAW);
            }

            void render(rtrShader::Shader& shader, 
                        const std::string& text, 
                        const float& xInput, 
                        const float& yInput, 
                        const float& scale, 
                        const glm::vec3& textColor, 
                        const unsigned int& screenWidth, 
                        const unsigned int& screenHeight)
            {
                shader.use();
                shader.setVec3F("textColor", textColor);
                glActiveTexture(GL_TEXTURE0);
                glBindVertexArray(VAO);

                // Enable Blending for text rendering
                glEnable(GL_BLEND);
                glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);

                // Adapt projection matrix to screen size
                ProjectionMatrix = glm::ortho(0.0f, (float)screenWidth, 0.0f, (float)screenHeight);
                shader.setMat4F("projectionMatrix", ProjectionMatrix);
                //iterate through all characters in the string and render them
                int advance = 0;
                std::string::const_iterator c;
                int x = xInput;
                int y = yInput;
                for (c = text.begin(); c != text.end(); c++)
                {
                    freeTypeCharacter ch = Characters[*c];
                    
                    // Define bottom left vertex of the box that holds just the glyph
                    float xPos = (x  + ch.Bearing.x * scale);
                    float yPos = (y - (ch.Size.y - ch.Bearing.y) * scale);
                    float w = (ch.Size.x * scale);
                    float h = (ch.Size.y * scale);

                    //generate vertices for quad that holds character
                   
                    float vertices[] = {
                        //pos                   //tex coords
                        xPos,       yPos + h,   0.0f, 0.0f,
                        xPos,       yPos,       0.0f, 1.0f,
                        xPos + w,   yPos,       1.0f, 1.0f,
                        xPos,       yPos + h,   0.0f, 0.0f,
                        xPos + w,   yPos,       1.0f, 1.0f,
                        xPos + w,   yPos + h,   1.0f, 0.0f
                    };
                    
                   // update cummulative advance bit shift equal multiplication by 64
                   x += (ch.Advance >> 6) * scale;
                    
                    // write quad vertices to vertex buffer
                    glBindBuffer(GL_ARRAY_BUFFER, VBO);
                    glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(vertices), vertices);
                    glBindBuffer(GL_ARRAY_BUFFER, 0);

                    // render quad
                    glActiveTexture(0);
                    glBindTexture(GL_TEXTURE_2D, ch.TextureID);
                    glDrawArrays(GL_TRIANGLES, 0, 6);
                }

                glDisable(GL_BLEND);

            }
    };

}
#endif

