/*
 * DMLib
 * -- Bitmap and TTF text & font support
 * Programmed and designed by Matti 'ccr' Hamalainen
 * (C) Copyright 2012 Tecnic Software productions (TNSP)
 */
#include "dmtext.h"


void dmDrawBMTextConst(SDL_Surface *screen, DMBitmapFont *font, BOOL condensed, int mode, int xc, int yc, const char *fmt)
{
    const char *ptr = fmt;
    DMUnscaledBlitFunc blit = NULL;

    while (*ptr)
    {
        int ch = *ptr++;
        SDL_Surface *glyph;

        if (ch >= 0 && ch < font->nglyphs && (glyph = font->glyphs[ch]) != NULL)
        {
            if (blit == NULL)
                blit = dmGetUnscaledBlitFunc(glyph->format, screen->format, mode);
            
            blit(glyph, xc, yc, screen);

            xc += condensed ? glyph->w : font->width;
        }
        else
            xc += font->width;
    }
}


void dmDrawBMTextVA(SDL_Surface *screen, DMBitmapFont *font, BOOL condensed, int mode, int xc, int yc, const char *fmt, va_list ap)
{
    char tmp[512];
    vsnprintf(tmp, sizeof(tmp), fmt, ap);
    dmDrawBMTextConst(screen, font, condensed, mode, xc, yc, tmp);
    dmFree(tmp);
}


void dmDrawBMText(SDL_Surface *screen, DMBitmapFont *font, BOOL condensed, int mode, int xc, int yc, const char *fmt, ...)
{
    va_list ap;
    
    va_start(ap, fmt);
    dmDrawBMTextVA(screen, font, condensed, mode, xc, yc, fmt, ap);
    va_end(ap);
}


DMBitmapFont *dmNewBitmapFont(int nglyphs, int width, int height)
{
    DMBitmapFont *font = dmMalloc0(sizeof(DMBitmapFont));
    if (font == NULL)
        return NULL;
    
    font->width = width;
    font->height = height;
    font->nglyphs = nglyphs;
    font->glyphs = dmCalloc(nglyphs, sizeof(SDL_Surface *));
    if (font->glyphs == NULL)
    {
        dmFree(font);
        return NULL;
    }

    return font;
}


int dmFreeBitmapFont(DMBitmapFont *font)
{
    int i;

    if (font == NULL)
        return DMERR_NULLPTR;
    
    for (i = 0; i < font->nglyphs; i++)
    {
        if (font->glyphs[i] != NULL)
        {
            SDL_FreeSurface(font->glyphs[i]);
            font->glyphs[i] = NULL;
        }
    }

    dmFree(font);
    return DMERR_OK;
}


/* Set the palette for each glyph. While the function allows you to
 * specify 'start' and 'end' indices and palette array freely, you should
 * typically use DMFONT_NPALETTE size palette starting at index 0.
 */
int dmSetBitmapFontPalette(DMBitmapFont *font, SDL_Color *pal, int start, int size)
{
    int i;

    if (font == NULL)
        return DMERR_NULLPTR;
    
    if (start < 0 || size < 1)
        return DMERR_INVALID_ARGS;
    
    for (i = 0; i < font->nglyphs; i++)
    {
        SDL_Surface *glyph = font->glyphs[i];
        if (glyph != NULL)
        {
            SDL_SetColors(glyph, pal, start, size);
        }
    }

    return DMERR_OK;
}

//#define FN_DEBUG

int dmLoadBitmapFont(DMResource *res, DMBitmapFont **pfont)
{
    DMBitmapFont *font;
    char magic[8];
    Uint16 version, nglyphs, maxglyph;
    int width, height;
    BOOL tsfont = FALSE;
    
    // Check magic and version
    dmf_read_str(res, (Uint8 *) &magic, 6);
    dmf_read_le16(res, &version);
    
    // Check if it is a legacy TSFONT file
    if (memcmp(magic, TSFONT_MAGIC, 6) == 0)
    {
        // Yep, we handle these a bit differently
        int encoding = dmfgetc(res);
        tsfont = TRUE;

        if (version > TSFONT_VERSION)
            return DMERR_VERSION;

#ifdef FN_DEBUG
        fprintf(stderr, "TSFONT v%d.%d (0x%04x), encoding=%d\n", version >> 8, version & 0xff, version, encoding);
#endif
        
        // There were only two encodings, 0 = none and 1 = RLE
        // of which RLE was never actually used ... derp.
        if (encoding != 0)
            return DMERR_NOT_SUPPORTED;
    }
    else
    {
        if (memcmp(magic, DMFONT_MAGIC, 6) != 0)
            return DMERR_INVALID;

        if (version > DMFONT_VERSION)
            return DMERR_VERSION;
    }
    
    // Read other header data
    if (tsfont)
    {
        // TSFONT only has number of glyphs stored in the file
        nglyphs = dmfgetc(res);
        
        // Maximum glyph number
        maxglyph = 256;
    }
    else
    {
        dmf_read_le16(res, &nglyphs);
        dmf_read_le16(res, &maxglyph);
    }
    
    width = dmfgetc(res);
    height = dmfgetc(res);

#ifdef FN_DEBUG
    fprintf(stderr, "nglyphs=%d (0x%02x), maxglyph=%d (0x%02x) width=%d, height=%d\n",
        nglyphs, nglyphs, maxglyph, maxglyph, width, height);
#endif

    if (tsfont)
    {
        // TSFONT color assignments (boolean) .. we discard this.
        dmfgetc(res);
        
        // Very old TSFONTs have some extra data that is not used
        // .. can't actually even remember what it was for.
        if (version == 0x0200)
        {
            int i;
            for (i = 0; i < 32; i++)
                dmfgetc(res);
        }
    }

    if (width < DMFONT_MIN_WIDTH ||
        height < DMFONT_MIN_HEIGHT ||
        width > DMFONT_MAX_WIDTH ||
        height > DMFONT_MAX_HEIGHT ||
        nglyphs > DMFONT_MAX_GLYPHS ||
        maxglyph > DMFONT_MAX_GLYPHS ||
        maxglyph < 1)
        return DMERR_INVALID_DATA;

    // Allocate font
    if ((*pfont = font = dmNewBitmapFont(maxglyph, width, height)) == NULL)
        return DMERR_MALLOC;

    // Read glyph data, if any
    if (nglyphs > 0)
    {
        int n, i;
        Uint32 BitsPerPixel, Rmask, Gmask, Bmask, Amask;
        SDL_Color pal[DMFONT_NPALETTE];

        // Setup palette for 8bpp fonts
        for (n = 0; n < DMFONT_NPALETTE; n++)
        {
            pal[n].r = pal[n].g = pal[n].b = 0;
            pal[n].unused = n > 0 ? 255 : 0;
        }

        if (tsfont)
        {
            BitsPerPixel = 8;
            Rmask = Gmask = Bmask = Amask = 0;
        }
        else
        {
            BitsPerPixel = dmfgetc(res);
            dmf_read_le32(res, &Rmask);
            dmf_read_le32(res, &Gmask);
            dmf_read_le32(res, &Bmask);
            dmf_read_le32(res, &Amask);
        }

        for (i = 0; i < nglyphs; i++)
        {
            int y;
            Uint16 index;
            Uint8 *pixels;
            SDL_Surface *glyph;

            // TSFONT format has only byte sized index
            if (tsfont)
                index = dmfgetc(res);
            else
                dmf_read_le16(res, &index);

            // Read dimensions
            width = dmfgetc(res);
            height = dmfgetc(res);

#ifdef FN_DEBUG
            fprintf(stderr, "#%d @ %d - w=%d, h=%d\n", i, index, width, height);
#endif

            if (width < DMFONT_MIN_WIDTH ||
                height < DMFONT_MIN_HEIGHT ||
                width > DMFONT_MAX_WIDTH ||
                height > DMFONT_MAX_HEIGHT ||
                index >= maxglyph)
                return DMERR_INVALID_DATA;

            // Allocate bitmap
            font->glyphs[index] = glyph = SDL_CreateRGBSurface(
                SDL_SWSURFACE, width, height,
                BitsPerPixel, Rmask, Gmask,
                Bmask,
                Amask);

            if (glyph == NULL)
                return DMERR_MALLOC;

            if (BitsPerPixel == 8)
                SDL_SetColors(glyph, pal, 0, DMFONT_NPALETTE);

            // Read pixel data
            pixels = glyph->pixels;
            for (y = 0; y < glyph->h; y++)
            {
                if (dmfread(pixels, glyph->format->BytesPerPixel, glyph->w, res) != (size_t) glyph->w)
                    return DMERR_FREAD;
                pixels += glyph->pitch;
            }
        }
    }

    return DMERR_OK;
}
