#include <os_common_api.h>
#include <mem_manager.h>
#include <ui_mem.h>
#include <string.h>
#include "bitmap_font_api.h"

#ifdef CONFIG_SIMULATOR
#include <kheap.h>
#define ALLOC_NO_WAIT OS_NO_WAIT
#else
#define ALLOC_NO_WAIT K_NO_WAIT
#endif

typedef struct _cache_mem_info
{
	void* ptr;
	size_t size;
	struct _cache_mem_info* next;
}cache_mem_info_t;


#define CMAP_CACHE_SIZE							1024*8

#ifdef CONFIG_BITMAP_FONT_USE_HIGH_FREQ_CACHE
#ifdef CONFIG_BITMAP_FONT_HIGH_FREQ_CACHE_SIZE
#define BITMAP_FONT_HIGH_FREQ_CACHE_SIZE		CONFIG_BITMAP_FONT_HIGH_FREQ_CACHE_SIZE
#else
#define BITMAP_FONT_HIGH_FREQ_CACHE_SIZE		1500*1024
#endif //CONFIG_BITMAP_FONT_HIGH_FREQ_CACHE_SIZE
#else
#define BITMAP_FONT_HIGH_FREQ_CACHE_SIZE		0
#endif //CONFIG_BITMAP_FONT_USE_HIGH_FREQ_CACHE

#ifdef CONFIG_BITMAP_PER_FONT_CACHE_SIZE

#define BITMAP_FONT_CACHE_SIZE				CONFIG_BITMAP_PER_FONT_CACHE_SIZE
#else
#define BITMAP_FONT_CACHE_SIZE				1024*64
#endif //CONFIG_BITMAP_PER_FONT_CACHE_SIZE

#ifdef CONFIG_BITMAP_FONT_MAX_OPENED_FONT
#define MAX_OPEND_FONT						CONFIG_BITMAP_FONT_MAX_OPENED_FONT
#else
#define MAX_OPEND_FONT						2
#endif //CONFIG_BITMAP_FONT_MAX_OPENED_FONT

#ifdef CONFIG_BITMAP_FONT_SUPPORT_EMOJI
#define MAX_EMOJI_FONT						1
#define MAX_EMOJI_NUM						100
#else
#define MAX_EMOJI_FONT						0
#define MAX_EMOJI_NUM						0
#endif  //CONFIG_BITMAP_FONT_SUPPORT_EMOJI

//#define BITMAP_FONT_PSRAM_SIZE				(MAX_OPEND_FONT+MAX_EMOJI_FONT)*(BITMAP_FONT_CACHE_SIZE+CMAP_CACHE_SIZE)+MAX_EMOJI_NUM*sizeof(emoji_font_entry_t)+BITMAP_FONT_HIGH_FREQ_CACHE_SIZE

#ifdef CONFIG_BITMAP_FONT_CACHE_POOL_SIZE
#define BITMAP_FONT_PSRAM_SIZE				CONFIG_BITMAP_FONT_CACHE_POOL_SIZE
#else
#define BITMAP_FONT_PSRAM_SIZE				360*1024
#endif

#define DEBUG_FONT_GLYPH					0
#define PRINT_FONT_GLYPH_ERR				0


#ifdef CONFIG_FREETYPE_FONT_MAX_FACES
#define FREETYPE_FONT_MAX_FACES				CONFIG_FREETYPE_FONT_MAX_FACES
#else
#define FREETYPE_FONT_MAX_FACES				2
#endif

#ifdef CONFIG_FREETYPE_FONT_MAX_SIZES
#define FREETYPE_FONT_MAX_SIZES				CONFIG_FREETYPE_FONT_MAX_SIZES
#else
#define FREETYPE_FONT_MAX_SIZES				3
#endif

#ifdef CONFIG_FREETYPE_FONT_MAX_SUBCACHE_BYTES
#define FREETYPE_FONT_MAX_SUBCACHE_BYTES				CONFIG_FREETYPE_FONT_MAX_SUBCACHE_BYTES
#else
#define FREETYPE_FONT_MAX_SUBCACHE_BYTES				150*1024
#endif

#if CONFIG_FREETYPE_FONT_SHAPE_CACHE_SIZE > 0
static char __aligned(4) shape_cache_buffer[CONFIG_FREETYPE_FONT_SHAPE_CACHE_SIZE] __in_section_unique(font.bss.cache);
static struct k_heap shape_cache_pool = {
    .heap = {
        .init_mem = shape_cache_buffer,
        .init_bytes = CONFIG_FREETYPE_FONT_SHAPE_CACHE_SIZE,
    },	
};
#endif

static char __aligned(4) bmp_font_info_buffer[12 * 1024] __in_section_unique(font.bss.cache);
static struct k_heap font_info_pool = {
    .heap = {
        .init_mem = bmp_font_info_buffer,
        .init_bytes = 12 * 1024,
    },	
};

#if defined(CONFIG_BITMAP_FONT_CACHE_POOL_SIZE) && CONFIG_BITMAP_FONT_CACHE_POOL_SIZE >= 12 * 1024
static char __aligned(4) bmp_font_cache_buffer[BITMAP_FONT_PSRAM_SIZE - 12 * 1024] __in_section_unique(font.bss.cache);
static struct k_heap font_mem_pool = {
    .heap = {
        .init_mem = bmp_font_cache_buffer,
        .init_bytes = BITMAP_FONT_PSRAM_SIZE - 12 * 1024,
    },
};

#endif


static int font_cache_peak_size = 0;
static int font_cache_total_size = 0;
static int font_cache_used_size = 0;
static int font_cahce_item_num = 0;
static cache_mem_info_t* cache_mem_info;
static int font_cache_inited = 0;

void bitmap_font_cache_init(void)
{
	if(font_cache_inited == 0)
	{
	    font_cache_total_size = BITMAP_FONT_PSRAM_SIZE;
	    font_cache_used_size = 0;

		os_printk("bitmap_font_cache_init\n");

		k_heap_init(&font_info_pool, font_info_pool.heap.init_mem, font_info_pool.heap.init_bytes);
#if defined(CONFIG_BITMAP_FONT_CACHE_POOL_SIZE) && CONFIG_BITMAP_FONT_CACHE_POOL_SIZE >= 10000
	    k_heap_init(&font_mem_pool, font_mem_pool.heap.init_mem, font_mem_pool.heap.init_bytes);
#endif

#if CONFIG_FREETYPE_FONT_SHAPE_CACHE_SIZE > 0
        k_heap_init(&shape_cache_pool, shape_cache_pool.heap.init_mem, shape_cache_pool.heap.init_bytes);
#endif
		font_cache_inited = 1;
	}
}

static void _add_cache_mem_info(void* ptr, uint32_t size)
{
    cache_mem_info_t* item=NULL;

    //item = mem_malloc(sizeof(cache_mem_info_t));

    item = k_heap_alloc(&font_info_pool, sizeof(cache_mem_info_t), ALLOC_NO_WAIT);
    if(item == NULL)
    {
        SYS_LOG_INF("cache mem info malloc failed %d, %d\n", font_cache_used_size, font_cahce_item_num);
        return;
    }

    item->ptr = ptr;
    item->size = size;
    item->next = NULL;
    if(cache_mem_info==NULL)
    {
        cache_mem_info = item;
    }
    else
    {
        cache_mem_info_t* pos = cache_mem_info;
        cache_mem_info_t* prev = cache_mem_info;
        while(pos)
        {
            prev = pos;
            pos = pos->next;
        }
        prev->next = item;
    }

    font_cache_used_size += size;
	font_cahce_item_num++;
	if(font_cache_peak_size < font_cache_used_size)
	{
		font_cache_peak_size = font_cache_used_size;
		//SYS_LOG_INF("font_cache_peak_size %d\n", font_cache_peak_size);
	}

	//SYS_LOG_INF("font_cache_used_size %d, font_cahce_item_num %d\n", font_cache_used_size, font_cahce_item_num);
    //SYS_LOG_INF("font cache mem add %d, total %d, peak size %d\n", size, font_cache_used_size, font_cache_peak_size);
}

static void _remove_cache_mem_info(void* ptr)
{
    cache_mem_info_t* item;
    cache_mem_info_t* prev;

    item = cache_mem_info;
    prev = NULL;
    while(item)
    {
        if(item->ptr == ptr)
        {
            break;
        }

        prev = item;
        item = item->next;
    }

    if(item == NULL)
    {
        SYS_LOG_INF("cache mem info not found %p\n", ptr);
        return;
    }

    if(prev)
    {
        prev->next = item->next;
    }

    if(item == cache_mem_info)
    {
        cache_mem_info = item->next;
    }
	font_cahce_item_num--;
    font_cache_used_size -= item->size;
    //SYS_LOG_INF("font cache mem remove %d, total %d\n", item->size, font_cache_used_size);
    k_heap_free(&font_info_pool, item);
}

uint32_t _get_cache_mem_info(void* ptr)
{
    cache_mem_info_t* item;

    item = cache_mem_info;
    while(item)
    {
        if(item->ptr == ptr)
        {
            break;
        }

        item = item->next;
    }

	if (item) {
		return item->size;
	}

	return 0;
}

void* bitmap_font_cache_malloc(uint32_t size)
{
	void *ptr;

	if(size % 4 != 0)
	{
		size = (size/4 + 1)*4;
	}
#if defined(CONFIG_BITMAP_FONT_CACHE_POOL_SIZE) && CONFIG_BITMAP_FONT_CACHE_POOL_SIZE >= 10000
    ptr = k_heap_alloc(&font_mem_pool, size, ALLOC_NO_WAIT);
#else
	ptr = ui_mem_gui_alloc(size);
#endif

	if (ptr == NULL) {
		SYS_LOG_ERR("font cache heap alloc failed, size %d\n", size);
		return ptr;
	}

	_add_cache_mem_info(ptr, size);
	return ptr;
}

void bitmap_font_cache_free(void* ptr)
{
#if defined(CONFIG_BITMAP_FONT_CACHE_POOL_SIZE) && CONFIG_BITMAP_FONT_CACHE_POOL_SIZE >= 10000
	k_heap_free(&font_mem_pool, ptr);
#else
	ui_mem_gui_free(ptr);
#endif
	_remove_cache_mem_info(ptr);
}

uint32_t bitmap_font_cache_get_size(void* ptr)
{
	return _get_cache_mem_info(ptr);
}

uint32_t bitmap_font_get_max_fonts_num(void)
{
	return (MAX_OPEND_FONT+MAX_EMOJI_FONT);
}

//use as default size if not configd for current font size
uint32_t bitmap_font_get_font_cache_size(void)
{
	return BITMAP_FONT_CACHE_SIZE;
}

uint32_t bitmap_font_get_max_emoji_num(void)
{
	return MAX_EMOJI_NUM;
}

uint32_t bitmap_font_get_cmap_cache_size(void)
{
	return CMAP_CACHE_SIZE;
}

void bitmap_font_cache_dump_info(void)
{
    SYS_LOG_INF("bitmap font cache info dump: total used %d, peak size %d, max size %d\n", font_cache_used_size, font_cache_peak_size, font_cache_total_size);
}

int bitmap_font_glyph_debug_is_on(void)
{
#if DEBUG_FONT_GLYPH==1
	return 1;
#else
	return 0;
#endif
}

int bitmap_font_glyph_err_print_is_on(void)
{
#if PRINT_FONT_GLYPH_ERR==1
	return 1;
#else
	return 0;
#endif
}

int bitmap_font_get_high_freq_enabled(void)
{
#ifdef CONFIG_BITMAP_FONT_USE_HIGH_FREQ_CACHE
	return 1;
#else
	return 0;
#endif
}

void bitmap_font_get_decompress_param(int bmp_size, int font_size, int* in_size, int* line_size)
{
	*in_size = bmp_size*3/2;
	*line_size = ((font_size+3)/4)*4*2;    
}


int freetype_font_get_font_fixed_bpp(void)
{
#ifdef CONFIG_FREETYPE_FONT_BITMAP_BPP
	return CONFIG_FREETYPE_FONT_BITMAP_BPP;
#else
	return 2;
#endif
}

int freetype_font_get_max_face_num(void)
{
	return FREETYPE_FONT_MAX_FACES;
}


int freetype_font_get_max_size_num(void)
{
	return FREETYPE_FONT_MAX_SIZES;
}

uint32_t freetype_font_get_font_cache_size(void)
{
#ifdef CONFIG_FREETYPE_PER_FONT_CACHE_SIZE
	return CONFIG_FREETYPE_PER_FONT_CACHE_SIZE;
#else
	return 65536;
#endif
}

int freetype_font_get_max_ftccache_bytes(void)
{
	return FREETYPE_FONT_MAX_SUBCACHE_BYTES;
}

int freetype_font_get_memory_face_enabled(void)
{
#ifndef CONFIG_SIMULATOR
#ifdef CONFIG_FREETYPE_FONT_ENABLE_MEMORY_FACE
	return 1;
#else
	return 0;
#endif
#else
    return 0;
#endif
}


int freetype_font_use_svg_path(void)
{
#ifdef CONFIG_FREETYPE_FONT_ENABLE_SVG_PATH
	return 1;
#else
	return 0;
#endif
}

int freetype_font_get_max_vertices(void)
{
#ifdef CONFIG_FREETYPE_FONT_ENABLE_SVG_PATH
	return 256;
#else
	return 1;
#endif    
}

int freetype_font_enable_subpixel(void)
{
#ifdef ENABLE_SUBPIX
    return 1;
#else
    return 0;
#endif
}


int emoji_font_use_mmap(void)
{
#ifdef CONFIG_EMOJI_FONT_USE_MMAP
	return 1;
#else
	return 0;
#endif
}

void* freetype_font_shape_cache_malloc(uint32_t size)
{
#if CONFIG_FREETYPE_FONT_SHAPE_CACHE_SIZE > 0
	void *ptr;
    ptr = k_heap_alloc(&shape_cache_pool, size, ALLOC_NO_WAIT);

	if (ptr == NULL) {
		SYS_LOG_ERR("shape cache heap alloc failed, size %d\n", size);
		return ptr;
	}

	return ptr;
#else
    return NULL;
#endif
}

void freetype_font_shape_cache_free(void* ptr)
{
#if CONFIG_FREETYPE_FONT_SHAPE_CACHE_SIZE > 0
    k_heap_free(&shape_cache_pool, ptr);
#else
    return;
#endif
}

int freetype_font_get_shape_info_size(void)
{
#if CONFIG_FREETYPE_FONT_SHAPE_CACHE_SIZE > 0
    return CONFIG_FREETYPE_SHAPE_INFO_CACHE_SIZE;
#else
    return 0;
#endif
}