// Copyright 2014 PDFium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
 
// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com

#include "JBig2_HuffmanTable.h"
#include "JBig2_BitStream.h"
#include <string.h>

CJBig2_HuffmanTable::CJBig2_HuffmanTable(const JBig2TableLine *pTable, int nLines,
        FX_BOOL bHTOOB)
{
    init();
    m_bOK = parseFromStandardTable(pTable, nLines, bHTOOB);
}

CJBig2_HuffmanTable::CJBig2_HuffmanTable(CJBig2_BitStream *pStream)
{
    init();
    m_bOK = parseFromCodedBuffer(pStream);
}

CJBig2_HuffmanTable::~CJBig2_HuffmanTable()
{
    if(CODES) {
        m_pModule->JBig2_Free(CODES);
    }
    if(PREFLEN) {
        m_pModule->JBig2_Free(PREFLEN);
    }
    if(RANGELEN) {
        m_pModule->JBig2_Free(RANGELEN);
    }
    if(RANGELOW) {
        m_pModule->JBig2_Free(RANGELOW);
    }
}
void CJBig2_HuffmanTable::init()
{
    HTOOB = FALSE;
    NTEMP = 0;
    CODES = NULL;
    PREFLEN = NULL;
    RANGELEN = NULL;
    RANGELOW = NULL;
}
int CJBig2_HuffmanTable::parseFromStandardTable(const JBig2TableLine *pTable, int nLines, FX_BOOL bHTOOB)
{
    int CURLEN, LENMAX, CURCODE, CURTEMP, i;
    int *LENCOUNT;
    int *FIRSTCODE;
    HTOOB = bHTOOB;
    NTEMP = nLines;
    CODES = (int*)m_pModule->JBig2_Malloc2(sizeof(int), NTEMP);
    PREFLEN = (int*)m_pModule->JBig2_Malloc2(sizeof(int), NTEMP);
    RANGELEN = (int*)m_pModule->JBig2_Malloc2(sizeof(int), NTEMP);
    RANGELOW = (int*)m_pModule->JBig2_Malloc2(sizeof(int), NTEMP);
    LENMAX = 0;
    for(i = 0; i < NTEMP; i++) {
        PREFLEN[i] = pTable[i].PREFLEN;
        RANGELEN[i] = pTable[i].RANDELEN;
        RANGELOW[i] = pTable[i].RANGELOW;
        if(PREFLEN[i] > LENMAX) {
            LENMAX = PREFLEN[i];
        }
    }
    LENCOUNT = (int*)m_pModule->JBig2_Malloc2(sizeof(int), (LENMAX + 1));
    JBIG2_memset(LENCOUNT, 0, sizeof(int) * (LENMAX + 1));
    FIRSTCODE = (int*)m_pModule->JBig2_Malloc2(sizeof(int), (LENMAX + 1));
    for(i = 0; i < NTEMP; i++) {
        LENCOUNT[PREFLEN[i]] ++;
    }
    CURLEN = 1;
    FIRSTCODE[0] = 0;
    LENCOUNT[0]  = 0;
    while(CURLEN <= LENMAX) {
        FIRSTCODE[CURLEN] = (FIRSTCODE[CURLEN - 1] + LENCOUNT[CURLEN - 1]) << 1;
        CURCODE = FIRSTCODE[CURLEN];
        CURTEMP = 0;
        while(CURTEMP < NTEMP) {
            if(PREFLEN[CURTEMP] == CURLEN) {
                CODES[CURTEMP] = CURCODE;
                CURCODE = CURCODE + 1;
            }
            CURTEMP = CURTEMP + 1;
        }
        CURLEN = CURLEN + 1;
    }
    m_pModule->JBig2_Free(LENCOUNT);
    m_pModule->JBig2_Free(FIRSTCODE);
    return 1;
}

#define HT_CHECK_MEMORY_ADJUST			\
    if(NTEMP >= nSize)	\
    {	\
        nSize += 16;	\
        PREFLEN  = (int*)m_pModule->JBig2_Realloc(PREFLEN,sizeof(int)*nSize);	\
        RANGELEN = (int*)m_pModule->JBig2_Realloc(RANGELEN,sizeof(int)*nSize);	\
        RANGELOW = (int*)m_pModule->JBig2_Realloc(RANGELOW,sizeof(int)*nSize);	\
    }
int CJBig2_HuffmanTable::parseFromCodedBuffer(CJBig2_BitStream *pStream)
{
    unsigned char HTPS, HTRS;
    int HTLOW, HTHIGH;
    int CURRANGELOW;
    int nSize = 16;
    int CURLEN, LENMAX, CURCODE, CURTEMP, i;
    int *LENCOUNT;
    int *FIRSTCODE;
    unsigned char cTemp;
    if(pStream->read1Byte(&cTemp) == -1) {
        goto failed;
    }
    HTOOB = cTemp & 0x01;
    HTPS  = ((cTemp >> 1) & 0x07) + 1;
    HTRS  = ((cTemp >> 4) & 0x07) + 1;
    if(pStream->readInteger((FX_DWORD*)&HTLOW) == -1 ||
            pStream->readInteger((FX_DWORD*)&HTHIGH) == -1) {
        goto failed;
    }
    PREFLEN  = (int*)m_pModule->JBig2_Malloc2(sizeof(int), nSize);
    RANGELEN = (int*)m_pModule->JBig2_Malloc2(sizeof(int), nSize);
    RANGELOW = (int*)m_pModule->JBig2_Malloc2(sizeof(int), nSize);
    CURRANGELOW = HTLOW;
    NTEMP = 0;
    do {
        HT_CHECK_MEMORY_ADJUST
        if((pStream->readNBits(HTPS, &PREFLEN[NTEMP]) == -1)
                || (pStream->readNBits(HTRS, &RANGELEN[NTEMP]) == -1)) {
            goto failed;
        }
        RANGELOW[NTEMP] = CURRANGELOW;
        CURRANGELOW = CURRANGELOW + (1 << RANGELEN[NTEMP]);
        NTEMP = NTEMP + 1;
    } while(CURRANGELOW < HTHIGH);
    HT_CHECK_MEMORY_ADJUST
    if(pStream->readNBits(HTPS, &PREFLEN[NTEMP]) == -1) {
        goto failed;
    }
    RANGELEN[NTEMP] = 32;
    RANGELOW[NTEMP] = HTLOW - 1;
    NTEMP = NTEMP + 1;
    HT_CHECK_MEMORY_ADJUST
    if(pStream->readNBits(HTPS, &PREFLEN[NTEMP]) == -1) {
        goto failed;
    }
    RANGELEN[NTEMP] = 32;
    RANGELOW[NTEMP] = HTHIGH;
    NTEMP = NTEMP + 1;
    if(HTOOB) {
        HT_CHECK_MEMORY_ADJUST
        if(pStream->readNBits(HTPS, &PREFLEN[NTEMP]) == -1) {
            goto failed;
        }
        NTEMP = NTEMP + 1;
    }
    CODES = (int*)m_pModule->JBig2_Malloc2(sizeof(int), NTEMP);
    LENMAX = 0;
    for(i = 0; i < NTEMP; i++) {
        if(PREFLEN[i] > LENMAX) {
            LENMAX = PREFLEN[i];
        }
    }
    LENCOUNT = (int*)m_pModule->JBig2_Malloc2(sizeof(int), (LENMAX + 1));
    JBIG2_memset(LENCOUNT, 0, sizeof(int) * (LENMAX + 1));
    FIRSTCODE = (int*)m_pModule->JBig2_Malloc2(sizeof(int), (LENMAX + 1));
    for(i = 0; i < NTEMP; i++) {
        LENCOUNT[PREFLEN[i]] ++;
    }
    CURLEN = 1;
    FIRSTCODE[0] = 0;
    LENCOUNT[0]  = 0;
    while(CURLEN <= LENMAX) {
        FIRSTCODE[CURLEN] = (FIRSTCODE[CURLEN - 1] + LENCOUNT[CURLEN - 1]) << 1;
        CURCODE = FIRSTCODE[CURLEN];
        CURTEMP = 0;
        while(CURTEMP < NTEMP) {
            if(PREFLEN[CURTEMP] == CURLEN) {
                CODES[CURTEMP] = CURCODE;
                CURCODE = CURCODE + 1;
            }
            CURTEMP = CURTEMP + 1;
        }
        CURLEN = CURLEN + 1;
    }
    m_pModule->JBig2_Free(LENCOUNT);
    m_pModule->JBig2_Free(FIRSTCODE);
    return TRUE;
failed:
    return FALSE;
}
