/* ***** BEGIN LICENSE BLOCK *****
 * Source last modified: $Id: imghelpr.cpp,v 1.1.24.1 2004/07/09 01:51:47 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
#include "hxtypes.h"
#include "hxwintyp.h"
#include "hxcom.h"
#include "ihxpckts.h"
#include "hxvsurf.h"
#include "hxfiles.h"
#include "hxerror.h"

// pnmisc
#include "unkimp.h"
#include "baseobj.h"

// pncont
#include "chxpckts.h"

// pxcomlib
#include "pxrect.h"
#include "pxcolor.h"
#include "pximage.h"
#include "pxrndcod.h"
#include "nestbuff.h"

// pxrend
#include "imghelpr.h"

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

BEGIN_INTERFACE_LIST(PXImageHelper)
END_INTERFACE_LIST

PXImageHelper::PXImageHelper()
{
    Reset();
    m_pCodec         = NULL;
    m_pErrorMessages = NULL;
    m_pImageInfo     = NULL;
    m_pFrameData     = NULL;
}

PXImageHelper::~PXImageHelper()
{
    Deallocate();
}

void PXImageHelper::Reset()
{
    m_ulNumFrames           = 0;
    m_cImageDim.cx          = 0;
    m_cImageDim.cy          = 0;
    m_ulHandle              = 0;
    m_ulFileLength          = 0;
    m_ulDisplayWidth        = 0;
    m_ulDisplayHeight       = 0;
    m_ulFormat              = 0;
    m_ulBitsPerPixel        = 0;
    m_bRowsInverted         = FALSE;
    m_ulNumPacketsProcessed = 0;
    m_ulFileBytesProcessed  = 0;
    m_ulSessionHandle       = 0;
    m_bDecodeFailed         = FALSE;
    m_ulOpaqueSize          = 0;
    m_bIgnoreDataPackets    = FALSE;
}

void PXImageHelper::Deallocate()
{
    if (m_pCodec && m_ulSessionHandle)
    {
        m_pCodec->FinishDecompress(m_ulSessionHandle);
    }
    HX_RELEASE(m_pCodec);
    HX_RELEASE(m_pErrorMessages);
    HX_RELEASE(m_pImageInfo);
    DeallocateImages();
}

UINT32 PXImageHelper::GetPacketBufferSize(IHXPacket* pPacket)
{
    UINT32 ulBytes = 0;

    if (pPacket)
    {
        IHXBuffer* pBuffer = pPacket->GetBuffer();
        if (pBuffer)
        {
            ulBytes = pBuffer->GetSize();
        }
        HX_RELEASE(pBuffer);
    }

    return ulBytes;
}

HX_RESULT PXImageHelper::Init(IHXErrorMessages* pErrorMessages, UINT32 ulHandle, UINT32 ulFileLength,
                              IUnknown* pCodec, UINT32 ulOpaqueSize, UINT32 ulDispWidth, UINT32 ulDispHeight,
                              UINT32 ulFormat, UINT32 ulBpp, BOOL bRowsInverted)
{
    HX_RESULT retVal = HXR_OK;

    if (ulHandle && ulFileLength && pCodec &&
        ulDispWidth && ulDispHeight)
    {
        // Deallocate and reset
        Deallocate();
        Reset();

        // Save members
        m_pErrorMessages  = pErrorMessages;
        m_pErrorMessages->AddRef();
        m_ulHandle        = ulHandle;
        m_ulFileLength    = ulFileLength;
        m_ulOpaqueSize    = ulOpaqueSize;
        m_ulDisplayWidth  = ulDispWidth;
        m_ulDisplayHeight = ulDispHeight;
        m_ulFormat        = ulFormat;
        m_ulBitsPerPixel  = ulBpp;
        m_bRowsInverted   = bRowsInverted;

        // Save a copy of the codec interface pointer
        retVal = pCodec->QueryInterface(IID_IHXRealPixRendererCodec, (void**) &m_pCodec);
    }

    if (FAILED(retVal))
    {
        Deallocate();
        Reset();
    }

    return retVal;
}

HX_RESULT PXImageHelper::OnImageDataPacket(IHXBuffer* pOpaque, IHXBuffer* pData)
{
    HX_RESULT retVal = HXR_FAIL;

    if (pOpaque && pData)
    {
        if (m_pCodec)
        {
            if (!m_ulNumPacketsProcessed)
            {
                retVal = InitHeader(pData, pOpaque);
                if (FAILED(retVal))
                {
                    m_bDecodeFailed = TRUE;
                }
            }
            else
            {
                retVal = HXR_OK;
            }

            if (SUCCEEDED(retVal))
            {
                retVal = m_pCodec->Decompress(m_ulSessionHandle, pData, pOpaque);
                if (SUCCEEDED(retVal))
                {
                    m_ulNumPacketsProcessed++;
                    m_ulFileBytesProcessed += pData->GetSize();
                    // If are either done decompressing or aborted, then 
                    // we need to run through each from and check whether
                    // or not to do an alpha channel check
                    INT32 lState = 0;
                    m_pCodec->GetDecompressStatus(m_ulSessionHandle, lState);
                    if (lState == DECOMPRESS_STATUS_FINISHED ||
                        lState == DECOMPRESS_STATUS_ABORTED)
                    {
//#define XXXMEH_DUMP_FRAMES
#if defined(XXXMEH_DUMP_FRAMES) && defined(_DEBUG)
                        if (lState == DECOMPRESS_STATUS_FINISHED)
                        {
                            for (UINT32 i = 0; i < m_ulNumFrames; i++)
                            {
                                IHXBuffer* pImgBuf = NULL;
                                m_pFrameData[i].m_pFrame->GetImageStore(&pImgBuf);
                                if (pImgBuf)
                                {
                                    UINT32* pBuf = (UINT32*) pImgBuf->GetBuffer();
                                    if (pBuf)
                                    {
                                        UINT32  ulWidth  = HXxRECT_WIDTH(m_pFrameData[i].m_cFrameDim);
                                        UINT32  ulHeight = HXxRECT_HEIGHT(m_pFrameData[i].m_cFrameDim);
                                        FILE* fp = fopen("c:\\rpimg.raw", "w");
                                        if (fp)
                                        {
                                            UINT32 ulNumPix = ulWidth * ulHeight;
                                            while (ulNumPix--)
                                            {
                                                UINT32 ulPix   = *pBuf++;
                                                BYTE   ucRed   = (ulPix & 0x00FF0000) >> 16;
                                                BYTE   ucGreen = (ulPix & 0x0000FF00) >>  8;
                                                BYTE   ucBlue  = (ulPix & 0x000000FF);
                                                BYTE   ucAlpha = 0;
                                                fwrite(&ucRed,   1, 1, fp);
                                                fwrite(&ucGreen, 1, 1, fp);
                                                fwrite(&ucBlue,  1, 1, fp);
                                                fwrite(&ucAlpha, 1, 1, fp);
                                            }
                                            fclose(fp);
                                        }
                                    }
                                }
                                HX_RELEASE(pImgBuf);
                            }
                        }
#endif
                        // We're finished, so run through and see if any of
                        // frames need an alpha channel check
                        for (UINT32 i = 0; i < m_ulNumFrames; i++)
                        {
                            if (m_pFrameData[i].m_bNeedAlphaCheck)
                            {
                                if (m_pFrameData[i].m_pFrame)
                                {
                                    m_pFrameData[i].m_pFrame->SelfDetermineHasAlpha();
                                }
                            }
                        }
                    }
                }
            }
        }
    }

    return retVal;
}

HX_RESULT PXImageHelper::GetFrame(UINT32 i, PXImage** ppFrame)
{
    HX_RESULT retVal = HXR_OK;

    if (i < m_ulNumFrames && ppFrame)
    {
        if (m_pFrameData[i].m_pFrame)
        {
            *ppFrame = m_pFrameData[i].m_pFrame;
            (*ppFrame)->AddRef();
        }
        else
        {
            retVal = HXR_UNEXPECTED;
        }
    }
    else
    {
        return HXR_INVALID_PARAMETER;
    }

    return retVal;
}

HX_RESULT PXImageHelper::GetFrameInfo(UINT32 i, IHXValues** ppFrameInfo)
{
    HX_RESULT retVal = HXR_OK;

    if (i < m_ulNumFrames && ppFrameInfo)
    {
        if (m_pFrameData[i].m_pFrameInfo)
        {
            *ppFrameInfo = m_pFrameData[i].m_pFrameInfo;
            (*ppFrameInfo)->AddRef();
        }
        else
        {
            retVal = HXR_UNEXPECTED;
        }
    }
    else
    {
        return HXR_INVALID_PARAMETER;
    }

    return retVal;
}

HX_RESULT PXImageHelper::GetFrameDim(UINT32 i, REF(HXxRect) rFrameDim)
{
    HX_RESULT retVal = HXR_OK;

    if (i < m_ulNumFrames)
    {
        if (m_pFrameData[i].m_pFrameInfo)
        {
            rFrameDim = m_pFrameData[i].m_cFrameDim;
        }
        else
        {
            retVal = HXR_UNEXPECTED;
        }
    }
    else
    {
        return HXR_INVALID_PARAMETER;
    }

    return retVal;
}

BOOL PXImageHelper::AllBytesDecoded() const
{
    BOOL bRetVal = FALSE;

    if (m_ulFileLength         >  0 &&
        m_ulFileBytesProcessed >= m_ulFileLength)
    {
        bRetVal = TRUE;
    }

    return bRetVal;
}

HX_RESULT PXImageHelper::CreateNestedBuffer(IHXBuffer* pBuffer, UINT32 ulOffset,
                                            UINT32 ulSize, IHXBuffer** ppBuffer)
{
    HX_RESULT retVal = HXR_OK;

    if (ppBuffer)
    {
        CHXNestedBuffer* pNested = NULL;
        retVal                   = CHXNestedBuffer::CreateObject(&pNested);
        if (SUCCEEDED(retVal))
        {
            pNested->AddRef();
            retVal = pNested->Init(pBuffer, ulOffset, ulSize);
            if (SUCCEEDED(retVal))
            {
                retVal = pNested->QueryInterface(IID_IHXBuffer, (void**) ppBuffer);
            }
        }
        HX_RELEASE(pNested);
    }
    else
    {
        retVal = HXR_INVALID_PARAMETER;
    }

    return retVal;
}

HX_RESULT PXImageHelper::InitHeader(IHXBuffer* pData, IHXBuffer* pOpaque)
{
    HX_RESULT retVal = HXR_OK;

    if (pData && pOpaque)
    {
        // Clear out all current images
        DeallocateImages();
        // Get the header info
        retVal = m_pCodec->GetHeaderInfo(pData, pOpaque, m_cImageDim, m_ulNumFrames,
                                         m_pImageInfo, m_ulSessionHandle);
        if (SUCCEEDED(retVal))
        {
#ifdef XXXMEH_DEBUG_LOG
            DEBUG_OUTF("c:\\realpix.log",
                       (s, "PXImageHelper::InitHeader() - allocating %lu frames\n",
                       m_ulNumFrames));
#endif
            // Allocate vector of FrameData structs
            m_pFrameData = new FrameData [m_ulNumFrames];
            if (m_pFrameData)
            {
                // NULL out the pointers
                UINT32 i;
                for (i = 0; i < m_ulNumFrames; i++)
                {
                    m_pFrameData[i].m_pFrame           = NULL;
                    m_pFrameData[i].m_pFrameInfo       = NULL;
                    m_pFrameData[i].m_cFrameDim.left   = 0;
                    m_pFrameData[i].m_cFrameDim.top    = 0;
                    m_pFrameData[i].m_cFrameDim.right  = 0;
                    m_pFrameData[i].m_cFrameDim.bottom = 0;
                    m_pFrameData[i].m_bNeedAlphaCheck  = FALSE;
                }

                // Loop through each frame
                for (i = 0; i < m_ulNumFrames; i++)
                {
                    retVal = m_pCodec->GetFrameInfo(m_ulSessionHandle,
                                                    i,
                                                    m_pFrameData[i].m_cFrameDim,
                                                    m_pFrameData[i].m_pFrameInfo);
                    if (SUCCEEDED(retVal))
                    {
                        // Create an image object for this frame
                        retVal = PXImage::CreateObject(&m_pFrameData[i].m_pFrame);
                        if (SUCCEEDED(retVal))
                        {
                            // Addref the object
                            m_pFrameData[i].m_pFrame->AddRef();
#ifdef XXXMEH_DEBUG_OUT
                            DEBUG_OUT(m_pErrorMessages,
                                      DOL_REALPIX_EXTENDED,
                                      (s, "Creating presentation image of size w=%ld, h=%ld",
                                       HXxRECT_WIDTH(m_pFrameData[i].m_cFrameDim),
                                       HXxRECT_HEIGHT(m_pFrameData[i].m_cFrameDim)));
#endif
                            retVal = m_pFrameData[i].m_pFrame->Create(HXxRECT_WIDTH(m_pFrameData[i].m_cFrameDim),
                                                                      HXxRECT_HEIGHT(m_pFrameData[i].m_cFrameDim),
                                                                      m_ulBitsPerPixel,
                                                                      m_ulFormat,
                                                                      m_bRowsInverted);
                            if (SUCCEEDED(retVal))
                            {
                                IHXBuffer* pImageStore = NULL;
                                retVal                  = m_pFrameData[i].m_pFrame->GetImageStore(&pImageStore);
                                if (SUCCEEDED(retVal))
                                {
                                    HXxSize cSize;
                                    cSize.cx = HXxRECT_WIDTH(m_pFrameData[i].m_cFrameDim);
                                    cSize.cy = HXxRECT_HEIGHT(m_pFrameData[i].m_cFrameDim);
                                    retVal = m_pCodec->SetDecompressParam(m_ulSessionHandle,
                                                                          i,
                                                                          pImageStore,
                                                                          cSize,
                                                                          m_pFrameData[i].m_pFrame->GetRowStride(),
                                                                          m_pFrameData[i].m_pFrame->GetBitsPerPixel(),
                                                                          m_pFrameData[i].m_pFrame->GetFormat(),
                                                                          m_pFrameData[i].m_pFrame->GetRowsInverted(),
                                                                          NULL);
                                    if (SUCCEEDED(retVal))
                                    {
                                        // Do we have a FrameInfo IHXValues from the codec?
                                        if (m_pFrameData[i].m_pFrameInfo)
                                        {
                                            // Do we have a "UsesAlphaChannel" ULONG32 property?
                                            ULONG32   ulFlag = 0;
                                            HX_RESULT rv     = m_pFrameData[i].m_pFrameInfo->GetPropertyULONG32("UsesAlphaChannel", ulFlag);
                                            if (SUCCEEDED(rv))
                                            {
                                                // We did have the flag, so set the member in the PXImage
                                                m_pFrameData[i].m_pFrame->SetHasAlpha((ulFlag ? TRUE : FALSE));
                                            }
                                            else
                                            {
                                                // There was no flag present, so after the image is decoded,
                                                // we'll have to self-determine it from the image bits themselves.
                                                // So here we simply need to set the flag that tells us to
                                                // perform that operation later.
                                                m_pFrameData[i].m_bNeedAlphaCheck = TRUE;
                                            }
                                        }
                                    }
                                }
                                HX_RELEASE(pImageStore);
                            }
                        }
                    }

                    if (FAILED(retVal))
                    {
                        break;
                    }
                }
            }
            else
            {
                retVal = HXR_OUTOFMEMORY;
            }
        }
    }
    else
    {
        retVal = HXR_INVALID_PARAMETER;
    }

    if (FAILED(retVal))
    {
        DeallocateImages();
    }

    return retVal;
}

void PXImageHelper::DeallocateImages()
{
    if (m_pFrameData)
    {
        for (UINT32 i = 0; i < m_ulNumFrames; i++)
        {
            HX_RELEASE(m_pFrameData[i].m_pFrame);
            HX_RELEASE(m_pFrameData[i].m_pFrameInfo);
        }
        HX_VECTOR_DELETE(m_pFrameData);
    }
}
