/*
NFont: A font class for SDL and SDL_Renderer
by Jonathan Dearborn

See NFont.h for license info.
*/

#include "NFont.h"
#include "SDL_FontCache.h"

#include <cmath>
#include <cstdio>
#ifndef M_PI
    #define M_PI 3.14159265358979323846
#endif

#include <string>
#include <cstring>
#include <list>
using std::string;
using std::list;

#ifdef NFONT_USE_SDL_GPU
#define NFont_Target GPU_Target
#define NFont_Image GPU_Image
#define NFont_Log GPU_LogError
#else
#define NFont_Target SDL_Renderer
#define NFont_Image SDL_Texture
#define NFont_Log SDL_Log
#endif

#define MIN(a,b) ((a) < (b)? (a) : (b))
#define MAX(a,b) ((a) > (b)? (a) : (b))

#define NFONT_BUFFER_SIZE 1024

// vsnprintf replacement adapted from Valentin Milea:
// http://stackoverflow.com/questions/2915672/snprintf-and-visual-studio-2010
#if defined(_MSC_VER) && _MSC_VER < 1900

#define vsnprintf c99_vsnprintf

static int c99_vsnprintf(char *outBuf, size_t size, const char *format, va_list ap)
{
    int count = -1;

    if (size != 0)
        count = _vsnprintf_s(outBuf, size, _TRUNCATE, format, ap);
    if (count == -1)
        count = _vscprintf(format, ap);

    return count;
}

#endif



static inline SDL_Surface* createSurface24(Uint32 width, Uint32 height)
{
    #if SDL_BYTEORDER == SDL_BIG_ENDIAN
        return SDL_CreateRGBSurface(SDL_SWSURFACE, width, height, 24, 0xFF0000, 0x00FF00, 0x0000FF, 0);
    #else
        return SDL_CreateRGBSurface(SDL_SWSURFACE, width, height, 24, 0x0000FF, 0x00FF00, 0xFF0000, 0);
    #endif
}

static inline SDL_Surface* createSurface32(Uint32 width, Uint32 height)
{
    #if SDL_BYTEORDER == SDL_BIG_ENDIAN
        return SDL_CreateRGBSurface(SDL_SWSURFACE, width, height, 32, 0xFF000000, 0x00FF0000, 0x0000FF00, 0x000000FF);
    #else
        return SDL_CreateRGBSurface(SDL_SWSURFACE, width, height, 32, 0x000000FF, 0x0000FF00, 0x00FF0000, 0xFF000000);
    #endif
}

static inline char* copyString(const char* c)
{
    if(c == NULL)
        return NULL;

    char* result = new char[strlen(c)+1];
    strcpy(result, c);

    return result;
}

static inline Uint32 getPixel(SDL_Surface *Surface, int x, int y)
{
    Uint8* bits;
    Uint32 bpp;

    if(x < 0 || x >= Surface->w)
        return 0;  // Best I could do for errors

    bpp = Surface->format->BytesPerPixel;
    bits = ((Uint8*)Surface->pixels) + y*Surface->pitch + x*bpp;

    switch (bpp)
    {
        case 1:
            return *((Uint8*)Surface->pixels + y * Surface->pitch + x);
            break;
        case 2:
            return *((Uint16*)Surface->pixels + y * Surface->pitch/2 + x);
            break;
        case 3:
            // Endian-correct, but slower
            Uint8 r, g, b;
            r = *((bits)+Surface->format->Rshift/8);
            g = *((bits)+Surface->format->Gshift/8);
            b = *((bits)+Surface->format->Bshift/8);
            return SDL_MapRGB(Surface->format, r, g, b);
            break;
        case 4:
            return *((Uint32*)Surface->pixels + y * Surface->pitch/4 + x);
            break;
    }

    return 0;  // FIXME: Handle errors better
}

static inline void setPixel(SDL_Surface* surface, int x, int y, Uint32 color)
{
    int bpp = surface->format->BytesPerPixel;
    Uint8* bits = ((Uint8 *)surface->pixels) + y*surface->pitch + x*bpp;

    /* Set the pixel */
    switch(bpp)
    {
        case 1:
            *((Uint8 *)(bits)) = (Uint8)color;
            break;
        case 2:
            *((Uint16 *)(bits)) = (Uint16)color;
            break;
        case 3: { /* Format/endian independent */
            Uint8 r,g,b;
            r = (color >> surface->format->Rshift) & 0xFF;
            g = (color >> surface->format->Gshift) & 0xFF;
            b = (color >> surface->format->Bshift) & 0xFF;
            *((bits)+surface->format->Rshift/8) = r;
            *((bits)+surface->format->Gshift/8) = g;
            *((bits)+surface->format->Bshift/8) = b;
            }
            break;
        case 4:
            *((Uint32 *)(bits)) = (Uint32)color;
            break;
    }
}

static inline void drawPixel(SDL_Surface *surface, Sint16 x, Sint16 y, Uint32 color, Uint8 alpha)
{
	if(x > surface->clip_rect.x + surface->clip_rect.w || x < surface->clip_rect.x || y > surface->clip_rect.y + surface->clip_rect.h || y < surface->clip_rect.y)
        return;

    switch (surface->format->BytesPerPixel)
    {
        case 1: { /* Assuming 8-bpp */

                Uint8 *pixel = (Uint8 *)surface->pixels + y*surface->pitch + x;

                Uint8 dR = surface->format->palette->colors[*pixel].r;
                Uint8 dG = surface->format->palette->colors[*pixel].g;
                Uint8 dB = surface->format->palette->colors[*pixel].b;
                Uint8 sR = surface->format->palette->colors[color].r;
                Uint8 sG = surface->format->palette->colors[color].g;
                Uint8 sB = surface->format->palette->colors[color].b;

                dR = dR + ((sR-dR)*alpha >> 8);
                dG = dG + ((sG-dG)*alpha >> 8);
                dB = dB + ((sB-dB)*alpha >> 8);

                *pixel = SDL_MapRGB(surface->format, dR, dG, dB);

        }
        break;

        case 2: { /* Probably 15-bpp or 16-bpp */

                Uint32 Rmask = surface->format->Rmask, Gmask = surface->format->Gmask, Bmask = surface->format->Bmask, Amask = surface->format->Amask;
                Uint16 *pixel = (Uint16 *)surface->pixels + y*surface->pitch/2 + x;
                Uint32 dc = *pixel;
                Uint32 R,G,B,A=0;

                R = ((dc & Rmask) + (( (color & Rmask) - (dc & Rmask) ) * alpha >> 8)) & Rmask;
                G = ((dc & Gmask) + (( (color & Gmask) - (dc & Gmask) ) * alpha >> 8)) & Gmask;
                B = ((dc & Bmask) + (( (color & Bmask) - (dc & Bmask) ) * alpha >> 8)) & Bmask;
                if( Amask )
                    A = ((dc & Amask) + (( (color & Amask) - (dc & Amask) ) * alpha >> 8)) & Amask;

                *pixel= R | G | B | A;

        }
        break;

        case 3: { /* Slow 24-bpp mode, usually not used */
            Uint8 *pix = (Uint8 *)surface->pixels + y * surface->pitch + x*3;
            Uint8 rshift8=surface->format->Rshift/8;
            Uint8 gshift8=surface->format->Gshift/8;
            Uint8 bshift8=surface->format->Bshift/8;
            Uint8 ashift8=surface->format->Ashift/8;



                Uint8 dR, dG, dB, dA;
                Uint8 sR, sG, sB, sA;

                pix = (Uint8 *)surface->pixels + y * surface->pitch + x*3;

                dR = *((pix)+rshift8);
                dG = *((pix)+gshift8);
                dB = *((pix)+bshift8);
                dA = *((pix)+ashift8);

                sR = (color>>surface->format->Rshift)&0xff;
                sG = (color>>surface->format->Gshift)&0xff;
                sB = (color>>surface->format->Bshift)&0xff;
                sA = (color>>surface->format->Ashift)&0xff;

                dR = dR + ((sR-dR)*alpha >> 8);
                dG = dG + ((sG-dG)*alpha >> 8);
                dB = dB + ((sB-dB)*alpha >> 8);
                dA = dA + ((sA-dA)*alpha >> 8);

                *((pix)+rshift8) = dR;
                *((pix)+gshift8) = dG;
                *((pix)+bshift8) = dB;
                *((pix)+ashift8) = dA;

        }
        break;

        case 4: { /* Probably 32-bpp */
            Uint32 Rmask = surface->format->Rmask, Gmask = surface->format->Gmask, Bmask = surface->format->Bmask, Amask = surface->format->Amask;
            Uint32* pixel = (Uint32*)surface->pixels + y*surface->pitch/4 + x;
            Uint32 source = *pixel;
            Uint32 R,G,B,A;
            R = color & Rmask;
            G = color & Gmask;
            B = color & Bmask;
            A = 0;  // keep this as 0 to avoid corruption of non-alpha surfaces

            // Blend and keep dest alpha
            if( alpha != SDL_ALPHA_OPAQUE ){
                R = ((source & Rmask) + (( R - (source & Rmask) ) * alpha >> 8)) & Rmask;
                G = ((source & Gmask) + (( G - (source & Gmask) ) * alpha >> 8)) & Gmask;
                B = ((source & Bmask) + (( B - (source & Bmask) ) * alpha >> 8)) & Bmask;
            }
            if(Amask)
                A = (source & Amask);

            *pixel = R | G | B | A;
        }
        break;
    }
}

static inline NFont::Rectf rectUnion(const NFont::Rectf& A, const NFont::Rectf& B)
{
    float x,x2,y,y2;
    x = MIN(A.x, B.x);
    y = MIN(A.y, B.y);
    x2 = MAX(A.x+A.w, B.x+B.w);
    y2 = MAX(A.y+A.h, B.y+B.h);
    NFont::Rectf result(x, y, MAX(0, x2 - x), MAX(0, y2 - y));
    return result;
}

// Adapted from SDL_IntersectRect
static inline NFont::Rectf rectIntersect(const NFont::Rectf& A, const NFont::Rectf& B)
{
    NFont::Rectf result;
	float Amin, Amax, Bmin, Bmax;

	// Horizontal intersection
	Amin = A.x;
	Amax = Amin + A.w;
	Bmin = B.x;
	Bmax = Bmin + B.w;
	if(Bmin > Amin)
	        Amin = Bmin;
	result.x = Amin;
	if(Bmax < Amax)
	        Amax = Bmax;
	result.w = Amax - Amin > 0 ? Amax - Amin : 0;

	// Vertical intersection
	Amin = A.y;
	Amax = Amin + A.h;
	Bmin = B.y;
	Bmax = Bmin + B.h;
	if(Bmin > Amin)
	        Amin = Bmin;
	result.y = Amin;
	if(Bmax < Amax)
	        Amax = Bmax;
	result.h = Amax - Amin > 0 ? Amax - Amin : 0;

	return result;
}

static inline SDL_Surface* copySurface(SDL_Surface *Surface)
{
    return SDL_ConvertSurface(Surface, Surface->format, Surface->flags);
}









NFont::Color::Color()
    : r(0), g(0), b(0), a(255)
{}
NFont::Color::Color(Uint8 r, Uint8 g, Uint8 b)
    : r(r), g(g), b(b), a(255)
{}
NFont::Color::Color(Uint8 r, Uint8 g, Uint8 b, Uint8 a)
    : r(r), g(g), b(b), a(a)
{}
NFont::Color::Color(const SDL_Color& color)
    : r(color.r), g(color.g), b(color.b), a(color.a)
{}

NFont::Color& NFont::Color::rgb(Uint8 R, Uint8 G, Uint8 B)
{
    r = R;
    g = G;
    b = B;

    return *this;
}

NFont::Color& NFont::Color::rgba(Uint8 R, Uint8 G, Uint8 B, Uint8 A)
{
    r = R;
    g = G;
    b = B;
    a = A;

    return *this;
}

NFont::Color& NFont::Color::color(const SDL_Color& color)
{
    r = color.r;
    g = color.g;
    b = color.b;
    a = color.a;

    return *this;
}

SDL_Color NFont::Color::to_SDL_Color() const
{
    SDL_Color c = {r, g, b, a};
    return c;
}




NFont::Rectf::Rectf()
    : x(0), y(0), w(0), h(0)
{}

NFont::Rectf::Rectf(float x, float y)
    : x(x), y(y), w(0), h(0)
{}

NFont::Rectf::Rectf(float x, float y, float w, float h)
    : x(x), y(y), w(w), h(h)
{}

NFont::Rectf::Rectf(const SDL_Rect& rect)
    : x(rect.x), y(rect.y), w(rect.w), h(rect.h)
{}

#ifdef NFONT_USE_SDL_GPU
NFont::Rectf::Rectf(const GPU_Rect& rect)
    : x(rect.x), y(rect.y), w(rect.w), h(rect.h)
{}
#endif

SDL_Rect NFont::Rectf::to_SDL_Rect() const
{
    SDL_Rect r = {int(x), int(y), int(w), int(h)};
    return r;
}

#ifdef NFONT_USE_SDL_GPU
GPU_Rect NFont::Rectf::to_GPU_Rect() const
{
    return GPU_MakeRect(x, y, w, h);
}
#endif







char* NFont::buffer = NULL;  // Shared buffer for efficient drawing


// Constructors
NFont::NFont()
{
    init();
}


#ifdef NFONT_USE_SDL_GPU
NFont::NFont(TTF_Font* ttf)
{
    init();
    load(ttf, FC_GetDefaultColor(font));
}
NFont::NFont(TTF_Font* ttf, const NFont::Color& color)
{
    init();
    load(ttf, color);
}
NFont::NFont(const char* filename_ttf, Uint32 pointSize)
{
    init();
    load(filename_ttf, pointSize);
}
NFont::NFont(const char* filename_ttf, Uint32 pointSize, const NFont::Color& color, int style)
{
    init();
    load(filename_ttf, pointSize, color, style);
}
NFont::NFont(SDL_RWops* file_rwops_ttf, Uint8 own_rwops, Uint32 pointSize, const NFont::Color& color, int style)
{
    init();
    load(file_rwops_ttf, own_rwops, pointSize, color, style);
}

#else

NFont::NFont(NFont_Target* renderer, TTF_Font* ttf)
{
    init();
    load(renderer, ttf, FC_GetDefaultColor(font));
}
NFont::NFont(NFont_Target* renderer, TTF_Font* ttf, const NFont::Color& color)
{
    init();
    load(renderer, ttf, color);
}
NFont::NFont(NFont_Target* renderer, const char* filename_ttf, Uint32 pointSize)
{
    init();
    load(renderer, filename_ttf, pointSize);
}
NFont::NFont(NFont_Target* renderer, const char* filename_ttf, Uint32 pointSize, const NFont::Color& color, int style)
{
    init();
    load(renderer, filename_ttf, pointSize, color, style);
}
NFont::NFont(NFont_Target* renderer, SDL_RWops* file_rwops_ttf, Uint8 own_rwops, Uint32 pointSize, const NFont::Color& color, int style)
{
    init();
    load(renderer, file_rwops_ttf, own_rwops, pointSize, color, style);
}
#endif


NFont::~NFont()
{
    FC_FreeFont(font);
}

void NFont::init()
{
    font = FC_CreateFont();

    if(buffer == NULL)
        buffer = new char[NFONT_BUFFER_SIZE];
}






void NFont::setLoadingString(const char* str)
{
    FC_SetLoadingString(font, str);
}

#ifdef NFONT_USE_SDL_GPU
bool NFont::load(TTF_Font* ttf)
#else
bool NFont::load(NFont_Target* renderer, TTF_Font* ttf)
#endif
{
    #ifdef NFONT_USE_SDL_GPU
    return load(ttf, FC_GetDefaultColor(font));
    #else
    return load(renderer, ttf, Color(0,0,0,255));
    #endif
}

#ifdef NFONT_USE_SDL_GPU
bool NFont::load(TTF_Font* ttf, const NFont::Color& color)
#else
bool NFont::load(NFont_Target* renderer, TTF_Font* ttf, const NFont::Color& color)
#endif
{
    if(ttf == NULL)
        return false;

    #ifndef NFONT_USE_SDL_GPU
    if(renderer == NULL)
        return false;
    #endif

    FC_ClearFont(font);
    #ifdef NFONT_USE_SDL_GPU
    return FC_LoadFontFromTTF(font, ttf, color.to_SDL_Color());
    #else
    return FC_LoadFontFromTTF(font, renderer, ttf, color.to_SDL_Color());
    #endif
}

#ifdef NFONT_USE_SDL_GPU
bool NFont::load(const char* filename_ttf, Uint32 pointSize)
#else
bool NFont::load(NFont_Target* renderer, const char* filename_ttf, Uint32 pointSize)
#endif
{
    #ifdef NFONT_USE_SDL_GPU
    return load(filename_ttf, pointSize, Color(0,0,0,255));
    #else
    return load(renderer, filename_ttf, pointSize, Color(0,0,0,255));
    #endif
}

#ifdef NFONT_USE_SDL_GPU
bool NFont::load(const char* filename_ttf, Uint32 pointSize, const NFont::Color& color, int style)
#else
bool NFont::load(NFont_Target* renderer, const char* filename_ttf, Uint32 pointSize, const NFont::Color& color, int style)
#endif
{
    FC_ClearFont(font);
    #ifdef NFONT_USE_SDL_GPU
    return FC_LoadFont(font, filename_ttf, pointSize, color.to_SDL_Color(), style);
    #else
    return FC_LoadFont(font, renderer, filename_ttf, pointSize, color.to_SDL_Color(), style);
    #endif
}

#ifdef NFONT_USE_SDL_GPU
bool NFont::load(SDL_RWops* file_rwops_ttf, Uint8 own_rwops, Uint32 pointSize, const NFont::Color& color, int style)
#else
bool NFont::load(NFont_Target* renderer, SDL_RWops* file_rwops_ttf, Uint8 own_rwops, Uint32 pointSize, const NFont::Color& color, int style)
#endif
{
    FC_ClearFont(font);
    #ifdef NFONT_USE_SDL_GPU
    return FC_LoadFont_RW(font, file_rwops_ttf, own_rwops, pointSize, color.to_SDL_Color(), style);
    #else
    return FC_LoadFont_RW(font, renderer, file_rwops_ttf, own_rwops, pointSize, color.to_SDL_Color(), style);
    #endif
}



void NFont::free()
{
    FC_ClearFont(font);
}



NFont::Rectf NFont::draw(NFont_Target* dest, float x, float y, const char* formatted_text, ...)
{
    if(formatted_text == NULL)
        return Rectf(x, y, 0, 0);

    va_list lst;
    va_start(lst, formatted_text);
    vsnprintf(buffer, NFONT_BUFFER_SIZE, formatted_text, lst);
    va_end(lst);

    return FC_Draw(font, dest, x, y, "%s", buffer);
}

/*static int getIndexPastWidth(const char* text, int width, const int* charWidth)
{
    int charnum;
    int len = strlen(text);

    for (int index = 0; index < len; index++)
    {
        char c = text[index];
        charnum = (unsigned char)(c) - 33;

        // spaces and nonprintable characters
        if (c == ' ' || charnum > 222)
        {
            width -= charWidth[0];
        }
        else
            width -= charWidth[charnum];

        if(width <= 0)
            return index;
    }
    return 0;
}*/



/*static list<string> explode(const string& str, char delimiter)
{
    list<string> result;

    size_t oldPos = 0;
    size_t pos = str.find_first_of(delimiter);
    while(pos != string::npos)
    {
        result.push_back(str.substr(oldPos, pos - oldPos));
        oldPos = pos+1;
        pos = str.find_first_of(delimiter, oldPos);
    }

    result.push_back(str.substr(oldPos, string::npos));

    return result;
}*/

NFont::Rectf NFont::drawBox(NFont_Target* dest, const Rectf& box, const char* formatted_text, ...)
{
    if(formatted_text == NULL)
        return Rectf(box.x, box.y, 0, 0);

    va_list lst;
    va_start(lst, formatted_text);
    vsnprintf(buffer, NFONT_BUFFER_SIZE, formatted_text, lst);
    va_end(lst);

    #ifdef NFONT_USE_SDL_GPU
    return FC_DrawBox(font, dest, box.to_GPU_Rect(), "%s", buffer);
    #else
    return FC_DrawBox(font, dest, box.to_SDL_Rect(), "%s", buffer);
    #endif
}

static FC_AlignEnum translate_enum_NFont_to_FC(NFont::AlignEnum align)
{
    switch(align)
    {
    case NFont::LEFT:
        return FC_ALIGN_LEFT;
    case NFont::CENTER:
        return FC_ALIGN_CENTER;
    case NFont::RIGHT:
        return FC_ALIGN_RIGHT;
    default:
        return FC_ALIGN_LEFT;
    }
}

NFont::Rectf NFont::drawBox(NFont_Target* dest, const Rectf& box, AlignEnum align, const char* formatted_text, ...)
{
    if(formatted_text == NULL)
        return Rectf(box.x, box.y, 0, 0);

    va_list lst;
    va_start(lst, formatted_text);
    vsnprintf(buffer, NFONT_BUFFER_SIZE, formatted_text, lst);
    va_end(lst);

    #ifdef NFONT_USE_SDL_GPU
    return FC_DrawBoxAlign(font, dest, box.to_GPU_Rect(), translate_enum_NFont_to_FC(align), "%s", buffer);
    #else
    return FC_DrawBoxAlign(font, dest, box.to_SDL_Rect(), translate_enum_NFont_to_FC(align), "%s", buffer);
    #endif
}

NFont::Rectf NFont::drawBox(NFont_Target* dest, const Rectf& box, const Scale& scale, const char* formatted_text, ...)
{
    if(formatted_text == NULL)
        return Rectf(box.x, box.y, 0, 0);

    va_list lst;
    va_start(lst, formatted_text);
    vsnprintf(buffer, NFONT_BUFFER_SIZE, formatted_text, lst);
    va_end(lst);

    #ifdef NFONT_USE_SDL_GPU
    return FC_DrawBoxScale(font, dest, box.to_GPU_Rect(), FC_MakeScale(scale.x, scale.y), "%s", buffer);
    #else
    return FC_DrawBoxScale(font, dest, box.to_SDL_Rect(), FC_MakeScale(scale.x, scale.y), "%s", buffer);
    #endif
}

NFont::Rectf NFont::drawBox(NFont_Target* dest, const Rectf& box, const Color& color, const char* formatted_text, ...)
{
    if(formatted_text == NULL)
        return Rectf(box.x, box.y, 0, 0);

    va_list lst;
    va_start(lst, formatted_text);
    vsnprintf(buffer, NFONT_BUFFER_SIZE, formatted_text, lst);
    va_end(lst);

    #ifdef NFONT_USE_SDL_GPU
    return FC_DrawBoxColor(font, dest, box.to_GPU_Rect(), color.to_SDL_Color(), "%s", buffer);
    #else
    return FC_DrawBoxColor(font, dest, box.to_SDL_Rect(), color.to_SDL_Color(), "%s", buffer);
    #endif
}

NFont::Rectf NFont::drawBox(NFont_Target* dest, const Rectf& box, const Effect& effect, const char* formatted_text, ...)
{
    if(formatted_text == NULL)
        return Rectf(box.x, box.y, 0, 0);

    va_list lst;
    va_start(lst, formatted_text);
    vsnprintf(buffer, NFONT_BUFFER_SIZE, formatted_text, lst);
    va_end(lst);

    #ifdef NFONT_USE_SDL_GPU
    return FC_DrawBoxEffect(font, dest, box.to_GPU_Rect(), FC_MakeEffect(translate_enum_NFont_to_FC(effect.alignment), FC_MakeScale(effect.scale.x, effect.scale.y), effect.color.to_SDL_Color()), "%s", buffer);
    #else
    return FC_DrawBoxEffect(font, dest, box.to_SDL_Rect(), FC_MakeEffect(translate_enum_NFont_to_FC(effect.alignment), FC_MakeScale(effect.scale.x, effect.scale.y), effect.color.to_SDL_Color()), "%s", buffer);
    #endif
}

NFont::Rectf NFont::drawColumn(NFont_Target* dest, float x, float y, Uint16 width, const char* formatted_text, ...)
{
    if(formatted_text == NULL)
        return Rectf(x, y, 0, 0);

    va_list lst;
    va_start(lst, formatted_text);
    vsnprintf(buffer, NFONT_BUFFER_SIZE, formatted_text, lst);
    va_end(lst);

    return FC_DrawColumn(font, dest, x, y, width, "%s", buffer);
}

NFont::Rectf NFont::drawColumn(NFont_Target* dest, float x, float y, Uint16 width, AlignEnum align, const char* formatted_text, ...)
{
    if(formatted_text == NULL)
        return Rectf(x, y, 0, 0);

    va_list lst;
    va_start(lst, formatted_text);
    vsnprintf(buffer, NFONT_BUFFER_SIZE, formatted_text, lst);
    va_end(lst);

    return FC_DrawColumnAlign(font, dest, x, y, width, translate_enum_NFont_to_FC(align), "%s", buffer);
}

NFont::Rectf NFont::drawColumn(NFont_Target* dest, float x, float y, Uint16 width, const Scale& scale, const char* formatted_text, ...)
{
    if(formatted_text == NULL)
        return Rectf(x, y, 0, 0);

    va_list lst;
    va_start(lst, formatted_text);
    vsnprintf(buffer, NFONT_BUFFER_SIZE, formatted_text, lst);
    va_end(lst);

    return FC_DrawColumnScale(font, dest, x, y, width, FC_MakeScale(scale.x, scale.y), "%s", buffer);
}

NFont::Rectf NFont::drawColumn(NFont_Target* dest, float x, float y, Uint16 width, const Color& color, const char* formatted_text, ...)
{
    if(formatted_text == NULL)
        return Rectf(x, y, 0, 0);

    va_list lst;
    va_start(lst, formatted_text);
    vsnprintf(buffer, NFONT_BUFFER_SIZE, formatted_text, lst);
    va_end(lst);

    return FC_DrawColumnColor(font, dest, x, y, width, color.to_SDL_Color(), "%s", buffer);
}

NFont::Rectf NFont::drawColumn(NFont_Target* dest, float x, float y, Uint16 width, const Effect& effect, const char* formatted_text, ...)
{
    if(formatted_text == NULL)
        return Rectf(x, y, 0, 0);

    va_list lst;
    va_start(lst, formatted_text);
    vsnprintf(buffer, NFONT_BUFFER_SIZE, formatted_text, lst);
    va_end(lst);

    #ifdef NFONT_USE_SDL_GPU
    return FC_DrawColumnEffect(font, dest, x, y, width, FC_MakeEffect(translate_enum_NFont_to_FC(effect.alignment), FC_MakeScale(effect.scale.x, effect.scale.y), effect.color.to_SDL_Color()), "%s", buffer);
    #else
    return FC_DrawColumnEffect(font, dest, x, y, width, FC_MakeEffect(translate_enum_NFont_to_FC(effect.alignment), FC_MakeScale(effect.scale.x, effect.scale.y), effect.color.to_SDL_Color()), "%s", buffer);
    #endif
}



NFont::Rectf NFont::draw(NFont_Target* dest, float x, float y, const Scale& scale, const char* formatted_text, ...)
{
    if(formatted_text == NULL)
        return Rectf(x, y, 0, 0);

    va_list lst;
    va_start(lst, formatted_text);
    vsnprintf(buffer, NFONT_BUFFER_SIZE, formatted_text, lst);
    va_end(lst);

    return FC_DrawScale(font, dest, x, y, FC_MakeScale(scale.x, scale.y), "%s", buffer);
}

NFont::Rectf NFont::draw(NFont_Target* dest, float x, float y, AlignEnum align, const char* formatted_text, ...)
{
    if(formatted_text == NULL)
        return Rectf(x, y, 0, 0);

    va_list lst;
    va_start(lst, formatted_text);
    vsnprintf(buffer, NFONT_BUFFER_SIZE, formatted_text, lst);
    va_end(lst);

    return FC_DrawAlign(font, dest, x, y, translate_enum_NFont_to_FC(align), "%s", buffer);
}

NFont::Rectf NFont::draw(NFont_Target* dest, float x, float y, const Color& color, const char* formatted_text, ...)
{
    if(formatted_text == NULL)
        return Rectf(x, y, 0, 0);

    va_list lst;
    va_start(lst, formatted_text);
    vsnprintf(buffer, NFONT_BUFFER_SIZE, formatted_text, lst);
    va_end(lst);

    return FC_DrawColor(font, dest, x, y, color.to_SDL_Color(), "%s", buffer);
}


NFont::Rectf NFont::draw(NFont_Target* dest, float x, float y, const Effect& effect, const char* formatted_text, ...)
{
    if(formatted_text == NULL)
        return Rectf(x, y, 0, 0);

    va_list lst;
    va_start(lst, formatted_text);
    vsnprintf(buffer, NFONT_BUFFER_SIZE, formatted_text, lst);
    va_end(lst);

    return FC_DrawEffect(font, dest, x, y, FC_MakeEffect(translate_enum_NFont_to_FC(effect.alignment), FC_MakeScale(effect.scale.x, effect.scale.y), effect.color.to_SDL_Color()), "%s", buffer);
}




// Getters


NFont::FilterEnum NFont::getFilterMode() const
{
    FC_FilterEnum f = FC_GetFilterMode(font);
    if(f == FC_FILTER_LINEAR)
        return NFont::LINEAR;
    return NFont::NEAREST;
}

Uint16 NFont::getHeight() const
{
    return FC_GetLineHeight(font);
}

Uint16 NFont::getHeight(const char* formatted_text, ...) const
{
    if(formatted_text == NULL)
        return 0;

    va_list lst;
    va_start(lst, formatted_text);
    vsnprintf(buffer, NFONT_BUFFER_SIZE, formatted_text, lst);
    va_end(lst);

    return FC_GetHeight(font, "%s", buffer);
}

Uint16 NFont::getWidth(const char* formatted_text, ...)
{
    if (formatted_text == NULL)
        return 0;

    va_list lst;
    va_start(lst, formatted_text);
    vsnprintf(buffer, NFONT_BUFFER_SIZE, formatted_text, lst);
    va_end(lst);

    return FC_GetWidth(font, "%s", buffer);
}


NFont::Rectf NFont::getCharacterOffset(Uint16 position_index, int column_width, const char* formatted_text, ...)
{
    if(formatted_text == NULL)
        return Rectf(0,0,0,0);

    va_list lst;
    va_start(lst, formatted_text);
    vsnprintf(buffer, NFONT_BUFFER_SIZE, formatted_text, lst);
    va_end(lst);

    return FC_GetCharacterOffset(font, position_index, column_width, "%s", buffer);
}

// Given an offset (x,y) from the text draw position (the upper-left corner), returns the character position (UTF-8 index)
Uint16 NFont::getPositionFromOffset(float x, float y, int column_width, NFont::AlignEnum align, const char* formatted_text, ...)
{
    if(formatted_text == NULL)
        return 0;

    va_list lst;
    va_start(lst, formatted_text);
    vsnprintf(buffer, NFONT_BUFFER_SIZE, formatted_text, lst);
    va_end(lst);

    return FC_GetPositionFromOffset(font, x, y, column_width, translate_enum_NFont_to_FC(align), "%s", buffer);
}


Uint16 NFont::getColumnHeight(Uint16 width, const char* formatted_text, ...)
{
    if(formatted_text == NULL || width == 0)
        return 0;

    va_list lst;
    va_start(lst, formatted_text);
    vsnprintf(buffer, NFONT_BUFFER_SIZE, formatted_text, lst);
    va_end(lst);

    return FC_GetColumnHeight(font, width, "%s", buffer);
}

int NFont::getAscent(const char character)
{
    return FC_GetAscent(font, "%c", character);
}

int NFont::getAscent() const
{
    return FC_GetAscent(font, NULL);
}

int NFont::getAscent(const char* formatted_text, ...)
{
    if(formatted_text == NULL)
        return FC_GetAscent(font, NULL);

    va_list lst;
    va_start(lst, formatted_text);
    vsnprintf(buffer, NFONT_BUFFER_SIZE, formatted_text, lst);
    va_end(lst);

    return FC_GetAscent(font, "%s", buffer);
}

int NFont::getDescent(const char character)
{
    return FC_GetDescent(font, "%c", character);
}

int NFont::getDescent() const
{
    return FC_GetDescent(font, NULL);
}

int NFont::getDescent(const char* formatted_text, ...)
{
    if(formatted_text == NULL)
        return FC_GetDescent(font, NULL);

    va_list lst;
    va_start(lst, formatted_text);
    vsnprintf(buffer, NFONT_BUFFER_SIZE, formatted_text, lst);
    va_end(lst);

    return FC_GetDescent(font, "%s", buffer);
}

int NFont::getSpacing() const
{
    return FC_GetSpacing(font);
}

int NFont::getLineSpacing() const
{
    return FC_GetLineSpacing(font);
}

Uint16 NFont::getBaseline() const
{
    return FC_GetBaseline(font);
}

Uint16 NFont::getMaxWidth() const
{
    return FC_GetMaxWidth(font);
}

NFont::Color NFont::getDefaultColor() const
{
    return FC_GetDefaultColor(font);
}

    
int NFont::getNumCacheLevels() const
{
    return FC_GetNumCacheLevels(font);
}

NFont_Image* NFont::getCacheLevel(int level) const
{
    return FC_GetGlyphCacheLevel(font, level);
}





// Setters

void NFont::setFilterMode(NFont::FilterEnum filter)
{
    if(filter == NFont::LINEAR)
        FC_SetFilterMode(font, FC_FILTER_LINEAR);
    else
        FC_SetFilterMode(font, FC_FILTER_NEAREST);
}

void NFont::setSpacing(int LetterSpacing)
{
    FC_SetSpacing(font, LetterSpacing);
}

void NFont::setLineSpacing(int LineSpacing)
{
    FC_SetLineSpacing(font, LineSpacing);
}

void NFont::setBaseline()
{

}

void NFont::setDefaultColor(const Color& color)
{
    FC_SetDefaultColor(font, color.to_SDL_Color());
}

void NFont::enableTTFOwnership()
{

}




