/*Copyright (C) 2010-2020 Duncan Deveaux

This file is part of Hikou no mizu.

Hikou no mizu is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

Hikou no mizu is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with Hikou no mizu.  If not, see <http://www.gnu.org/licenses/>.
*/

#include "Text.hpp"

Text::Text() : Drawable(), m_font(NULL), m_width(0), m_height(0)
{
    
}

Text::Text(const std::string &text, Font &font) :
Drawable(), m_font(&font), m_text(text)
{
    loadGlyphs();
}

void Text::draw()
{
    if (m_font != NULL)
    {
        //Update matrix
        Drawable::pushMatrix();
        Drawable::updateMatrix();
        
        glColor4ub(m_color.red, m_color.green, m_color.blue, m_color.alpha);
        glTranslatef(0, m_height, 0);
        
        std::vector<GlyphKerning>::iterator it;
        for (it = m_glyphs.begin(); it != m_glyphs.end(); it++)
        {
            const Glyph *glyph = it->glyph;
            
            //Kerning.
            glTranslatef(it->kerning, 0, 0);
            glyph->draw();
            
            //Advance
            glTranslatef(glyph->getAdvance().x, -glyph->getAdvance().y, 0);
        }
        
        glColor4ub(255, 255, 255, 255); //Restore default color
        
        Drawable::popMatrix();
    }
}

void Text::setFont(Font &font)
{
    m_font = &font;
    loadGlyphs();
}

const Font *Text::getFont() const
{
    return m_font;
}

void Text::setText(const std::string &text)
{
    m_text = text;
    loadGlyphs();
}

const std::string &Text::getText() const
{
    return m_text;
}

void Text::setColor(const Color &color)
{
    m_color = color;
}

const Color &Text::getColor() const
{
    return m_color;
}

float Text::getWidth() const
{
    return m_width;
}

float Text::getHeight() const
{
    return m_height;
}

void Text::loadGlyphs()
{
    if (m_font != NULL)
    {
        //Clear current glyphs
        m_glyphs.clear();
        
        unsigned int textPosition = 0;
        unsigned int offset = UTF32_OFFSET_ERROR;
        
        float width = 0, top = 0, bottom = 0; // Compute dimensions of the text
        FT_ULong utf32char = 0, lastChar = 0;
        
        while (textPosition < m_text.size())
        {
            offset = Text::toUTF32(m_text, textPosition, utf32char);
            if (offset == UTF32_OFFSET_ERROR)
                break;
            
            const Glyph *glyph = m_font->getGlyph(utf32char);
            if (glyph == NULL)
            {
                textPosition += offset;
                continue;
            }
            
            float kerning = (textPosition > 0) ? m_font->getKerning(lastChar, utf32char) : 0.f;
            m_glyphs.push_back( GlyphKerning(glyph, kerning) );
            
            lastChar = utf32char;
            textPosition += offset;
            
            // Compute size
            width += kerning + ( (textPosition < m_text.size()) ? glyph->getAdvance().x : glyph->getWidth() );
            if (-glyph->getTopLeft().y < top) top = -glyph->getTopLeft().y;
            if (top + glyph->getHeight() > bottom) bottom = top + glyph->getHeight();
        }
        
        m_width = width;
        m_height = bottom - top;
    }
}

unsigned int Text::toUTF32(const std::string &utf8str, unsigned int position, FT_ULong &target)
{
    unsigned int bytesOffset = 1;
    unsigned int i = position;
    
    if ((utf8str[i] & 0xf8) == 0xf0)
        bytesOffset = 4;
    else if ((utf8str[i] & 0xf0) == 0xe0)
        bytesOffset = 3;
    else if ((utf8str[i] & 0xe0) == 0xc0)
        bytesOffset = 2;
    
    if (i + bytesOffset > utf8str.length())
        return UTF32_OFFSET_ERROR;
    
    if (bytesOffset == 1)
        target = static_cast<FT_ULong>(utf8str[i]);
    else if (bytesOffset == 2)
    {
        target = static_cast<FT_ULong>(
                    ( ( utf8str[i]   & 0x3F ) << 6 ) |
                      ( utf8str[i+1] & 0x3F )
                );
    }
    else if (bytesOffset == 3)
    {
        target = static_cast<FT_ULong>(
                    ( ( utf8str[i]   & 0x1F ) << 12 ) |
                    ( ( utf8str[i+1] & 0x3F ) << 6 ) |
                      ( utf8str[i+2] & 0x3F )
                );
    }
    else if (bytesOffset == 4)
    {
        target = static_cast<FT_ULong>(
                 ( ( utf8str[i]   & 0x0F ) << 18 ) |
                 ( ( utf8str[i+1] & 0x3F ) << 12 ) |
                 ( ( utf8str[i+2] & 0x3F ) << 6 ) |
                 (   utf8str[i+3] & 0x3F )
            );
    }
    
    return bytesOffset;
}
