/****************************************************************************
*
*    The MIT License (MIT)
*
*    Copyright 2020 NXP
*    All Rights Reserved.
*
*    Permission is hereby granted, free of charge, to any person obtaining
*    a copy of this software and associated documentation files (the
*    'Software'), to deal in the Software without restriction, including
*    without limitation the rights to use, copy, modify, merge, publish,
*    distribute, sub license, and/or sell copies of the Software, and to
*    permit persons to whom the Software is furnished to do so, subject
*    to the following conditions:
*
*    The above copyright notice and this permission notice (including the
*    next paragraph) shall be included in all copies or substantial
*    portions of the Software.
*
*    THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
*    EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
*    MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT.
*    IN NO EVENT SHALL VIVANTE AND/OR ITS SUPPLIERS BE LIABLE FOR ANY
*    CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
*    TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
*    SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*
*****************************************************************************/

/** Include Files */
#include <mcufont.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include "FreeRTOS.h"
#include "vg_lite.h"
#include "buf_reader.h"
#include "vg_lite_text.h"
#include "vft_draw.h"
#include "vft_debug.h"

/** Macros */
#define __COUNTOF(x) (sizeof(x)/sizeof(x[0]))
#define MAX_SYSTEM_FONTS (8)

#define RCD_ALLOC(x) _mem_allocate(x)
#define RCD_FREE(x) _mem_free(x)

#ifdef ENABLE_DEBUG_TRACE
static int g_id;

#define DBG_ID() (g_id)
#define DBG_INC_ID() (g_id++)
#define DBG_SET_ID(x) g_id = x
#else

#define DBG_ID() (0)
#define DBG_INC_ID() (0)
#define DBG_SET_ID(x) 
#endif

#if APP_ENABLE_SDCARD
#define sprintf_s snprintf
#endif

/** Data structures */
typedef struct font_height_list {
    const char font_height;
    const char ref_font_height;
    const char ref_font_scale;
} font_height_list_t;

typedef struct font_info_internal {
  /* Variable shared between user and driver */
  vg_lite_font_params_t font_params;
  /* any additional variables internal to driver */
  int valid;
  /* Internal loaded raster font */
  struct mf_font_s *_raster_font;
  /* Internal loaded vector font */
  font_face_desc_t *_vector_font;
}font_info_internal_t;

/** Internal or external API prototypes */

/** Globals */
static font_info_internal_t s_device_fonts[MAX_SYSTEM_FONTS];

/** Local function prototypes */
int read_rle_font_header(bufferred_reader_t *f, struct mf_font_s* font);
int read_rle_font_from_buffer(char *buf, int size, struct mf_font_s* font);
int load_raster_font(char *data, int data_len, struct mf_font_s** font);

int read_8b(bufferred_reader_t *f, uint8_t* pdata);
int read_16b(bufferred_reader_t *f, uint16_t* pword);
int read_32b(bufferred_reader_t *f, uint32_t* pword);
int read_16b_blob(bufferred_reader_t *f, uint16_t** ary, uint32_t* ary_len);
int read_8b_blob(bufferred_reader_t *f, uint8_t** ary, uint32_t* ary_len);
int free_rle_font_memory(struct mf_font_s** font);
vg_lite_error_t vg_lite_free_font_memory(vg_lite_font_t font);
vg_lite_error_t vg_lite_load_font_data(vg_lite_font_t font, int font_height);

/** Externs if any */

/** Code section */
vg_lite_font_t vg_lite_find_font(
    char *font_name, 
    eFontWeight_t  font_weight,
    eFontStretch_t font_stretch,
    eFontStyle_t   font_style,
    int font_height);

vg_lite_error_t vg_lite_register_font(vg_lite_font_t *font, 
    vg_lite_font_params_t *params)
{
  int i;
  int free_entry = VG_LITE_INVALID_FONT;

  if (font != NULL)
    *font = VG_LITE_INVALID_FONT;

  /* Check if font is already registered */
  for (i=0; i<MAX_SYSTEM_FONTS; i++) {
    /* Is entry is valid ? */
    if ( s_device_fonts[i].valid == 0 ) {
        /* Grab free entry in list for new font */
        if (free_entry == -1 ) {
            free_entry = i;
        }
    } else if (s_device_fonts[i].valid == 1 ) {
      /* Does font.name matches */
      if ( strncmp(s_device_fonts[i].font_params.name,
                  params->name,
                  strlen(s_device_fonts[i].font_params.name)) == 0)
      {
            return VG_LITE_ALREADY_EXISTS;
      }
    } else {
      printf("Corrupt font table\n");
      return VG_LITE_INVALID_ARGUMENT;
    }
  }
  
  /* Check if list is completely full or not */
  if ( i == MAX_SYSTEM_FONTS && free_entry == VG_LITE_INVALID_FONT) {
      /* Font List descriptor exhausted */
      return VG_LITE_OUT_OF_RESOURCES;
  } else {
      /* Add new font in global table */
      memcpy(&s_device_fonts[free_entry].font_params, params, sizeof(vg_lite_font_params_t));
      s_device_fonts[free_entry].valid = 1;
/* 
   Loading font here leads to low run-time memory, we may need to characterize memory usage 
   e.g. pure path test don't require font, eventhrough application registers them
*/
#if 0
      error = vg_lite_load_font_data(free_entry, params->font_height);
      if ( error != 0 ) {
          s_device_fonts[free_entry].valid = 0;
          return error;
      }
#endif
  }

  if (font != NULL)
    *font = free_entry;

  return VG_LITE_SUCCESS;
}

int vg_lite_is_font_valid(vg_lite_font_t font)
{
    if (font < MAX_SYSTEM_FONTS) {
        if (s_device_fonts[font].valid == 1)
            return 0;
    }
    return VG_LITE_INVALID_ARGUMENT;
}

int vg_lite_is_vector_font(vg_lite_font_t font)
{
    if (font < MAX_SYSTEM_FONTS) {
        if (s_device_fonts[font].valid == 1)
            return (s_device_fonts[font].font_params.font_type ==
                eFontTypeVector);
        else
            return 0;
    }
    return VG_LITE_INVALID_ARGUMENT;;
}

vg_lite_error_t vg_lite_unregister_font(vg_lite_font_t font)
{
    if (vg_lite_is_font_valid(font) != 0 ) {
        /* Font not found */
        return VG_LITE_INVALID_ARGUMENT;;
    }

    vg_lite_free_font_memory(font);
    s_device_fonts[font].valid = 0;
    return VG_LITE_SUCCESS;
}

char *vg_lite_get_font_name(vg_lite_font_t font)
{
  if ( vg_lite_is_font_valid(font) != 0 ) {
        return NULL;
  }
  
  return (char *)s_device_fonts[font].font_params.name;
}

vg_lite_error_t vg_lite_free_font_memory(vg_lite_font_t font)
{
  if ( vg_lite_is_font_valid(font) != 0 ) {
        /* Font not found */
        return VG_LITE_INVALID_ARGUMENT;
  }
  switch (s_device_fonts[font].font_params.font_type) {
    case eFontTypeVector:
      if ( s_device_fonts[font]._vector_font != NULL ) {
          vft_unload(s_device_fonts[font]._vector_font);
          s_device_fonts[font]._vector_font = NULL;
      }
      break;
    case eFontTypeRaster:
      if ( s_device_fonts[font]._raster_font != NULL ) {
          free_rle_font_memory(&s_device_fonts[font]._raster_font);
          s_device_fonts[font]._raster_font = NULL;
      }
      break;
  }
  
  return VG_LITE_SUCCESS;
}

font_face_desc_t *_vg_lite_get_vector_font(vg_lite_font_t font)
{
  if ( vg_lite_is_font_valid(font) != 0 ) {
        return NULL;
  }

  return s_device_fonts[font]._vector_font;
}

struct mf_font_s *_vg_lite_get_raster_font(vg_lite_font_t font)
{
  if ( vg_lite_is_font_valid(font) != 0 ) {
        return NULL;
  }

  return s_device_fonts[font]._raster_font;
}

vg_lite_error_t vg_lite_load_font_data(vg_lite_font_t font, int font_height)
{
  if ( vg_lite_is_font_valid(font) != 0 ) {
        /* Font not found */
        return VG_LITE_INVALID_ARGUMENT;
  }

  switch (s_device_fonts[font].font_params.font_type) {
    case eFontTypeVector:
      if (s_device_fonts[font]._vector_font == NULL ) {
          //printf("Loading vector font : [%s]\n",
          //       s_device_fonts[font].font_params.name);
          s_device_fonts[font]._vector_font = 
          vft_load_from_buffer(
                 s_device_fonts[font].font_params.data,
                 s_device_fonts[font].font_params.data_len);
          if ( s_device_fonts[font]._vector_font == NULL )  {
                return VG_LITE_INVALID_ARGUMENT;
          }
      }
      return VG_LITE_SUCCESS;
    case eFontTypeRaster:
      if (s_device_fonts[font]._raster_font == NULL ) {
          //printf("Loading raster font : [%s]\n",
          //       s_device_fonts[font].font_params.name);
          /* Raster fonts height should match */
          if ( font_height == s_device_fonts[font].font_params.font_height &&
              load_raster_font(
                 s_device_fonts[font].font_params.data,
                 s_device_fonts[font].font_params.data_len,
                 &s_device_fonts[font]._raster_font) != 0)
          {
              return VG_LITE_INVALID_ARGUMENT;
          }
      }
      return VG_LITE_SUCCESS;
  }
  return VG_LITE_INVALID_ARGUMENT;
}


vg_lite_font_t vg_lite_find_font(
    char *font_name_list, 
    eFontWeight_t  font_weight,
    eFontStretch_t font_stretch,
    eFontStyle_t   font_style,
    int font_height)
{
    int i;
    char tmp_data;
    int font_found = 0;
    int end_pos = 0; /* End position of font name */
    char *font_name;
    /*
    printf("Font params: [%s],%d, %d, %d, %d\n",
           font_name_list,
           font_weight,
           font_stretch,
           font_style,
           font_height);
    */
    
    /* While probing font, if required font is found, then
       only keep that name in supplied font list */
    
    /* Split and extract font name one-by-one */
    font_name = font_name_list;
    while (font_found == 0 &&
           font_name[end_pos] != '\0')
    {
        tmp_data = '\0';
        /* Scan for seperator */
        while (font_name[end_pos] != '\0') {
            if (font_name[end_pos] == ',' || font_name[end_pos] == ' ' ||
              font_name[end_pos] == '\t')
            {
                tmp_data = font_name[end_pos];
                font_name[end_pos] = '\0';
                break;
            }
            end_pos++;
        }       

        /* Search for exact font-name match */
        for (i=0; i<__COUNTOF(s_device_fonts); i++) {
           if (s_device_fonts[i].valid == 0)
               continue;

           /* For vector font only compare name */
           if (s_device_fonts[i].font_params.font_type == eFontTypeVector ) {
                if ( strlen(font_name) > 0 &&
                    strcmp(font_name, 
                    s_device_fonts[i].font_params.name) == 0)
                {
                   font_name[end_pos] = tmp_data; /* Restor delimeter */
                   return i;
                }
           } else {
                /* For raster font compare all properties */
                if (s_device_fonts[i].font_params.font_weight == font_weight &&
                    s_device_fonts[i].font_params.font_stretch == font_stretch &&
                    s_device_fonts[i].font_params.font_style == font_style ) 
                {
                    if ( strlen(font_name) > 0 &&
                        strcmp(font_name, 
                        s_device_fonts[i].font_params.name) == 0)
                    {
                       font_name[end_pos] = tmp_data; /* Restor delimeter */

                       if (s_device_fonts[i].font_params.font_height == font_height)
                       {
                            return i;
                       }
                        /* Update font_name_list to improve future searches */
                        strncpy(font_name_list, font_name, strlen(font_name)+1);
                    }
                }
            }
        }
        /* Continue searching for other font entries */
        font_name[end_pos] = tmp_data;
        font_name += (end_pos + 1);
        end_pos = 0;
    }
    printf("WARNING: [%s] Font not found\r\n",font_name_list);

    return VG_LITE_INVALID_FONT;
}

void vg_lite_text_init(void)
{
    static int font_table_ready = 0;

    if (font_table_ready)
        return;

    /* Initialize font table */
    memset(s_device_fonts, 0, MAX_SYSTEM_FONTS * sizeof(font_info_internal_t));
    font_table_ready = 1;
}

/* Read-Write 8-bit unsigned int data */
int read_8b(bufferred_reader_t * f, uint8_t* pdata)
{
    bufferred_fread(pdata, 1, 1, f);
    TRACE_DBG(("%d) 8b: fp=%08x %d %d\r\n", DBG_INC_ID(),
               bufferred_ftell(f) - 1, 1, *pdata));
    return 1;
}

/* Read-Write 16-bit unsigned int data */
int read_16b(bufferred_reader_t * f, uint16_t* pword)
{
    uint8_t buf[4];
    uint16_t word;

    bufferred_fread(buf, 2, 1, f);
    word = 0;   word += buf[1];
    word <<= 8; word += buf[0];

    TRACE_DBG(("%d) 16b: fp=%08x %d %d\r\n", DBG_INC_ID(),
               bufferred_ftell(f)-2, 2, word));
    *pword = word;
    return 2;
}

/* Read-write 32-bit unsigned int data */
int read_32b(bufferred_reader_t * f, uint32_t* pword)
{
    uint8_t buf[4];
    uint32_t word;

    bufferred_fread(buf, 4, 1, f);
    word = 0;   word += buf[3];
    word <<= 8; word += buf[2];
    word <<= 8; word += buf[1];
    word <<= 8; word += buf[0];

    *pword = word;
    TRACE_DBG(("%d) 32b: fp=%08x %d %d\r\n", DBG_INC_ID(),
               bufferred_ftell(f) - 4, 4, 
               word));
    return 4;
}

int read_16b_blob(bufferred_reader_t * f, uint16_t** ary, uint32_t* ary_len)
{
    uint16_t blob_len = 0;

    *ary_len = 0;
    bufferred_fread(&blob_len, 1, sizeof(uint16_t), f);
    *ary = (uint16_t *)RCD_ALLOC(blob_len * sizeof(uint16_t)); /* Malloc must be aligned on 2 byte boundary */
    if (*ary == NULL) {
        TRACE_ERR(("ERROR: malloc failed\n"));;
        return VG_LITE_OUT_OF_MEMORY;
    }
    if ((((unsigned long)*ary) & 0x1) != 0) {
        TRACE_ERR(("ERROR: malloc pointer not 2 byte aligned\n"));;
        return VG_LITE_NOT_ALIGNED;
    }
    TRACE_DBG(("%d ) 16b_blob: fp=%08x %d\r\n", DBG_INC_ID(), bufferred_ftell(f)-2, 
               (int)blob_len));
    bufferred_fread(*ary, 2, blob_len, f);
    *ary_len = blob_len * 2;

    return blob_len + 2; /* Actual bytes read from file */
}

int read_8b_blob(bufferred_reader_t * f, uint8_t** ary, uint32_t *ary_len)
{
    uint16_t blob_len = 0;
    uint8_t* p_tmp = (uint8_t *)NULL;
    bufferred_fread(&blob_len, 1, sizeof(uint16_t), f);
    //TRACE_DBG(("%d) 8b_blob: %d\r\n", DBG_INC_ID(), blob_len));
    p_tmp = (uint8_t *)RCD_ALLOC(blob_len+1);
    if (p_tmp == NULL) {
        TRACE_ERR(("ERROR: malloc failed\n"));;
        return VG_LITE_OUT_OF_MEMORY;
    }
    TRACE_DBG(("%d ) 8b_blob: fp=%08x %d\r\n", DBG_INC_ID(), bufferred_ftell(f)-2,
               (int)blob_len));
    bufferred_fread(p_tmp, 1, blob_len, f);
    p_tmp[blob_len] = 0;
    *ary = p_tmp;

    return blob_len + 2; /* Actual bytes read from file */
}

#define EXIT_IF_NEGATIVE(param) if ((ret = param) < 0) return ret;

int read_rle_font_header(bufferred_reader_t * f, struct mf_font_s* font)
{
    int raw_header_offset = 0;
    struct mf_rlefont_s* mfont = (struct mf_rlefont_s*)font;
    uint32_t size = 0;
    int ret = 0;

    TRACE_DBG(("** %s\r\n", __FUNCTION__));
    DBG_SET_ID(0);

    EXIT_IF_NEGATIVE(read_8b_blob(f, (uint8_t **)&font->full_name, &size));
    raw_header_offset += ret; /* font.full_name */

    EXIT_IF_NEGATIVE(read_8b_blob(f, (uint8_t **)&font->short_name, &size));
    raw_header_offset += ret; /* font.short_name */

    EXIT_IF_NEGATIVE(read_8b(f, &font->width)); 
    raw_header_offset += 1;                       /* font.width */

    EXIT_IF_NEGATIVE(read_8b(f, &font->height));
    raw_header_offset += 1;                       /* font.height */

    EXIT_IF_NEGATIVE(read_8b(f, &font->min_x_advance));
    raw_header_offset += 1;                       /* font.min_x_advance */

    EXIT_IF_NEGATIVE(read_8b(f, &font->max_x_advance));
    raw_header_offset += 1;                       /* font.max_x_advance */

    EXIT_IF_NEGATIVE(read_8b(f, (uint8_t *)&font->baseline_x));
    raw_header_offset += 1;                       /* font.baseline_x */

    EXIT_IF_NEGATIVE(read_8b(f, &font->baseline_y));
    raw_header_offset += 1;                       /* font.baseline_y */

    EXIT_IF_NEGATIVE(read_8b(f, &font->line_height));
    raw_header_offset += 1;                       /* font.line_height */

    EXIT_IF_NEGATIVE(read_8b(f, &font->flags));
    raw_header_offset += 1;                       /* font.flags */

    EXIT_IF_NEGATIVE(read_16b(f, &font->fallback_character));
    raw_header_offset += 2;                       /* font.fallback_character */

    EXIT_IF_NEGATIVE(read_8b(f, &(mfont->version)));
    raw_header_offset += 1;                           /* mf_rlefont_s.version */

    /* Other mf_rlefont_s variables */
    uint16_t value = 0;
    EXIT_IF_NEGATIVE(read_16b(f, &value)); //&mfont->dictionary_data_size));
    mfont->dictionary_data_size = value;
    raw_header_offset += 2;                           /* mf_rlefont_s.dictionary_data_size */

    EXIT_IF_NEGATIVE(read_32b(f, &mfont->dictionary_data_fp_offset));
    raw_header_offset += 4;                           /* mf_rlefont_s.dictionary_data_fp_offset */

    value = 0;
    EXIT_IF_NEGATIVE(read_16b(f, &value)); //mfont->dictionary_offsets_size));
    mfont->dictionary_offsets_size = value;    
    raw_header_offset += 2;                           /* mf_rlefont_s.dictionary_data_size */

    EXIT_IF_NEGATIVE(read_32b(f, &mfont->dictionary_offsets_fp_offset));
    raw_header_offset += 4;                           /* mf_rlefont_s.dictionary_data_fp_offset */

    EXIT_IF_NEGATIVE(read_8b(f, &mfont->rle_entry_count));
    raw_header_offset += 1;                           /* mf_rlefont_s.rle_entry_count */

    EXIT_IF_NEGATIVE(read_8b(f, &mfont->dict_entry_count));
    raw_header_offset += 1;                           /* mf_rlefont_s.dict_entry_count */

    EXIT_IF_NEGATIVE(read_8b(f, &mfont->char_range_count));
    raw_header_offset += 1;                           /* mf_rlefont_s.char_range_count */

    mfont->char_ranges = (struct mf_rlefont_char_range_s *)RCD_ALLOC(sizeof(struct mf_rlefont_char_range_s)* mfont->char_range_count);
    memset(mfont->char_ranges, 0, sizeof(struct mf_rlefont_char_range_s) * mfont->char_range_count);

    /* Skip size of ranges */
    for (int r = 0; r < mfont->char_range_count; r++) {
        EXIT_IF_NEGATIVE(read_16b(f, &mfont->char_ranges[r].first_char));
        raw_header_offset += 2;                           /* mf_rlefont_s.char_ranges[r].first_char */

        EXIT_IF_NEGATIVE(read_16b(f, &mfont->char_ranges[r].char_count));
        raw_header_offset += 2;                           /* mf_rlefont_s.char_ranges[r].char_count */

        EXIT_IF_NEGATIVE(read_32b(f, &mfont->char_ranges[r].glyph_offsets_fp_offset));
        raw_header_offset += 4;                           /* mf_rlefont_s.char_ranges[r].glyph_offsets_fp_offset */

        EXIT_IF_NEGATIVE(read_32b(f, &mfont->char_ranges[r].glyph_offsets_size));
        raw_header_offset += 4;                           /* mf_rlefont_s.char_ranges[r].glyph_offsets_size */

        EXIT_IF_NEGATIVE(read_32b(f, &mfont->char_ranges[r].glyph_data_fp_offset));
        raw_header_offset += 4;                           /* mf_rlefont_s.char_ranges[r].glyph_offsets_fp_offset */

        EXIT_IF_NEGATIVE(read_32b(f, &mfont->char_ranges[r].glyph_data_size));
        raw_header_offset += 4;                           /* mf_rlefont_s.char_ranges[r].glyph_offsets_size */
    }
    return raw_header_offset;
}

/* Writes a BMP file. The data is assumed to be 8-bit grayscale. */
int read_rle_font_from_buffer(char *buff, int size, struct mf_font_s* font)
{
    struct mf_rlefont_s* mfont = (struct mf_rlefont_s*)font;
    bufferred_reader_t f_obj;
    bufferred_reader_t * f = &f_obj;
    int raw_header_offset = 0;
    int fp_offset;
    int ret;

    DBG_SET_ID(0);

    /* No need to dynamically allocate small descriptor */
    if ( bufferred_fopen(f, buff, size) < 0 ) {
        /* Font file open failed */
        return VG_LITE_INVALID_ARGUMENT;
    }

    raw_header_offset = read_rle_font_header(f, font);
    if (mfont->dictionary_data_fp_offset != raw_header_offset) {
        TRACE_ERR(("ERROR: dictonary offset is different"));
    }
    TRACE_DBG(("** %s\r\n", __FUNCTION__));
    DBG_SET_ID(0);

    /* Skip header portion */
    bufferred_fseek(f, raw_header_offset, SEEK_SET);
    /* Write dictionary entries */
    fp_offset = raw_header_offset;
    TRACE_DBG(("dictionary_data_fp_offset=%08x %08x\r\n",
        mfont->dictionary_data_fp_offset, bufferred_ftell(f)));
    //mfont->dictionary_data_fp_offset = fp_offset;
    EXIT_IF_NEGATIVE(read_8b_blob(f, &mfont->dictionary_data,
        &mfont->dictionary_data_size));
    fp_offset += mfont->dictionary_data_size + 2;

    //mfont->dictionary_offsets_fp_offset = fp_offset;
    TRACE_DBG(("dictionary_offsets_fp_offset=%08x %08x\r\n",
        mfont->dictionary_offsets_fp_offset, bufferred_ftell(f)));
    EXIT_IF_NEGATIVE(read_16b_blob(f, &mfont->dictionary_offsets,
        &mfont->dictionary_offsets_size));
    fp_offset += mfont->dictionary_offsets_size + 2;

    /* Write range entries */
    for (int r = 0; r < mfont->char_range_count; r++) {
        //mfont->char_ranges[r].glyph_offsets_fp_offset = fp_offset;
        TRACE_DBG(("mfont->char_ranges[%d].glyph_offsets=%08x %08x\r\n", r, 
            mfont->char_ranges[r].glyph_offsets_fp_offset, bufferred_ftell(f)));
        EXIT_IF_NEGATIVE(read_16b_blob(f, &mfont->char_ranges[r].glyph_offsets,
            &mfont->char_ranges[r].glyph_offsets_size));
        fp_offset += mfont->char_ranges[r].glyph_offsets_size + 2;

        //mfont->char_ranges[r].glyph_data_fp_offset = fp_offset;
        TRACE_DBG(("mfont->char_ranges[%d].glyph_data_fp_offset=%08x %08x\r\n",
            r, 
            mfont->char_ranges[r].glyph_data_fp_offset, bufferred_ftell(f)));
        EXIT_IF_NEGATIVE(read_8b_blob(f, &mfont->char_ranges[r].glyph_data,\
            &mfont->char_ranges[r].glyph_data_size));
        fp_offset += mfont->char_ranges[r].glyph_data_size + 2;
    }

    bufferred_fclose(f);
	return 0;
}

int load_raster_font(char *data, int data_len, struct mf_font_s** font)
{
    int ret;

    /* Allocate font memory */
    *font = (struct mf_font_s*)RCD_ALLOC(sizeof(struct mf_rlefont_s));
    if (*font == NULL) {
        return VG_LITE_OUT_OF_MEMORY;
    }
    memset(*font, 0, sizeof(struct mf_rlefont_s));

    /* Load font from file */
    ret = read_rle_font_from_buffer(data,
                                    data_len, *font);
    if (ret < 0) {
        return ret;
    }

    /* Update generic char width pointers of mculib */
    uint8_t mf_rlefont_character_width(const struct mf_font_s* font,
        uint16_t character);
    uint8_t mf_rlefont_render_character(const struct mf_font_s* font,
        int16_t x0, int16_t y0,
        uint16_t character,
        mf_pixel_callback_t callback,
        void* state);

    (*font)->character_width = &mf_rlefont_character_width;
    (*font)->render_character = &mf_rlefont_render_character;


    return 0;
}

int free_rle_font_memory(struct mf_font_s** font)
{
    struct mf_rlefont_s* mfont = (struct mf_rlefont_s*)(*font);

    RCD_FREE(mfont->font.full_name);
    RCD_FREE(mfont->font.short_name);
    RCD_FREE(mfont->dictionary_data);
    RCD_FREE(mfont->dictionary_offsets);
    for (int r = 0; r < mfont->char_range_count; r++) {
        RCD_FREE(mfont->char_ranges[r].glyph_offsets);
        RCD_FREE(mfont->char_ranges[r].glyph_data);
    }
    #ifdef DEBUG_RESET_DATASTRUCTURE_ON_FREE
    memset(mfont->char_ranges);
    #endif
    RCD_FREE(mfont->char_ranges);

    #ifdef DEBUG_RESET_DATASTRUCTURE_ON_FREE
    memset(mfont);
    #endif
    RCD_FREE(mfont);

    *font = NULL;
    return 0;
}

void vg_lite_unload_font_data(void)
{
    for(int i=0; i<MAX_SYSTEM_FONTS; i++)
    {
        vg_lite_free_font_memory(i);
    }

    return;
}
