/****************************************************************************
*
*    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 <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "vg_lite.h"
#include "vg_lite_text.h"
#include "vft_draw.h"
#include "vft_debug.h"
#include "FreeRTOS.h"

/** Macros */
#define VFT_ALLOC(x) _mem_allocate(x)
#define VFT_FREE(x) _mem_free(x)
#define READ_BIN_FIELD(x) memcpy(&g->x, (buf + offset), sizeof(g->x));  offset += sizeof(g->x)
#define READ_BIN_FIELD_STR(x) READ_BIN_FIELD(x)
#define READ_BIN_FIELD_UINT16(x) READ_BIN_FIELD(x)
#define READ_BIN_FIELD_UINT32(x) READ_BIN_FIELD(x)
#define READ_BIN_FIELD_FLOAT(x) READ_BIN_FIELD(x)
#define READ_BIN_FIELD_DUMMY_POINTER(x) offset += 4;
#define GLYPH_CACHE_SIZE 16
#define ENABLE_TEXT_WRAP 0
#define HALT_ALLOCATOR_ERROR 1

/** Data structures */
typedef struct glyph_cache_desc {
    vg_lite_path_t *h_path;
    glyph_desc_t *g;
    uint32_t use_count;
}glyph_cache_desc_t;

/** Internal or external API prototypes */

/** Globals */
static int g_glyph_cache_init_done = 0;
static glyph_cache_desc_t g_glyph_cache[GLYPH_CACHE_SIZE];
int g_total_bytes = 0;

/** Externs if any */

/* Internal API, not published to user */
font_face_desc_t *_vg_lite_get_vector_font(vg_lite_font_t font_idx);
void matrix_multiply(vg_lite_matrix_t * matrix, vg_lite_matrix_t *mult);

void *_mem_allocate(int size)
{
  char *buf = NULL;
  
  if ( size == 0 ) {
    printf("ERROR: HALT: Why allocating %d bytes\n", size);
#if HALT_ALLOCATOR_ERROR
    while(1);
#else
    return NULL;
#endif
  }
  
  buf = pvPortMalloc(size);
  if (buf == NULL) {
    printf("ERROR: HALT: allocating %d bytes \"system out of memory\"\n", size);
#if HALT_ALLOCATOR_ERROR
    while(1);
#else
    return NULL;
#endif
  }
  g_total_bytes += size;
  return buf;
}

void _mem_free(void *buf)
{
    vPortFree(buf);
}

/** GLYPH CACHING Code */
void glyph_cache_init(void)
{
    if ( g_glyph_cache_init_done == 0 ) {
        memset(g_glyph_cache,0,sizeof(g_glyph_cache));
        g_glyph_cache_init_done = 1;
    }
}

void glyph_cache_free(void)
{
    int i;
    for (i=0; i<GLYPH_CACHE_SIZE; i++) {
        if ( g_glyph_cache[i].h_path != NULL ) {
            /* For non-mapped path this resetting is sufficient */
            vg_lite_clear_path(g_glyph_cache[i].h_path);

            /* Release path handle */
            VFT_FREE(g_glyph_cache[i].h_path);

            /* Reset pointer */
            g_glyph_cache[i].h_path = NULL;
        }
    }

    /* Next time font init will be required */
    g_glyph_cache_init_done = 0;
}

vg_lite_path_t *vft_cache_lookup(glyph_desc_t *g)
{
    int i;
    int unused_idx = 0;

    /* Not present caching code does not handle multiple font-face */
    glyph_cache_init();

    /* Check if path object for given glyph exists */
    for (i=0; i<GLYPH_CACHE_SIZE; i++) {
        if ( g_glyph_cache[i].g != NULL && g->unicode == 
            g_glyph_cache[i].g->unicode )
        {
            g_glyph_cache[i].use_count++;
            return g_glyph_cache[i].h_path;
        }
    }

    /* Find least used descriptor */
    unused_idx = 0;
    for (i=1; i<GLYPH_CACHE_SIZE; i++) {
      if ( g_glyph_cache[i].use_count < 
          g_glyph_cache[unused_idx].use_count ) {
            unused_idx = i;
      }
    }
    /* Re-cycle descriptor */
    if ( g_glyph_cache[unused_idx].h_path != NULL ) {
        /* For non-mapped path this resetting is sufficient */
        vg_lite_clear_path(g_glyph_cache[unused_idx].h_path);
    } else {
        /* Allocate new path object */
        g_glyph_cache[unused_idx].h_path = 
            (vg_lite_path_t *)VFT_ALLOC(sizeof(vg_lite_path_t));
    }
    /* Allocate new path */
    vg_lite_init_path(g_glyph_cache[unused_idx].h_path,
                      VG_LITE_FP32, VG_LITE_HIGH,
                      g->path.num_draw_cmds*4,
                      g->path.draw_cmds,
                      g->path.bounds[0],
                      g->path.bounds[1],
                      g->path.bounds[2],
                      g->path.bounds[3]);
    g_glyph_cache[unused_idx].g = g;
    g_glyph_cache[unused_idx].use_count = 1;
    return g_glyph_cache[unused_idx].h_path;
}

/** Render text using vector fonts */
int vg_lite_vtf_draw_text(vg_lite_buffer_t *rt, int x, int y,
                      vg_lite_blend_t blend, 
                      vg_lite_font_t font, 
                      vg_lite_matrix_t *matrix,
                      vg_lite_font_attributes_t  * attributes,
                      char *text)
{
    font_face_desc_t *font_face;
    glyph_desc_t* g1 = NULL;
    glyph_desc_t* g2;
    int error = 0;
    float font_scale = 1.0;
    int text_wrap = 0;
    
    font_face = (font_face_desc_t *)_vg_lite_get_vector_font(font);
    
    attributes->last_dx = 0;
    font_scale = ((1.0*attributes->font_height)/font_face->units_per_em);

    vg_lite_matrix_t mat;

    vg_lite_fill_t    fill_rule = VG_LITE_FILL_NON_ZERO;
    vg_lite_color_t   color = attributes->text_color;

    /* Compute size of tex in pixels 
     * For center alignment adjust x position
     * Present parser has bug in encoding alignment value,
     * Once it is fixed following code will align text in center
     */
    if ( attributes->alignment == eTextAlignCenter ||
         attributes->alignment == eTextAlignRight ) {
        char *t2 = text;
        int dx = 0;
        uint16_t ug2; /* Unicode glyph */
        int kx;

        /* Case of center alignement */
        while (*t2 != '\0') {
            ug2 = *t2;
            kx = 0;
            g2 = vft_find_glyph(font_face, ug2);
            if (g1 != NULL && g2 != NULL) {
                kx = vft_glyph_distance(g1, g2);
            }

            dx += ((g2->horiz_adv_x + kx )* font_scale);
            t2++;
        }

        if ( attributes->alignment == eTextAlignCenter) {
            x -= (dx/2);
        } else if ( attributes->alignment == eTextAlignRight) {
            x -= (dx);
        }
    }

    /* Compute pixels that will cover this vector path */
    while (*text != '\0') {
        uint16_t ug2; /* Unicode glyph */
        int kx;
        
        if (text_wrap == 0) {
            vg_lite_identity(&mat);
            matrix_multiply(&mat, matrix);
            vg_lite_translate(x,y, &mat);
            vg_lite_scale(font_scale,font_scale, &mat); // 0.35 = height/units_per_em
            vg_lite_scale(-1.0,1.0, &mat);
            vg_lite_scale(-1.0,-1.0, &mat);
            text_wrap = 1;
        }

        /* Read unicode character */
        kx = 0;
        ug2 = *text;
        g2 = vft_find_glyph(font_face, ug2);
        if (g1 != NULL && g2 != NULL) {
            kx = vft_glyph_distance(g1, g2);
        }

#if (ENABLE_TEXT_WRAP==1)
        /* Wrap text */
        if ( (x + attributes->last_dx + ((g2->horiz_adv_x + kx )* font_scale))
              >= (720 - 5) )
        {
          text_wrap = 0;
          attributes->last_dx = 0;
          y += (attributes->font_height + 1);
          continue;
        }
#endif /* ENABLE_TEXT_WRAP */

        /* Compute glyph size in horizontal dimension */
        g1 = g2;
        text++;
             
        error = vg_lite_draw(rt, vft_cache_lookup(g2),
                             fill_rule,
                             &mat,
                             blend,
                             color);
        if ( error != VG_LITE_SUCCESS ) {
          break;
        }

        vg_lite_translate(g2->horiz_adv_x + kx, 0, &mat);
        attributes->last_dx += ((g2->horiz_adv_x + kx )* font_scale);
    }
    
    attributes->last_dx += 2; /* Space between 2 text strings */

    return 0;
}

void load_font_face(font_face_desc_t* font_face, uint8_t* buf, int font_face_offset)
{
    font_face_desc_t* g = font_face;
    int offset = font_face_offset;

    READ_BIN_FIELD_STR(font_family_name);
    READ_BIN_FIELD_UINT16(units_per_em);
    READ_BIN_FIELD_UINT16(ascent);
    READ_BIN_FIELD_UINT16(descent);
    READ_BIN_FIELD_UINT16(vg_font);
    READ_BIN_FIELD_UINT32(num_glyphs);
}

void load_glyph_table(font_face_desc_t* font_face, uint8_t* buf, int glyph_table_offset)
{
    int offset = glyph_table_offset;

    for (uint32_t i = 0; i < font_face->num_glyphs; i++) {
        glyph_desc_t* g = &font_face->glyphs[i];
    
        memset(g, 0, sizeof(glyph_desc_t));
        READ_BIN_FIELD_UINT16(unicode);
        READ_BIN_FIELD_UINT16(horiz_adv_x);
        READ_BIN_FIELD_UINT32(kern_num_entries);
        READ_BIN_FIELD_UINT32(kern_table_offset);
        READ_BIN_FIELD_DUMMY_POINTER();
        READ_BIN_FIELD_FLOAT(path.bounds[0]);
        READ_BIN_FIELD_FLOAT(path.bounds[1]);
        READ_BIN_FIELD_FLOAT(path.bounds[2]);
        READ_BIN_FIELD_FLOAT(path.bounds[3]);
        READ_BIN_FIELD_UINT32(path.num_draw_cmds);
        READ_BIN_FIELD_UINT32(path.draw_cmds_offset);
        READ_BIN_FIELD_DUMMY_POINTER();

        font_face->glyphs[i].kern_table = (kern_desc_t *)(buf + font_face->glyphs[i].kern_table_offset);
        font_face->glyphs[i].path.draw_cmds = (float *)(buf + font_face->glyphs[i].path.draw_cmds_offset);
    
        TRACE_INFO(("Glyph: '%c'\n", g->unicode));
        vft_dbg_glyph_desc(g, d_offset);
    }
}

/* Load vector font ROM table from file */
font_face_desc_t* vft_load_from_buffer(char* buf_base, int file_size)
{
    font_face_desc_t* font_face = NULL;
    uint32_t* blk_hdr;
    //int error = 0;

    /* Setup internal memory pointers of font_face_desc_t */
    int font_face_offset = 0;
    int glyph_table_offset = 0;
    //int kern_table_offset = 0;
    //int path_data_offset = 0;
    int offset = 0;

    /* May be we can avoid this lookup */
    while (offset < file_size) {
        blk_hdr = (uint32_t*)(buf_base + offset);
        eFontBlock_t eType = (eFontBlock_t)((*blk_hdr) >> 24);
        int size  = ((*blk_hdr) & ((1<<24)-1));
        TRACE_BIN(("BIN: %08x:  block(%hu, %hu)\n",
            offset, eType, size));
        offset += BLK_HDR_SIZE;

        /* Check integrity of block and then only proceed */
        switch (eType) {
        case eFontFaceDesc:
            font_face_offset = offset;
            break;
        case eGlyphTableDesc:
            glyph_table_offset = offset;
            break;
        case eKernTableDesc:
            //kern_table_offset = offset;
            break;
        case eGlyphData:
            //path_data_offset = offset;
            break;
        default:
        case eUnkBlock:
        case eMaxBlock:
            return NULL;;
        }
        offset += size;
    }

    if (offset < 0 || offset > file_size) {
        printf("ERROR: Vector font file integrity error aborting..\n");
        return NULL;
    }

    /* Make structure binary compliant to reduce loading time */
    font_face = (font_face_desc_t*)(buf_base + font_face_offset);
    font_face->glyphs = (glyph_desc_t*)(buf_base + glyph_table_offset);

    VFT_DBG_FONT_FACE_DESC(font_face, font_face_offset);
    for (uint32_t i = 0; i < font_face->num_glyphs; i++) {
        /* Update internal pointer from memory mapped file */
        font_face->glyphs[i].kern_table =
            (kern_desc_t*)(buf_base + font_face->glyphs[i].kern_table_offset);
        font_face->glyphs[i].path.draw_cmds =
            (float*)(buf_base + 
                font_face->glyphs[i].path.draw_cmds_offset);
       /* Try to get these paramters from Binary object or attributes */
       //vg_lite_format_t data_format = VG_LITE_FP32; //VG_LITE_S16;
       //vg_lite_quality_t quality = VG_LITE_HIGH;
    }
    VFT_DBG_GLYPH_TABLE(font_face, glyph_table_offset);

    VFT_DBG_KERN_TABLE(font_face, kern_table_offset);
    VFT_DBG_PATH_TABLE(font_face, path_data_offset);

    return font_face;
}

/* Find glyph of given character from glyph table */
glyph_desc_t* vft_find_glyph(font_face_desc_t* font_face, uint16_t ug2)
{
    const int num_glyphs = font_face->num_glyphs;
    glyph_desc_t* glyphs = font_face->glyphs;

    /* 
    * Present approach is slow linear search, this is ok in 1st prototype
    * Since glyph table is already sorted, we can have binary search 
    */
    for (int i = 0; i < num_glyphs; i++) {
        if (glyphs[i].unicode == ug2) {
            return (glyph_desc_t*)&glyphs[i];
        }
    }
    return (glyph_desc_t*)&glyphs[0];;
}

/* Find distance between 2 glyph symbols */
int vft_glyph_distance(glyph_desc_t* g1, glyph_desc_t* g2)
{
    signed short kx = 0;
    uint16_t ug2 = g2->unicode;
    kern_desc_t* kern_table = &g1->kern_table[0];

    for (int i = 0; i < g1->kern_num_entries; i++) {
        if (kern_table[i].unicode == ug2) {
            signed short *pkx = (signed short *)&kern_table[i].kern;
            kx = *pkx;
            break;
        }
    }

    return kx;
}

/*
* Get internal vector path of given glyph
* This is internal function
*/
path_desc_t* vft_get_glyph_path(glyph_desc_t* g2)
{
    return &g2->path;
}

/* Unload font face descriptor and all glyphs */
void vft_unload(font_face_desc_t* font_face)
{
    glyph_cache_free();
    //VFT_FREE(font_face);
}
