#include <tinyxml2.h>
#include <window.h>
#include "font.h"
#include "manager.h"

using namespace tinyxml2;

namespace render::text{


Font::Font():glyphTable(_symbols)
{
    memset(_symbols, 0, sizeof(uint16_t)*0xFFFF);
}

Font::Font(const std::string &path):Font()
{
    load(path);
}

bool Font::load(const std::string &path)
{
    XMLDocument doc;

    std::string tempPath = path+".png";

    if(!_tex.load(tempPath, 1))
        return 0;

    tempPath = path+".xml";

    if(doc.LoadFile(tempPath.c_str()))
    {
        free();
        return 0;
    }

    uint32_t size = 1;

    XMLElement *fontElement = doc.RootElement()->FirstChildElement("font");

    if(sscanf(fontElement->Attribute("size"), "%dpt", &size))
        defaultScale = 12.0/size;
    else if(sscanf(fontElement->Attribute("size"), "%dpx", &size))
        defaultScale = 16.0/size;
    else
        defaultScale = 1;

    vec2u32 tsize = _tex.size();

    for(XMLElement *element = doc.RootElement()->FirstChildElement()->FirstChildElement("char"); element;
        element = element->NextSiblingElement("char"))
        {
            charInfo tempInfo;
            vec4i32 tempCoord;
            element->QueryAttribute("id", &tempInfo.code);
            element->QueryAttribute("x", &tempCoord.x);
            element->QueryAttribute("y", &tempCoord.y);
            element->QueryAttribute("width", &tempCoord.z);
            element->QueryAttribute("height", &tempCoord.w);

            tempInfo.imgSize.x = tempCoord.z;
            tempInfo.imgSize.y = tempCoord.w;

            tempInfo.texCoord.x = float(tempCoord.x)/tsize.x;
            tempInfo.texCoord.y = float(tempCoord.y)/tsize.y;
            tempInfo.texCoord.z = float(tempCoord.z)/tsize.x;
            tempInfo.texCoord.w = float(tempCoord.w)/tsize.y;

            element->QueryAttribute("Xoffset", &tempInfo.offset.x);
            element->QueryAttribute("Yoffset", &tempInfo.offset.y);

            element->QueryAttribute("OrigWidth", &tempInfo.size.x);
            element->QueryAttribute("OrigHeight", &tempInfo.size.y);

            if(!tempInfo.size.x)
                tempInfo.size.x = tempCoord.x;

            if(!tempInfo.size.y)
                tempInfo.size.y = tempCoord.y;

            _charList.push_back(tempInfo);
        }
    uint32_t oldCode = 0, id;
    return 1;
}

struct quadCharInfo
{
    uint32_t reserved;
    vec2u16 pos;
    uint16_t id;
    vec2u8 size;
    vec2i16 origin;
    vec2i16 inc;
};

struct tagKERNINGPAIR
{
    uint16_t  wFirst;
    uint16_t  wSecond;
    int  iKernAmount;
};

bool Font::loadQuad(const std::string &path)
{
    std::string tempPath = path+".png";
    if(!_tex.load(tempPath, 1, 1))
        return 0;
    vec2u32 tsize = _tex.size();

    tempPath = path+".qef";
    memset(_symbols, 0, sizeof(uint16_t)*0xFFFF);
    FILE *f = fopen(tempPath.c_str(), "rb");
    if(!f)
    {
        free();
        return 0;
    }
    vec2u8 header;
    uint32_t size;
    fseek(f, 12, SEEK_SET); //нахуй хедер :D
    fread(&header, 1, 2, f); //или не нахуй

    fseek(f, 8,  SEEK_CUR);
    fread(&size, 4, 1, f);
    uint32_t charCount = size/sizeof(quadCharInfo);
    quadCharInfo *quadChars = new quadCharInfo[charCount];
    fread(quadChars, 1, size, f);

    fseek(f, 8,  SEEK_CUR);
    fread(&size, 4, 1, f);
    uint32_t kernCount = size/8;
    tagKERNINGPAIR *kerning = new tagKERNINGPAIR[kernCount];

    fread(kerning, 1, size, f);
    _maxHeight = 0;
    for(int i = 0; i < charCount; i++)
    {
        charInfo info;

        if(_maxHeight < quadChars[i].size.y)
            _maxHeight = quadChars[i].size.y;

        info.code = quadChars[i].id;
        info.imgSize = quadChars[i].size;
        info.size = vec2f(quadChars[i].inc)/header.y;
        info.size.y *= 2;
        info.offset = vec2f(quadChars[i].origin)/header.y;
        info.offset.y *= -1;

        info.texCoord.x = (float)quadChars[i].pos.x/tsize.x;
        info.texCoord.z = (float)quadChars[i].size.x/tsize.x;
        info.texCoord.y = (float)quadChars[i].pos.y/tsize.y;
        info.texCoord.w = (float)quadChars[i].size.y/tsize.y;
        _charList.push_back(info);
    }

    for(int i = 0; i < _charList.size(); i++)
    {
        _charList[i].offset.y += float(_maxHeight)/2;
        _symbols[_charList[i].code] = &_charList[i];
    }

    delete [] quadChars;

    for(int i = 0; i < kernCount; i++)
    {
        _kerningPairs[std::make_pair(kerning[i].wFirst, kerning[i].wSecond)] = ((float)kerning[i].iKernAmount)/header.y*2;
    }

    delete [] kerning;

    defaultScale = _maxHeight/header.x;

    fclose(f);
    return 1;
}

void Font::draw(const drawGlyphInfo *data, uint32_t count, float scale) const
{
    if(!count)
        return;
    constexpr vec2f offset[6] = {{0, 1}, {1, 1}, {1, 0},
                                 {1, 0}, {0, 0}, {0, 1}};

    render::text::textVert buffer[0xfff];
    uint32_t counter = 0;

    scale *= defaultScale;

    float rScale = scale;
    if(const render::camera2D *cam = dynamic_cast<const render::camera2D*>(render::getActiveCamera()))
    {
        vec2f s = window::getSize();
        float wScale = 1;
        if(cam->screenSize.y != -1)
            rScale /= fabs((cam->screenSize.y-cam->offset.y)/s.y);
    }


    for(int c = 0; c < count; c++)
    {
        charInfo *glyph = _symbols[data[c].code];
        if(!glyph)
            continue;

        for(int i = 0; i < 6; i++)
        {
            buffer[counter].vert = data[c].pos+(offset[i]*glyph->imgSize+glyph->offset)*scale;
            buffer[counter].uv.x = (offset[i].x*glyph->texCoord.z)+glyph->texCoord.x;
            buffer[counter].uv.y = (offset[i].y*glyph->texCoord.w)+glyph->texCoord.y;

            buffer[counter].color = data[c].color;
            counter++;
        }
    }
    render::text::text(buffer, rScale, counter, &_tex);
}

float Font::maxHeight()
{
    return _maxHeight;
}

}
