/*
 * ACF就是把bdf字体进行二进制压缩。其文件分为三个部分
 * 1文件头，总共6字节，前2字节是字体数量，后面4字节是bdf的FONTBOUNDINGBOX
 * 2数据索引，总共是字体数量*5个字节。其中5个字节内容是前2字节的unicode编码和后3字节的数据偏移量
 * 3数据，每个数据是前4个字节的bbx，第5个字节的前7位是DWIDTH的x分量，后1位0表示位图每次读取1字节，为1表示每次读取2字节。第6个字节及以后是BITMAP的数据，总长度是bbx[1]的长度。
 */

#include "arm11/acf.h"
#include "arm11/console.h"
#include "arm11/fmt.h"
#include "fs.h"

// 画点函数
#ifndef ACF_LIT_POINT
#error "ACF_LIT_POINT SHOULD BE DECLARE BEFORE USING GB2312 FONT"
#endif

extern uint8_t canvas[];

typedef struct
{
    uint16_t start;
    uint16_t end;
    uint8_t padding;
    uint8_t bbxdcnt;
} Fragment;

typedef struct
{
    uint8_t width;
    uint8_t height;
    int8_t offsetx;
    int8_t offsety;
    char *filename;
    uint32_t filesize;
    uint16_t fragsize;
    Fragment *fragments;
} ACFont;

static ACFont gblfont = {
    0, 0, 0, 0, NULL, 0, 0, NULL
};

#define ACFONT_HEAD_SIZE 10u
#define ACFONT_HEAD_MAGNO 0x02464341u
#define ACFONT_HEAD_WIDTH(buf) ((buf)[4])
#define ACFONT_HEAD_HEIGHT(buf) ((buf)[5])
#define ACFONT_HEAD_OFFSETX(buf) ((int8_t)(buf)[6])
#define ACFONT_HEAD_OFFSETY(buf) ((int8_t)(buf)[7])
#define ACFONT_HEAD_MEMFRAG(buf) (*(uint16_t*)( (buf)+8 ))

#define ACFONT_SIZEOF_BBX 4u
#define ACFONT_SIZEOF_GLYPH 64u

#define ACFONT_BBXD_FROM_FRAG(pFrag, idx) (uint8_t*)( (uint8_t*)(pFrag) + sizeof(Fragment) + ACFONT_SIZEOF_BBX*(idx) )

// 提供两个方法：
// 1 设置字体文件名称
// 2 使用该字体在一个原点范围内渲染一行文字

// 设置字体名称
int acf_set_font(const char *acfile)
{
    // open the font file
    FHandle font;
    uint32_t readed;
    if( RES_OK != fOpen(&font, acfile, FA_OPEN_EXISTING | FA_READ) )
        return ACFONT_NOT_FOUND;

    // 读取数据
    uint8_t head[ACFONT_HEAD_SIZE];
    if( RES_OK != fRead(font, head, ACFONT_HEAD_SIZE, &readed)
        || readed != ACFONT_HEAD_SIZE )
    {
        fClose(font);
        return ACFONT_INVALID;
    }
    if ( *(uint32_t*)(head) != ACFONT_HEAD_MAGNO )
    {
        fClose(font);
        return ACFONT_NOT_SUPPORT;
    }

    // assign basic data from file
    gblfont.width = ACFONT_HEAD_WIDTH(head);
    gblfont.height = ACFONT_HEAD_HEIGHT(head);
    gblfont.offsetx = ACFONT_HEAD_OFFSETX(head);
    gblfont.offsety = ACFONT_HEAD_OFFSETY(head);

    // get filesize
    gblfont.filesize = fSize(font);

    if (gblfont.filename != NULL)
    {
        free(gblfont.filename);
        gblfont.filename = NULL;
    }
    if (gblfont.fragments)
    {
        free( gblfont.fragments );
        gblfont.fragments = NULL;
    }
    
    //if( RES_OK != fLseek(font, ACFONT_HEAD_SIZE) )
    //{
    //    fClose( font );
    //    return ACFONT_INVALID;
    //}

    // get index chapters
    // 1 decide chapter count
    uint16_t memfrag = ACFONT_HEAD_MEMFRAG(head);
    uint8_t *fragments = (uint8_t*)malloc(memfrag);
    if( fragments == NULL )
    {
        fClose(font);
        return ACFONT_MEM_EMPTY;
    }

    int fill = 0;
    do{
        Result res;
        if( RES_OK != (res=fRead(font, fragments+fill, memfrag-fill < 256 ? memfrag-fill : 256, &readed)) ){
            free( fragments );
            fClose( font );
            return ACFONT_INVALID;
        }
        fill += readed;
    } while( fill < memfrag );    

    // save filename for `fopen`
    int len = strlen(acfile);
    char *file = (char *)malloc(len + 1);
    if (file == NULL)
    {
        free( fragments );
        memset(&gblfont, 0, sizeof(gblfont));
        fClose(font);
        return ACFONT_MEM_EMPTY;
    }

    memcpy(file, acfile, len);
    file[len] = 0;
    gblfont.filename = file;

    gblfont.fragments = (Fragment*)fragments;
    gblfont.fragsize = memfrag;

    fClose(font);
    return 0;
}

/*
   |  Unicode符号范围      |  UTF-8编码方式
 n |  (十六进制)           | (二进制)
---+-----------------------+------------------------------------------------------
 1 | 0000 0000 - 0000 007F |                                              0xxxxxxx
 2 | 0000 0080 - 0000 07FF |                                     110xxxxx 10xxxxxx
 3 | 0000 0800 - 0000 FFFF |                            1110xxxx 10xxxxxx 10xxxxxx
 4 | 0001 0000 - 0010 FFFF |                   11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
 5 | 0020 0000 - 03FF FFFF |          111110xx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx
 6 | 0400 0000 - 7FFF FFFF | 1111110x 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx
 */
#define BYTE_H 1u << 7
#define TEST_H(c) ((c)&BYTE_H)
#define ISSET_H(c) TEST_H(c) == BYTE_H
#define EXBYTE 0x3fu
#define EXDATA(c) ((c)&EXBYTE)
static inline const char *next_unicode(const char *utf8, uint32_t *code)
{
    if (utf8 == NULL || *utf8 == '\0')
        return NULL;

    uint8_t c = *utf8++;

    if (c > 0x7f)
    {
        int n;
        for (n = 0; ISSET_H(c); c <<= 1)
            ++n;
        if (n > 1)
        {
            for (*code = 0, c >>= n;
                 n-- > 0;
                 c = EXDATA(*utf8++))
                *code = (*code << 6) | c;
            --utf8;
        }
        else
            *code = c;
    }
    else
        *code = c;

    return utf8;
}

static int bsearch_font(uint32_t unicode, Fragment **pFrag)
{
    int retval = 0;
    uint8_t *memory = (uint8_t*)gblfont.fragments;
    for( int i=0; i < gblfont.fragsize; )
    {
        Fragment *frag = (Fragment*)&memory[i];
        if( frag->start <= unicode ) {
            if( unicode <= frag->end )
            {
                // hit
                int diff = unicode - frag->start;
                *pFrag = frag;
                return retval + frag->padding * diff;
            }
            else {
                retval += frag->padding * (frag->end - frag->start + 1);
                i += sizeof(Fragment) + ACFONT_SIZEOF_BBX * frag->bbxdcnt;
            }
        }
        else return ACFONT_NOT_FOUND;
    }
    return ACFONT_NOT_FOUND;
}

#define BIT_PER_BYTE 8

#define BIT_AT_POS(mem, idx) ((mem)[(idx) / BIT_PER_BYTE] &  (1 << ((idx) % BIT_PER_BYTE)))
#define SET_AT_POS(mem, idx) ((mem)[(idx) / BIT_PER_BYTE] |= (1 << ((idx) % BIT_PER_BYTE)) )

#define SIZEOF_S6 6
static inline int readS6(uint8_t *p, int index)
{
    int ret = 0;
    int symbol = BIT_AT_POS(p, index);
    for (int i = 0; i < SIZEOF_S6 - 1; ++i)
    {
        ret <<= 1;
        ++index;
        if (BIT_AT_POS(p, index))
            ret |= 1;
    }
    return symbol ? -ret : ret;
}

#define PIX_IN_LINE(x,y,w) (x) + (y)*(w)
static int render_unicode(FHandle fd, int *x, unsigned width, unsigned height, uint32_t code, unsigned *width_max, uint8_t *ram)
{
    uint32_t readed;

    // 获取code对应的字体信息
    Fragment *frag;
    int font_pos = bsearch_font(code, &frag);
    if (font_pos < 0)
    {
        return -1;
    }
    font_pos += ACFONT_HEAD_SIZE + gblfont.fragsize;
    if ((uint32_t)font_pos > gblfont.filesize)
    {
        return -2;
    }

    uint8_t font[ACFONT_SIZEOF_GLYPH], *glyph;
    if( RES_OK != fLseek(fd, font_pos) )
        return -3;
    
    if( RES_OK != fRead(fd, font, frag->padding, &readed)
        || readed != (uint32_t)frag->padding )
        return -4;

    int8_t bbx[4], fw;
    uint8_t *bbxd;
    if( frag->bbxdcnt == 1 )
    {
        bbxd = ACFONT_BBXD_FROM_FRAG(frag, 0);
        glyph = font;
    }
    else 
    {
        uint8_t index = font[0];
        bbxd = ACFONT_BBXD_FROM_FRAG(frag, index);
        glyph = font+1;
    }
    bbx[0] = readS6(bbxd, 0);
    bbx[1] = readS6(bbxd, SIZEOF_S6);
    bbx[2] = readS6(bbxd, SIZEOF_S6 * 2);
    bbx[3] = readS6(bbxd, SIZEOF_S6 * 3);
    fw = readS6(bbxd, SIZEOF_S6 * 4);

    // render
    // 从上往下(y从大到小，x从小到大)进行绘制
    int px = *x, py = 0-gblfont.offsety;                            // px/py不参与计算位置
    int cx = px + bbx[2], cy = py + bbx[1] + bbx[3]; // cx/cy计算位置进行绘制

    // 先检查宽度
    if ((width_max != NULL) && (cx + bbx[0] >= (int)(*width_max)))
        return 1;

    // 绘制
    for (int i = 0; i < bbx[1]; ++i)
    {
        for (int j = 0; j < bbx[0]; ++j)
        {
            int tx = cx+j, ty = cy-i;
            if( 0 <= ty && ty < (int)height && 0 <= tx && tx < (int)width )
            {
                if( BIT_AT_POS(glyph, bbx[0] * i + j) )
                {
                    SET_AT_POS( ram, PIX_IN_LINE(tx, ty, width) );
                }
            }
        }
    }
    
    // 更新
    *x = px + fw;
    return 0;
}

// 根据字体绘制中文字符
// x,y - 绘制一行字符的基准点
// maxwidth - 最长绘制多少个像素点，填0则忽略此参数
// utf8_line - utf8字符串
// 返回：第一个未绘制的字符的位置，如果width为0，则返回永远是NULL
const char *acf_draw(int x, int y, unsigned width, unsigned height, unsigned maxwidth, uint16_t color, const char *utf8_line)
{
    FHandle font;
    int linex = 0;
    if( gblfont.filename == NULL || RES_OK != fOpen(&font, gblfont.filename, FA_OPEN_EXISTING | FA_READ) )
    {
        // log
        return utf8_line;
    }

    uint32_t unicode;
    if( maxwidth == 0 ) maxwidth = width;
    unsigned *option = width == 0 ? NULL : &maxwidth;

    const int ramsize = gblfont.height * maxwidth + BIT_PER_BYTE - 1 / BIT_PER_BYTE;
    uint8_t *localram = malloc( ramsize );
    memset( localram, 0, ramsize );

    for (const char *next = next_unicode(utf8_line, &unicode);
         next != NULL;
         next = next_unicode(utf8_line, &unicode))
    {
        int error = render_unicode(font, &linex, maxwidth, gblfont.height, unicode, option, localram);
        if (error > 0 ) break;
        else if(error < 0 ) error = render_unicode(font, &linex, maxwidth, height, '?', option, localram);

        if( error )
        {
            free( localram );
            fClose(font);
            return utf8_line;
        }

        utf8_line = next;
    }

    // copy back to canvas
    uint16_t *frame = consoleGet()->frameBuffer;
    uint16_t fg = color;
    for( int i=0; i < gblfont.height; ++i )
    {
        for( int j=0; j < (int)maxwidth; ++j )
        {
            if( BIT_AT_POS(localram, PIX_IN_LINE(j, i, maxwidth)) )
            { 
                ACF_LIT_POINT(x+j, y-(gblfont.height-1-i), (int)width, (int)height, 1);
            }
        }
    }

    free( localram );
    fClose(font);
    return NULL;
}
