/**
 * @file Font.cpp
 * @author Petr Egert (egertpet@fel.cvut.cz)
 * @brief Contains Font class implementation
 */

#include <stdio.h>

#ifdef __WINDOWS__
#include <windows.h>
#endif

#include <gl/gl.h>

#include "Font.h"
#include "Globals.h"

using namespace std;
using namespace RD;

/**
 * @brief Initialize the font from the given filename
 * @param fileName Path to the BFF file
 * @return RES_OK if everything goes well
 */
Result Font::Init(const char *fileName)
{
    return Load(fileName);
}

/**
 * @brief Performs class deinitialization
 *
 * Currently does nothing
 * @return RES_OK on success
 */
Result Font::Release()
{
    return RES_OK;
}

/**
 * @brief Draws a given string on the screen, using the current font color
 *
 * Current OpenGL context transformation is used, so it must be set properly before calling this function.
 * Each character of the text has height of 1.0 and width between 0.0 and 1.0
 * Newlines are supported
 * @param text Text to be drawn
 */
void Font::DrawString(const std::string &text)
{
    const char *t = text.c_str();

    glEnable(GL_TEXTURE_2D);
//    glDisable(GL_BLEND);
    glBindTexture(GL_TEXTURE_2D, texID);
    glColor3fv(color);

    glBegin(GL_QUADS);

    float x = 0.0;
    float y = 0.0;


    for(unsigned int i = 0; t[i]; i++)
    {
        if(t[i] == '\n')
        {
            x = 0.0;
            y += 1.0;
            continue;
        }

        float u = ((t[i] - header.startChar) % cellsPerRow) * cellWidth;
        float v = ((t[i] - header.startChar) / cellsPerRow) * cellHeight;

        glTexCoord2f(u, v );
        glVertex2f(x, y);

        glTexCoord2f(u + cellWidth, v);
        glVertex2f(x + 1.0, y);

        glTexCoord2f(u + cellWidth, v + cellHeight);
        glVertex2f(x + 1.0, y + 1.0);

        glTexCoord2f(u, v + cellHeight);
        glVertex2f(x, y + 1.0);

        x += widths[(unsigned int) t[i]];

    }
    glEnd();
}

/**
 * @brief Loads a font from the given BFF file
 * @param fileName Path to the BFF file
 * @return RES_OK on success, RES_NOT_FOUND if file is not found/readable, RES_ERROR in other cases
 */
Result Font::Load(const char *fileName)
{
    FILE *inFile = fopen(fileName, "rb");
    if(!inFile)
    {
        return ErrorHandler::Error(string("Font::Load(") + string(fileName) + string(")- file not found"), RES_NOT_FOUND);
    }

    fread(&header.id, 2, 1, inFile);
    if(header.id != 0xF2BF)
    {
        return ErrorHandler::Error(string("Font::Load(") + string(fileName) + string(")- file is not a valid BFF file"), RES_ERROR);
    }

    fread(&header.imgWidth, 4, 1, inFile);
    fread(&header.imgHeight, 4, 1, inFile);
    fread(&header.cellWidth, 4, 1, inFile);
    fread(&header.cellHeight, 4, 1, inFile);

    fread(&header.bpp, 1, 1, inFile);
    fread(&header.startChar, 1, 1, inFile);

    switch(header.bpp)
    {
        case 8:
        case 24:
        case 32:
            break;

        default:
            fclose(inFile);
            return ErrorHandler::Error(string("Font::Load(") + string(fileName) + string(")- invalid BPP"), RES_ERROR);
    }

    unsigned char charWidths[256];
    fread(charWidths, 256, 1, inFile);

    for(unsigned int i = 0; i < 256; i++)
    {
        widths[i] = charWidths[i] / (float) header.cellWidth;
    }

    unsigned char *data = new unsigned char[header.imgWidth * header.imgHeight * (header.bpp / 8)];
    fread(data, header.imgWidth * header.imgHeight * (header.bpp / 8), 1, inFile);

    glGenTextures(1, &texID);
    glBindTexture(GL_TEXTURE_2D, texID);

    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);

//    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
//    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);

    switch(header.bpp)
    {
        case 8:
            glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, header.imgWidth, header.imgHeight, 0, GL_ALPHA, GL_UNSIGNED_BYTE, data);
            break;

        case 24:
            glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, header.imgWidth, header.imgHeight, 0, GL_RGB, GL_UNSIGNED_BYTE, data);
            break;

        case 32:
            glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, header.imgWidth, header.imgHeight, 0, GL_RGBA, GL_UNSIGNED_BYTE, data);
            break;
    }

    delete [] data;

    cellsPerRow = header.imgWidth / header.cellWidth;
    cellWidth = (float) header.cellWidth / (float) header.imgWidth;
    cellHeight = (float) header.cellHeight / (float) header.imgHeight;

    return RES_OK;
}
