/**
 * @file hv_boot_Lzss.c
 * @brief Lzss source code.
 * @details This file provides the following functions: \n
 *          (1) Initialization and de-initialization functions \n
 *          (2) Start and stop functions \n
 *          (3) Feed functions \n
 *
 * @author HiView SoC Software Team
 * @version 1.0.0
 * @date 2022-08-18
 * @copyright Copyright(c),2022-8, Hiview Software. All rights reserved.
 * @par History:
 * <table>
 * <tr><th>Author                        <th>Date         <th>Change Description
 * <tr><td>HiView SoC Software Team      <td>2022-08-23   <td>init
 * </table>
 */

#include "hv_chip_Config.h"
#include "hv_boot_Common.h"

#define N                   4096            /* size of ring buffer */
#define F                   18              /* upper limit for match_length */
#define THRESHOLD           4               /* encode string into position and length if match_length is greater than this */
#define NIL                 N               /* index for root of binary search trees */

static unsigned long int textsize = 0;             /* text size counter */
static unsigned long int codesize = 0;             /* code size counter */
static unsigned long int printcount = 0;           /* counter for reporting progress every 1K bytes */
static unsigned char text_buf[N + F - 1] = {0};    /* ring buffer of size N,with extra F-1 bytes to facilitate string comparison */
static int     match_position = 0;
static int     match_length = 0;                   /* of longest match.  These are set by the InsertNode() procedure. */
static int     lson[N + 1] = {0};
static int     rson[N + 257] = {0};
static int     dad[N + 1] = {0};                   /* left & right children &parents -- These constitute binary search trees. */

/* input & output files */
static unsigned char *infile = 0;
static unsigned char *fileoff = 0;
static unsigned char *outfile = 0;

void InitTree(void)  /* initialize trees */
{
    int  i;

    /* For i = 0 to N - 1, rson[i] and lson[i] will be the right and
       left children of node i.  These nodes need not be initialized.
       Also, dad[i] is the parent of node i.  These are initialized to
       NIL (= N), which stands for 'not used.'
       For i = 0 to 255, rson[N + i + 1] is the root of the tree
       for strings that begin with character i.  These are initialized
       to NIL.  Note there are 256 trees. */

    for (i = N + 1; i <= N + 256; i++) rson[i] = NIL;
    for (i = 0; i < N; i++) dad[i] = NIL;
}

void InsertNode(int r)
    /* Inserts string of length F, text_buf[r..r+F-1], into one of the
       trees (text_buf[r]'th tree) and returns the longest-match position
       and length via the global variables match_position and match_length.
       If match_length = F, then removes the old node in favor of the new
       one, because the old one will be deleted sooner.
       Note r plays double role, as tree node and position in buffer. */
{
    int  i, p, cmp;
    unsigned char  *key;

    cmp = 1;  key = &text_buf[r];  p = N + 1 + key[0];
    rson[r] = lson[r] = NIL;  match_length = 0;
    for ( ; ; ) {
        if (cmp >= 0) {
            if (rson[p] != NIL) p = rson[p];
            else {  rson[p] = r;  dad[r] = p;  return;  }
        } else {
            if (lson[p] != NIL) p = lson[p];
            else {  lson[p] = r;  dad[r] = p;  return;  }
        }
        for (i = 1; i < F; i++)
            if ((cmp = key[i] - text_buf[p + i]) != 0)  break;
        if (i > match_length) {
            match_position = p;
            if ((match_length = i) >= F)  break;
        }
    }
    dad[r] = dad[p];  lson[r] = lson[p];  rson[r] = rson[p];
    dad[lson[p]] = r;  dad[rson[p]] = r;
    if (rson[dad[p]] == p) rson[dad[p]] = r;
    else                   lson[dad[p]] = r;
    dad[p] = NIL;  /* remove p */
}

void DeleteNode(int p)  /* deletes node p from tree */
{
    int  q;

    if (dad[p] == NIL) return;  /* not in tree */
    if (rson[p] == NIL) q = lson[p];
    else if (lson[p] == NIL) q = rson[p];
    else {
        q = lson[p];
        if (rson[q] != NIL) {
            do {  q = rson[q];  } while (rson[q] != NIL);
            rson[dad[q]] = lson[q];  dad[lson[q]] = dad[q];
            lson[q] = lson[p];  dad[lson[p]] = q;
        }
        rson[q] = rson[p];  dad[rson[p]] = q;
    }
    dad[q] = dad[p];
    if (rson[dad[p]] == p) rson[dad[p]] = q;  else lson[dad[p]] = q;
    dad[p] = NIL;
}

void Decode(void)   /* Just the reverse of Encode(). */
{
    int  i, j, k, r, c;
    unsigned int  flags;

    for (i = 0; i < N - F; i++) text_buf[i] = ' ';
    r = N - F;  flags = 0;
    for ( ; ; ) {
        if (((flags >>= 1) & 256) == 0) {
            if (infile==fileoff)
            {
                break;
            }
            c = *infile++;
            flags = c | 0xff00;     /* uses higher byte cleverly */
        }                           /* to count eight */
        if (flags & 1) {
            if (infile==fileoff)
            {
                break;
            }
            c = *infile++;
            *outfile++=c;  text_buf[r++] = c;  r &= (N - 1);
        } else {
            if (infile==fileoff)
            {
                break;
            }
            i = *infile++;
            if (infile==fileoff)
            {
                break;
            }
            j = *infile++;
            i |= ((j & 0xf0) << 4);  j = (j & 0x0f) + THRESHOLD;
            for (k = 0; k <= j; k++) {
                c = text_buf[(i + k) & (N - 1)];
                *outfile++=c;  text_buf[r++] = c;  r &= (N - 1);
            }
        }
    }
}


int Hv_Boot_GetFileSize(void)
{
    char *pcPoint   = infile;
    int uiSize      = 0;
    int iCount      = 0;
    unsigned int uiMaigic = 0;

    Hv_Boot_Print_String(BOOT_DEBUG_LEVEL_INFO, ">>> check size in\n");
    Hv_Boot_PrintHex(BOOT_DEBUG_LEVEL_INFO, (unsigned int)infile);

    /* check magic "HIVIEW!" */
    for (pcPoint = infile, iCount = 0; iCount < 4; iCount++, pcPoint++)
    {
        uiMaigic = (uiMaigic << 8) + (*pcPoint);
    }
    if (uiMaigic != 0x48495649)
    {
        Hv_Boot_Print_String(BOOT_DEBUG_LEVEL_WAR, ">>> no hivi\n");
        return -1;
    }
    for (pcPoint = infile + 4, iCount = 0; iCount < 4; iCount++, pcPoint++)
    {
        uiMaigic = (uiMaigic << 8) + (*pcPoint);
    }
    if (uiMaigic != 0x4557210A)
    {
         Hv_Boot_Print_String(BOOT_DEBUG_LEVEL_WAR, ">>> no iew!\n");
        return -2;
    }

    /* check size */
    for (pcPoint = infile + 8, iCount = 0;
        (*pcPoint != 0xa) && (*pcPoint < 0x3a) && (*pcPoint > 0x2f) && (iCount < 8);
        pcPoint++,iCount++)
    {
        uiSize = uiSize * 10 + (*pcPoint - 0x30);
    }

    if ((uiSize > 512) && (uiSize < 1458716))
    {
        infile += 32;
        fileoff = infile + uiSize;

        Hv_Boot_PrintHex(BOOT_DEBUG_LEVEL_INFO, uiSize);
        return 0;
    }

    Hv_Boot_Print_String(BOOT_DEBUG_LEVEL_WAR, ">>> size wrong!\n");
    return -3;
}

int Hv_Boot_LzssDecompress(char *pcSrc, char *pcDst, int iChanegeEndian, int iSize)
{
    int iLoop = 0;
    unsigned char ucTemp = 0;

    if (pcSrc != 0)
    {
        infile = pcSrc;
    }

    if (infile == 0)
    {
        return -1;
    }

    if (pcDst == 0)
    {
        return -1;
    }
    outfile = pcDst;

    if (Hv_Boot_GetFileSize())
    {
        if (iSize == 0)
        {
            return -1;
        } else {
            fileoff = infile + iSize;
            Hv_Boot_Print_String(BOOT_DEBUG_LEVEL_INFO, ">>> valid size\n");
            Hv_Boot_PrintHex(BOOT_DEBUG_LEVEL_INFO, (unsigned int)iSize);
        }
    }

    Hv_Boot_Print_String(BOOT_DEBUG_LEVEL_INFO, ">>> decompress bin\n");
    Hv_Boot_PrintHex(BOOT_DEBUG_LEVEL_INFO, (unsigned int)infile);
    Hv_Boot_PrintHex(BOOT_DEBUG_LEVEL_INFO, (unsigned int)fileoff);

    Decode();

    Hv_Boot_Print_String(BOOT_DEBUG_LEVEL_INFO, ">>> decompress bin over\n");
    Hv_Boot_PrintHex(BOOT_DEBUG_LEVEL_INFO, (unsigned int)pcDst);
    Hv_Boot_PrintHex(BOOT_DEBUG_LEVEL_INFO, (unsigned int)outfile);

    if (iChanegeEndian == 0)
    {
        return 0;
    }

    for (iLoop = 0; iLoop < (fileoff - infile); iLoop += 4)
    {
        ucTemp              = *(infile+iLoop+3);
        *(infile+iLoop+3)   = *(infile+iLoop+0);
        *(infile+iLoop+0)   = ucTemp;
        ucTemp              = *(infile+iLoop+2);
        *(infile+iLoop+2)   = *(infile+iLoop+1);
        *(infile+iLoop+1)   = ucTemp;
    }

    return 0;
}

