


#include "mvprender.h"
#include <fcntl.h>

#define BUF_TO_INT32(dest,src) \
(dest) = ( ((unsigned char)(src)[0] << 24) | ((unsigned char)(src)[1] << 16) |((unsigned char)(src)[2] << 8) | (unsigned char)(src)[3]); \
(src) += 4;

#define BUF_TO_STR(dest,len,src) \
{ \
  memcpy( (dest), (src), len); \
  (src) += (len); \
}                                                        


#if 0
#define DEBUG(xxx...) fprintf(stderr,xxx)
#else
#define DEBUG(xxx...)
#endif


struct _glyph {
    int32_t   height;		// Height of the character
    int32_t   width;		// Width of the character
    int32_t  *bitmap;	// Character data
};

struct _font {
    char 	 *display_name;	// In case we need it
    int32_t   height;		// Average height of the font
    int32_t   width;		// Average width of the font
    int32_t   glyph_max;	// Maximum number of the font can contain
    int32_t   glyph_num;	// Number of glyphs in the font
    uint8_t  *defined;	// Bit map of defined characters
    glyph_t  *glyphs;	// List of characters
};

glyph_t nbsp = {0, 1, NULL};

void glyph_add_bitmap(glyph_t *glyph, int bitmap)
{
    if(! glyph) {
        return ;
    }
    glyph->bitmap = (int *)realloc(glyph->bitmap, glyph->height + 1);
    glyph->bitmap[glyph->height++] = bitmap;
}

int *glyph_get_bitmap(glyph_t *glyph)
{
    if(! glyph) {
        return NULL;
    }

    return glyph->bitmap;
}

void glyph_set_height(glyph_t *glyph, int height)
{
    if(! glyph) {
        return;
    }

    glyph->height = height;
}

int glyph_get_height(glyph_t *glyph)
{
    if(! glyph) {
        return 0;
    }

    return glyph->height;
}

void glyph_set_width(glyph_t *glyph, int width)
{
    if(! glyph) {
        return;
    }

    glyph->width = width;
}

int glyph_get_width(glyph_t *glyph)
{
    if(! glyph) {
        return 0;
    }

    return glyph->width;
}

font_t *new_font()
{
    font_t *font = (font_t *)malloc(sizeof(font_t));

    font->display_name = NULL;
    font->height = -1;
    font->width = -1;
    font->glyph_max = -1;
    font->glyph_num = -1;
    font->defined = NULL;
    font->glyphs = NULL;

    return font;
}

/* Check the bitmap of defined glyphs */
int font_is_defined(font_t *font, int glyph_id)
{
    int hash, pos;

    hash = glyph_id / 8;
    pos = glyph_id % 8;
    
    if( hash > (font->glyph_max / 8) ) {
        return 0;
    }

    return font->defined[hash] & (1<<pos);
}

static int font_read_data(void *ptr, int size, int font_file)
{
    int bytes_read = 0;

    if(font_file < 0 || size <= 0) {
        return -1;
    }

    bytes_read = read(font_file, ptr, size);

    if(bytes_read != size) {
        DEBUG( "font_read_data: Expected %d got %d\n", size, bytes_read);
        return -1;
    }

    return bytes_read;
}

static void font_error_cleanup(font_t *font, int font_file)
{
    delete_font(font);
    if(font_file >= 0) {
        close(font_file);
    }
}

font_t *font_load(char *font_name)
{
    int font_file = -1;
    char *font_magic = NULL;
    font_t *font = NULL;
    int32_t data_length = 0;
    int32_t defined_bytes;
    int     i, row;

    DEBUG( "font_load\n");

    // Open the font file
    font_file = open(font_name, O_RDONLY);

    if(font_file < 0) {
        Dprintf(ERROR, "Unable to open font [%s]\n", font_name);
        return NULL;
    }

    // Read the magic number
    font_magic = (char *)calloc(1, 8);

    if( font_read_data(font_magic, 8, font_file) == -1 || strncmp(font_magic, MVPFONT_MAGIC, 8) ) {
        free(font_magic);
        Dprintf(ERROR, "[%s] is not an MVP font file\n", font_name);
        font_error_cleanup(font, font_file);
        return NULL;
    }
    free(font_magic);

    font = new_font();
    
    // Start reading the meta data
   
    if(font_read_data(&data_length, sizeof(data_length), font_file) == -1) {
        font_error_cleanup(font, font_file);
        return NULL;
    }

    data_length = ntohl(data_length);
    DEBUG( "Display Name Length: %u\n", data_length);

    font->display_name = (char *)calloc(1, data_length + 1);

    if(font_read_data(font->display_name, data_length, font_file) == -1) {
        font_error_cleanup(font, font_file);
        return NULL;
    }
    DEBUG( "Display Name: %s\n", font->display_name);

    if(font_read_data(&font->height, sizeof(font->height), font_file) == -1) {
        font_error_cleanup(font, font_file);
        return NULL;
    }
    font->height = ntohl(font->height);
    DEBUG( "Font Height: %u\n", font->height);

    if(font_read_data(&font->width, sizeof(font->width), font_file) == -1) {
        font_error_cleanup(font, font_file);
        return NULL;
    }
    font->width = ntohl(font->width);
    DEBUG( "Font Width: %u\n", font->width);

    if(font_read_data(&font->glyph_max, sizeof(font->glyph_max), font_file) == -1) {
        font_error_cleanup(font, font_file);
        return NULL;
    }
    font->glyph_max = ntohl(font->glyph_max);
    DEBUG( "Max Glyph: %u\n", font->glyph_max);

    if(font_read_data(&font->glyph_num, sizeof(font->glyph_num), font_file) == -1) {
        font_error_cleanup(font, font_file);
        return NULL;
    }
    font->glyph_num = ntohl(font->glyph_num);
    DEBUG( "Num Glyph: %u\n", font->glyph_num);

    /* Read the map of defined glyphs */
    if(font_read_data(&defined_bytes, sizeof(defined_bytes), font_file) == -1) {
        font_error_cleanup(font, font_file);
        return NULL;
    }
    defined_bytes = ntohl(defined_bytes);
    DEBUG( "Defined bytes: %u\n", defined_bytes);

    font->defined = (char *)calloc(1, defined_bytes);

    if(font_read_data(font->defined, defined_bytes, font_file) == -1) {
        font_error_cleanup(font, font_file);
        return NULL;
    }
    for(i = 0; i < defined_bytes; i++) {
        DEBUG( "defined[%d] = 0x%02x\n", i, font->defined[i]);
    }

    font->glyphs = (glyph_t *)malloc(font->glyph_max * sizeof(glyph_t));
    /* Read the glyphs */
    for(i=0; i < font->glyph_max; i++)
    {
        if(font_is_defined(font, i)) {
            if(font_read_data(&font->glyphs[i].width, sizeof(font->glyphs[i].height), font_file) == -1) {
                font_error_cleanup(font, font_file);
                return NULL;
            }
            font->glyphs[i].width = ntohl(font->glyphs[i].width);
            if(font_read_data(&font->glyphs[i].height, sizeof(font->glyphs[i].width), font_file) == -1) {
                font_error_cleanup(font, font_file);
                return NULL;
            }
            font->glyphs[i].height = ntohl(font->glyphs[i].height);
            DEBUG( "Font Height: %d\n", font->glyphs[i].height);
            DEBUG( "Font Width : %d\n", font->glyphs[i].width);

            font->glyphs[i].bitmap = (int *)malloc(font->glyphs[i].height * sizeof(int32_t));
            memset(font->glyphs[i].bitmap, 0, font->glyphs[i].height * sizeof(int32_t));

            for( row = 0; row < font->glyphs[i].height; row++ ) {
                DEBUG( "%d\t", row);

                if(font_read_data(&font->glyphs[i].bitmap[row], sizeof(int), font_file) == -1) {
                    font_error_cleanup(font, font_file);
                    return NULL;
                }
                font->glyphs[i].bitmap[row] = ntohl(font->glyphs[i].bitmap[row]);
                DEBUG( "0x%08x\n", font->glyphs[i].bitmap[row]);
            }
        } else {
            font->glyphs[i].width = 0;
            font->glyphs[i].height = 0;
            font->glyphs[i].bitmap = NULL;
        }
    }

    close(font_file);

    return font;
}

void delete_glyph(glyph_t *glyph)
{
    DEBUG( "delete_glyph\n");
    if(! glyph) {
        return;
    }

    if(glyph->bitmap) {
        free(glyph->bitmap);
    }

    free(glyph);
}

void delete_font(font_t *font)
{
    int i;

    DEBUG( "delete_font\n");
    if(font->display_name) {
        free(font->display_name);
        font->display_name = NULL;
    }
    if(font->defined) {
        free(font->defined);
        font->defined = NULL;
    }

    if(font->glyphs) {
        for(i=0; i < font->glyph_max ; i++) {
            if(font->glyphs[i].bitmap) {
                free(font->glyphs[i].bitmap);
                font->glyphs[i].bitmap = NULL;
            }
        }
    }

    free(font->glyphs);
    font->glyphs = NULL;

    free(font);
}


void font_set_name(font_t *font, char *display_name)
{
    DEBUG( "font_set_name\n");
    if(font) {
        if(font->display_name) {
            free(font->display_name);
        }

        font->display_name = (char *)strdup(display_name);
    }
}

int font_get_height(font_t *font)
{
    DEBUG( "font_get_height\n");
    if(!font) {
        return -1;
    }

    return font->height;
}

int font_get_width(font_t *font)
{
    DEBUG( "font_get_width\n");
    if(!font) {
        return -1;
    }

    return font->width;
}

int font_get_glyph_max(font_t *font)
{
    DEBUG( "font_get_glyph_max\n");
    if(!font) {
        return -1;
    }
    return font->glyph_max;
}

int font_get_glyph_num(font_t *font)
{
    DEBUG( "font_get_glyph_num\n");
    if(!font) {
        return -1;
    }

    return font->glyph_num;
}

glyph_t *font_get_glyph(font_t *font, int glyph_id)
{
    DEBUG( "font_get_glyph\n");
    if (!font) {
        return NULL;
    }

    if (! font_is_defined(font, glyph_id)) {
        return NULL;
    }

    return &font->glyphs[glyph_id];
}


font_t *font_read(char *data)
{
    font_t *font = NULL;
    int data_length = 0;
    int i, row, defined_bytes;

    DEBUG( "font_read\n");
   
    
    if ( strncmp(data,MVPFONT_MAGIC,8) ) {
        Dprintf(ERROR, "Buffer is not an MVP font buffer\n");
        return NULL;
    }
    data += 8;                  

    font = new_font();
    
    // Start reading the meta data

    BUF_TO_INT32(data_length,data);   
    DEBUG( "Display Name Length: %u\n", data_length);

    font->display_name = malloc(data_length+1);
    BUF_TO_STR(font->display_name, data_length, data);   
    font->display_name[data_length] = 0;
    DEBUG( "Display Name: %s\n", font->display_name);


    BUF_TO_INT32(font->height,data);   
    DEBUG( "Font Height: %u\n", font->height);

    BUF_TO_INT32(font->width,data);
    DEBUG( "Font Width: %u\n", font->width);

    BUF_TO_INT32(font->glyph_max,data);   
    DEBUG( "Max Glyph: %u\n", font->glyph_max);

    BUF_TO_INT32(font->glyph_num,data);
    DEBUG( "Num Glyph: %u\n", font->glyph_num);

    BUF_TO_INT32(defined_bytes,data);    
    DEBUG( "Defined bytes: %u\n", defined_bytes);

    font->defined = (char *)calloc(1, defined_bytes);
    BUF_TO_STR(font->defined, defined_bytes, data);
 
    for(i = 0; i < defined_bytes; i++) {
        DEBUG( "defined[%d] = 0x%02x\n", i, font->defined[i]);
    }


    font->glyphs = (glyph_t *)malloc(font->glyph_max * sizeof(glyph_t));

    /* Read the glyphs */
    for(i=0; i < font->glyph_max; i++) {
        if(font_is_defined(font, i)) {
            BUF_TO_INT32(font->glyphs[i].width, data);
            BUF_TO_INT32(font->glyphs[i].height, data);

            DEBUG( "Font Height: %d\n", font->glyphs[i].height);
            DEBUG( "Font Width : %d\n", font->glyphs[i].width);

            font->glyphs[i].bitmap = (int *)malloc(font->glyphs[i].height * sizeof(int));
            memset(font->glyphs[i].bitmap, 0, font->glyphs[i].height * sizeof(int));

            for(row=0;row < font->height; row++) {
                DEBUG( "%d\t", row);

                memcpy(&font->glyphs[i].bitmap[row], data, sizeof(uint32_t));
                font->glyphs[i].bitmap[row] = htonl(font->glyphs[i].bitmap[row]);
                data += sizeof(uint32_t);
              
                DEBUG( "0x%08x\n", font->glyphs[i].bitmap[row]);
            }
        } else {
            font->glyphs[i].width = 0;
            font->glyphs[i].height = 0;
            font->glyphs[i].bitmap = NULL;
        }
    }

    return font;
}
