#include "DirectRenderer.h"
#include "Wrapper.h"
#include <cmath>

DirectRenderer::DirectRenderer()
{
    glGenVertexArrays(1, &vba);
    glGenBuffers(1, &vbo);

    glBindVertexArray(vba);
    glBindBuffer(GL_ARRAY_BUFFER, vbo);

    glEnableVertexAttribArray(0);
    glVertexAttribPointer(0, 3, GL_FLOAT, false, 24, (GLvoid*) 0);

    glEnableVertexAttribArray(1);  
    glVertexAttribPointer(1, 4, GL_UNSIGNED_BYTE, true, 24, (GLvoid*) 12);
    
    glEnableVertexAttribArray(2);  
    glVertexAttribPointer(2, 2, GL_FLOAT, false, 24, (GLvoid*) 16);
}

DirectRenderer::DirectRenderer(const DirectRenderer& orig)
{
}

DirectRenderer::~DirectRenderer()
{
    glDeleteVertexArrays(1, &vba);
    glDeleteBuffers(1, &vbo);
}

void DirectRenderer::drawRectangle(float minX, float minY, float maxX, float maxY, float tMinX, float tMinY, float tMaxX, float tMaxY, IntColor c)
{
    glBindBuffer(GL_ARRAY_BUFFER, vbo);
        
    if(offset + OBJECT_LENGTH >= BUFFER_LENGTH)
    {
        offset = 0;
        glBufferData(GL_ARRAY_BUFFER, BUFFER_LENGTH, NULL, GL_STREAM_DRAW);
    }
    float* buffer = (float*) glMapBufferRange(GL_ARRAY_BUFFER, offset, OBJECT_LENGTH, GL_MAP_WRITE_BIT | GL_MAP_UNSYNCHRONIZED_BIT);
    if(buffer == NULL)
    {
        return;
    }

    buffer[0] = minX;
    buffer[1] = maxY;
    buffer[2] = 0.0f;
    buffer[3] = *((float*) (&c));
    buffer[4] = tMinX;
    buffer[5] = tMaxY;
    
    buffer[6] = maxX;
    buffer[7] = maxY;
    buffer[8] = 0.0f;
    buffer[9] = *((float*) (&c));
    buffer[10] = tMaxX;
    buffer[11] = tMaxY;
    
    buffer[12] = minX;
    buffer[13] = minY;
    buffer[14] = 0.0f;
    buffer[15] = *((float*) (&c));
    buffer[16] = tMinX;
    buffer[17] = tMinY;
    
    buffer[18] = maxX;
    buffer[19] = minY;
    buffer[20] = 0.0f;
    buffer[21] = *((float*) (&c));
    buffer[22] = tMaxX;
    buffer[23] = tMinY;

    glUnmapBuffer(GL_ARRAY_BUFFER);

    glBindVertexArray(vba);
    glDrawArrays(GL_TRIANGLE_STRIP, offset / (OBJECT_LENGTH / 4), 4);
    offset += OBJECT_LENGTH;
}

void DirectRenderer::drawRectangle(float minX, float minY, float maxX, float maxY, IntColor c)
{
    drawRectangle(minX, minY, maxX, maxY, 0.0f, 0.0f, 0.0f, 0.0f, c);
}

void DirectRenderer::drawRectangle(float minX, float minY, float maxX, float maxY, float tMinX, float tMinY, float tMaxX, float tMaxY)
{
    drawRectangle(minX, minY, maxX, maxY, tMinX, tMinY, tMaxX, tMaxY, 0);
}

float DirectRenderer::drawString(float x, float y, bool shadow, string& s)
{
    int size = s.length() * OBJECT_LENGTH * (shadow + 1);
    
    glBindBuffer(GL_ARRAY_BUFFER, vbo);
        
    if(offset + size >= BUFFER_LENGTH)
    {
        offset = 0;
        glBufferData(GL_ARRAY_BUFFER, BUFFER_LENGTH, NULL, GL_STREAM_DRAW);
    }
    
    float* buffer = (float*) glMapBufferRange(GL_ARRAY_BUFFER, offset, size, GL_MAP_WRITE_BIT | GL_MAP_UNSYNCHRONIZED_BIT);
    if(buffer == NULL)
    {
        return y;
    }

    int index = 0;
    if(shadow)
    {
        addString(x + SHADOW_STEP, y + SHADOW_STEP, index, buffer, Color::DARK_COLORS, s);
    }
    y = addString(x, y, index, buffer, Color::COLORS, s);

    glUnmapBuffer(GL_ARRAY_BUFFER);

    glBindVertexArray(vba);
    FONTS[min(Engine::getScale() - 1, FONTS_LENGTH - 1)].bind();
    glDrawArrays(GL_TRIANGLE_STRIP, offset / (OBJECT_LENGTH / 4), (index + 1) / 6);
    offset += size;
    
    return y;
}

float DirectRenderer::addString(float x, float y, int& index, float* data, const IntColor* color, string& s)
{
    int l = min((int) s.length(), static_cast<int>(MAX_STRING_LENGTH));
    
    //x = roundf(x / GameEngine::scale) * GameEngine::scale;
    //y = roundf(y / GameEngine::scale) * GameEngine::scale;
        
    float oldX = x;
    float currentColor = *((float*) (&(color[Color::COLOR_AMOUNT - 1])));

    for(int pos = 0; pos < l; pos++)
    {
        char c = s[pos];
        if(c == COLOR_CHAR)
        {
            pos++;
            if(pos < l)
            {
                int index = s[pos] - 'a';
                if(index >= 0 && index < Color::COLOR_AMOUNT)
                {
                    currentColor = *((float*) (&(color[index])));
                }
            }
            continue;
        }
        else if(c == '\n')
        {
            y += FONT_SIZE + LINE_STEP;
            x = oldX;
            continue;
        }

        float tMinX = (c & 0xF) * 0.0625f;
        float tMinY = (c >> 4) * 0.0625f;
        float tMaxX = tMinX + 0.0625f;
        float tMaxY = tMinY + 0.0625f;
        float maxX = x + FONT_SIZE;
        float maxY = y + FONT_SIZE;
        
        data[index++] = x;
        data[index++] = maxY;
        data[index++] = 0.0f;
        data[index++] = currentColor;
        data[index++] = tMinX;
        data[index++] = tMaxY;
        
        data[index++] = maxX;
        data[index++] = maxY;
        data[index++] = 0.0f;
        data[index++] = currentColor;
        data[index++] = tMaxX;
        data[index++] = tMaxY;
        
        data[index++] = x;
        data[index++] = y;
        data[index++] = 0.0f;
        data[index++] = currentColor;
        data[index++] = tMinX;
        data[index++] = tMinY;
        
        data[index++] = maxX;
        data[index++] = y;
        data[index++] = 0.0f;
        data[index++] = currentColor;
        data[index++] = tMaxX;
        data[index++] = tMinY;
        
        x += FONT_SIZE;
    }
    return y + FONT_SIZE + LINE_STEP;
}

/*  
    public Rectangle getSize(String s)
    {
        int length = 0;
        int counter = 0;
        for(int i = 0; i < s.length(); i++)
        {
            switch(s.charAt(i))
            {
                case '\n':
                    counter++;
                    break;
                case COLOR_CHAR:
                    i++;
                    break;
                default:
                    length++;
            }
        }
        return new Rectangle(FONT_SIZE * length, (FONT_SIZE + LINE_STEP) * counter);
    }
    
    public Rectangle getSize(int w, int h)
    {
        return new Rectangle(FONT_SIZE * w, (FONT_SIZE + LINE_STEP) * h);
    }
    
    public float getHeight()
    {
        return FONT_SIZE + LINE_STEP;
    }
    
    public float getWidth()
    {
        return FONT_SIZE;
    }
 
 
 */