/* ***** BEGIN LICENSE BLOCK *****
 * Source last modified: $Id: lzw.cpp,v 1.2.24.1 2004/07/09 01:54:29 hubbe Exp $
 * 
 * Portions Copyright (c) 1995-2004 RealNetworks, Inc. All Rights Reserved.
 * 
 * The contents of this file, and the files included with this file,
 * are subject to the current version of the RealNetworks Public
 * Source License (the "RPSL") available at
 * http://www.helixcommunity.org/content/rpsl unless you have licensed
 * the file under the current version of the RealNetworks Community
 * Source License (the "RCSL") available at
 * http://www.helixcommunity.org/content/rcsl, in which case the RCSL
 * will apply. You may also obtain the license terms directly from
 * RealNetworks.  You may not use this file except in compliance with
 * the RPSL or, if you have a valid RCSL with RealNetworks applicable
 * to this file, the RCSL.  Please see the applicable RPSL or RCSL for
 * the rights, obligations and limitations governing use of the
 * contents of the file.
 * 
 * Alternatively, the contents of this file may be used under the
 * terms of the GNU General Public License Version 2 or later (the
 * "GPL") in which case the provisions of the GPL are applicable
 * instead of those above. If you wish to allow use of your version of
 * this file only under the terms of the GPL, and not to allow others
 * to use your version of this file under the terms of either the RPSL
 * or RCSL, indicate your decision by deleting the provisions above
 * and replace them with the notice and other provisions required by
 * the GPL. If you do not delete the provisions above, a recipient may
 * use your version of this file under the terms of any one of the
 * RPSL, the RCSL or the GPL.
 * 
 * This file is part of the Helix DNA Technology. RealNetworks is the
 * developer of the Original Code and owns the copyrights in the
 * portions it created.
 * 
 * This file, and the files included with this file, is distributed
 * and made available on an 'AS IS' basis, WITHOUT WARRANTY OF ANY
 * KIND, EITHER EXPRESS OR IMPLIED, AND REALNETWORKS HEREBY DISCLAIMS
 * ALL SUCH WARRANTIES, INCLUDING WITHOUT LIMITATION, ANY WARRANTIES
 * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, QUIET
 * ENJOYMENT OR NON-INFRINGEMENT.
 * 
 * Technology Compatibility Kit Test Suite(s) Location:
 *    http://www.helixcommunity.org/content/tck
 * 
 * Contributor(s):
 * 
 * ***** END LICENSE BLOCK ***** */

#include <hlxclib/memory.h>
#include <string.h>

#include "lzw.h"

#include "hxheap.h"
#ifdef _DEBUG
#undef HX_THIS_FILE		
static char HX_THIS_FILE[] = __FILE__;
#endif

LZWCodec::LZWCodec()
{
    /* Initialize all member variables */
    m_bFresh                   = TRUE;
    m_bPostClearCode           = FALSE;
    m_lCodeSize                = 0;
    m_lSetCodeSize             = 0;
    m_lMaxCode                 = 0;
    m_lMaxCodeSize             = 0;
    m_lFirstCode               = 0;
    m_lOldCode                 = 0;
    m_lClearCode               = 0;
    m_lEndCode                 = 0;
    m_pCurByte                 = NULL;
    m_lCurBit                  = 0;
    m_lBytesInBuffer           = -1;
    m_bFinished                = FALSE;
    m_bSuspended               = TRUE;
    m_bAllCompressedDataCopied = FALSE;
    m_pCompressedBuffer        = NULL;
    m_lCompressedBufferSize    = 0;
    m_lCompressedBytesCopied   = 0;

    /* Clear the tables */
    ClearTables();

    /* Reset the stack */
    m_plStackPtr = m_lStack;
};

LZWCodec::~LZWCodec()
{
    if (m_pCompressedBuffer)
    {
        delete [] m_pCompressedBuffer;
        m_pCompressedBuffer = NULL;
    }
};

HX_RESULT LZWCodec::SetCompressedBufferSize(INT32 lSize)
{
    /* Check for input error conditions */
    if (lSize < 1)
    {
        return HXR_INVALID_PARAMETER;
    }

    /* Allocate space for the compressed buffer */
    if (m_pCompressedBuffer)
    {
        delete [] m_pCompressedBuffer;
        m_pCompressedBuffer = NULL;
    }
    m_pCompressedBuffer = new BYTE [lSize];
    if (!m_pCompressedBuffer)
    {
        return HXR_OUTOFMEMORY;
    }

    /* Set the size */
    m_lCompressedBufferSize  = lSize;
    m_lCompressedBytesCopied = 0;

    /* Initialize the state variables */
    m_pCurByte                 = m_pCompressedBuffer;
    m_lBytesInBuffer           = -1;
    m_bAllCompressedDataCopied = FALSE;
    m_bSuspended               = TRUE;

    return HXR_OK;
}

HX_RESULT LZWCodec::InitDecompress(INT32 lMinCodeBits)
{
    /* Check for input error conditions */
    if (lMinCodeBits < 1 || lMinCodeBits >  kMaxLZWBits)
    {
        return HXR_INVALID_PARAMETER;
    }

    /* Set up code size parameters */
    m_lSetCodeSize = lMinCodeBits;
    m_lCodeSize    = m_lSetCodeSize + 1;
    m_lClearCode   = 1 << m_lSetCodeSize;
    m_lEndCode     = m_lClearCode + 1;
    m_lMaxCodeSize = m_lClearCode << 1;
    m_lMaxCode     = m_lClearCode + 2;

    /* This says to look for clear codes */
    m_bFresh = TRUE;

    /* Clear out all table entries */
    ClearTables();

    /* Reset the stack pointer */
    m_plStackPtr = m_lStack;

    return HXR_OK;
}

HX_RESULT LZWCodec::AppendCompressedBuffer(BYTE *pBuffer, INT32 lBufLen)
{
    /* Check for input error conditions */
    if (pBuffer == NULL || lBufLen < 1)
    {
        return HXR_INVALID_PARAMETER;
    }

    /* Make sure we're not called after we should be */
    if (m_bAllCompressedDataCopied == TRUE)
    {
        return HXR_OK;
    }

    /* Make sure we don't copy off the end of the buffer */
    INT32 lBytesToCopy;
    if (m_lCompressedBytesCopied + lBufLen <= m_lCompressedBufferSize)
    {
        lBytesToCopy = lBufLen;
    }
    else
    {
        lBytesToCopy = m_lCompressedBufferSize - m_lCompressedBytesCopied;
    }

    /* Do the copy */
    memcpy(&m_pCompressedBuffer[m_lCompressedBytesCopied], pBuffer, lBytesToCopy); /* Flawfinder: ignore */

    /* Update the member variables */
    m_lCompressedBytesCopied += lBufLen;
    m_lBytesInBuffer         += lBufLen;
    m_bSuspended              = FALSE;

    /* Check if we've received all data */
    if (m_lCompressedBytesCopied >= m_lCompressedBufferSize)
    {
        m_bAllCompressedDataCopied = TRUE;
    }

    return HXR_OK;
}

HX_RESULT LZWCodec::LZWReadByte(INT32 &rlSymbol)
{
    /* Are we still suspended? */
    if (m_bSuspended == TRUE)
    {
        rlSymbol = -1;
        return HXR_OK;
    }

    INT32 lCode;

    /* If this flag is set, it means skip all clear codes at the current point */
    if (m_bFresh == TRUE)
    {
        do
        {
            lCode = GetCode();
            if (lCode < 0)
            {
                rlSymbol     = -1;
                m_bSuspended = TRUE;
                return HXR_OK;
            }
            m_lFirstCode = m_lOldCode = lCode;
        }
        while (m_lFirstCode == m_lClearCode);

        m_bFresh = FALSE;
        rlSymbol = m_lFirstCode;
        return HXR_OK;
    }


    /* If we're currently copying off the stack, then return the stack values */
    if (m_plStackPtr > m_lStack)
    {
        rlSymbol = *--m_plStackPtr;
        return HXR_OK;
    }

    /* Read values until we're done or we suspend */
    while (1)
    {
        /* Get a code */
        lCode = GetCode();
        if (lCode < 0)
        {
            rlSymbol     = -1;
            m_bSuspended = TRUE;
            return HXR_OK;
        }

        /* Is this a code just after a clear code? */
        if (m_bPostClearCode == TRUE)
        {
            m_bPostClearCode = FALSE;
            m_lFirstCode     = m_lOldCode = rlSymbol = lCode;
            return HXR_OK;
        }

        /* We got a valid code, so now take action based on that code */
        if (lCode == m_lClearCode)
        {
            /* We got a clear code, so we clear the tables and reset everything */
            ClearTables();

            m_lCodeSize    = m_lSetCodeSize + 1;
            m_lMaxCodeSize = m_lClearCode << 1;
            m_lMaxCode     = m_lClearCode + 2;
            m_plStackPtr   = m_lStack;

            /* Set the flag which says we just got a clear code */
            m_bPostClearCode = TRUE;
        }
        else if (lCode == m_lEndCode)
        {
            rlSymbol     = lCode;
            m_bFinished  = TRUE;
            m_bSuspended = FALSE;
            return HXR_OK;
        }
        else
        {
            INT32 lInCode = lCode;

            if (lCode >= m_lMaxCode)
            {
                *m_plStackPtr++ = m_lFirstCode;
                lCode           = m_lOldCode;
            }

            INT32* pStackLimit = &m_lStack[0] + (kMaxLZWVal << 1);
            while (lCode >= m_lClearCode)
            {
                if (m_plStackPtr >= pStackLimit)
                {
                    // We've blown our code stack, so we have a corrupt
                    // GIF image. In this case, we need to error out.
                    return HXR_FAIL;
                }
                *m_plStackPtr++ = m_lTable[1][lCode];

                if (lCode == m_lTable[0][lCode])
                {
                    /* Tables have a circular reference in them - corrupt file */
                    return HXR_FAILED;
                }

                lCode = m_lTable[0][lCode];
            }

            *m_plStackPtr++ = m_lFirstCode = m_lTable[1][lCode];

            lCode = m_lMaxCode;

            if (lCode < kMaxLZWVal)
            {
                m_lTable[0][lCode] = m_lOldCode;
                m_lTable[1][lCode] = m_lFirstCode;
                m_lMaxCode++;

                if (m_lMaxCode     >= m_lMaxCodeSize &&
                    m_lMaxCodeSize <  kMaxLZWVal)
                {
                    m_lMaxCodeSize <<= 1;
                    m_lCodeSize++;
                }
            }

            m_lOldCode = lInCode;

            if (m_plStackPtr > m_lStack)
            {
                rlSymbol = *--m_plStackPtr;
                return HXR_OK;
            }
        }
    }

    rlSymbol = 0;

    return HXR_OK;
}

