/* ***** BEGIN LICENSE BLOCK *****
 * Source last modified: $Id: pxcpngrn.cpp,v 1.1.26.1 2004/07/09 01:51:04 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 "hxcomm.h"
#include "ihxpckts.h"
#include "hxplugn.h"
#include "hxfiles.h"
#include "hxauth.h"
#include "hxver.h"
#include "hxvsurf.h"

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

// pncont
#include "hxslist.h"
#include "hxstring.h"
#include "hxmap.h"

// pxcomlib
#include "gstring.h"
#include "pxrndcod.h"
#include "pxutil.h"
#include "pxmapmgr.h"

// libpng
#include "png.h"

// pxpnglib
#include "pxpngdec.h"

// pxcpng
#include "pxcpngrn.h"
#include "rppngdll.ver"

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

const char*  CRealPixPNGRendererCodec::m_pszDescription       = "Helix RealPix PNG Renderer Codec Plugin";
const char*  CRealPixPNGRendererCodec::m_pszCopyright         = HXVER_COPYRIGHT;
const char*  CRealPixPNGRendererCodec::m_pszMoreInfoURL       = HXVER_MOREINFO;
const char*  CRealPixPNGRendererCodec::m_ppszFileMimeType[]   = {"image/vnd.rn-realpix.png", NULL};
const UINT32 CRealPixPNGRendererCodec::m_ulHighestSupportedContentVersion = HX_ENCODE_PROD_VERSION(0, 0, 0, 0);
const UINT32 CRealPixPNGRendererCodec::m_ulHighestSupportedStreamVersion  = HX_ENCODE_PROD_VERSION(0, 0, 0, 0);

CRealPixPNGRendererCodec::CRealPixPNGRendererCodec()
{
    m_lRefCount           = 0;
    m_pContext            = NULL;
    m_pCommonClassFactory = NULL;
    m_pMapManager         = NULL;
}

CRealPixPNGRendererCodec::~CRealPixPNGRendererCodec()
{
    ReleaseAllDecodeObjects();
    HX_RELEASE(m_pContext);
    HX_RELEASE(m_pCommonClassFactory);
    HX_RELEASE(m_pMapManager);
}


STDMETHODIMP CRealPixPNGRendererCodec::QueryInterface(REFIID riid, void** ppvObj)
{
    HX_RESULT retVal = HXR_OK;

    if (ppvObj)
    {
        // Set default
        *ppvObj = NULL;
        // Check for IID type
        if (IsEqualIID(riid, IID_IUnknown))
        {
            AddRef();
            *ppvObj = (IUnknown*) (IHXPlugin*) this;
        }
        else if (IsEqualIID(riid, IID_IHXPlugin))
        {
            AddRef();
            *ppvObj = (IHXPlugin*) this;
        }
        else if (IsEqualIID(riid, IID_IHXRealPixRendererCodec))
        {
            AddRef();
            *ppvObj = (IHXRealPixRendererCodec*) this;
        }
        else
        {
            retVal = HXR_NOINTERFACE;
        }
    }
    else
    {
        retVal = HXR_FAIL;
    }

    return retVal;
}

STDMETHODIMP_(UINT32) CRealPixPNGRendererCodec::AddRef()
{
    return InterlockedIncrement(&m_lRefCount);
}


STDMETHODIMP_(UINT32) CRealPixPNGRendererCodec::Release()
{
    if (InterlockedDecrement(&m_lRefCount) > 0)
    {
        return m_lRefCount;
    }

    delete this;

    return 0;
}

STDMETHODIMP CRealPixPNGRendererCodec::GetPluginInfo(REF(BOOL)        bMultipleLoad,
                                                     REF(const char*) pDescription,
                                                     REF(const char*) pCopyright,
                                                     REF(const char*) pMoreInfoURL,
                                                     REF(ULONG32)     ulVersionNumber)
{
    bMultipleLoad   = TRUE;
    pDescription    = m_pszDescription;
    pCopyright      = m_pszCopyright;
    pMoreInfoURL    = m_pszMoreInfoURL;
    ulVersionNumber = TARVER_ULONG32_VERSION;

    return HXR_OK;
}

STDMETHODIMP CRealPixPNGRendererCodec::InitPlugin(IUnknown* pContext)
{
    HX_RESULT retVal = HXR_OK;

    if (pContext)
    {
        // Save a copy of the context
        HX_RELEASE(m_pContext);
        m_pContext = pContext;
        m_pContext->AddRef();

        HX_RELEASE(m_pCommonClassFactory);
        retVal = m_pContext->QueryInterface(IID_IHXCommonClassFactory,
                                            (void**) &m_pCommonClassFactory);
        if (SUCCEEDED(retVal))
        {
            HX_RELEASE(m_pMapManager);
            m_pMapManager = new PXMapManager();
            if (m_pMapManager)
            {
                m_pMapManager->AddRef();
            }
            else
            {
                retVal = HXR_OUTOFMEMORY;
            }
        }
    }
    else
    {
        retVal = HXR_INVALID_PARAMETER;
    }

    return retVal;
}

STDMETHODIMP CRealPixPNGRendererCodec::GetRendererCodecInfo(REF(const char**) rppszFileMimeType,
                                                            REF(UINT32)       rulHighestSupportedContentVersion,
                                                            REF(UINT32)       rulHighestSupportedStreamVersion)
{
    rppszFileMimeType                 = m_ppszFileMimeType;
    rulHighestSupportedContentVersion = m_ulHighestSupportedContentVersion;
    rulHighestSupportedStreamVersion  = m_ulHighestSupportedStreamVersion;

    return HXR_OK;
}

STDMETHODIMP CRealPixPNGRendererCodec::ValidInputData(IHXBuffer* pBuffer, REF(BOOL) rbValid)
{
    rbValid = PXPNGDecode::ValidInputData(pBuffer);

    return HXR_OK;
}

STDMETHODIMP CRealPixPNGRendererCodec::GetHeaderInfo(IHXBuffer*      pBuffer,
                                                     IHXBuffer*      pOpaqueImageData,
                                                     REF(HXxSize)     rImageDim,
                                                     REF(UINT32)      rulNumFrames,
                                                     REF(IHXValues*) rpImageInfo,
                                                     REF(UINT32)      rulSessionHandle)
{
    HX_RESULT retVal = HXR_OK;

    if (pBuffer && m_pMapManager)
    {
        // Set defaults
        rImageDim.cx = 0;
        rImageDim.cy = 0;
        rulNumFrames = 1;
        rpImageInfo  = NULL;
        // Create a PXPNGDecode object
        PXPNGDecode* pDecode = new PXPNGDecode();
        if (pDecode)
        {
            // AddRef the object
            pDecode->AddRef();
            // Initialize the object
            retVal = pDecode->Init(m_pContext, pBuffer, TRUE);
            if (SUCCEEDED(retVal))
            {
                // Initialize the last sequence number
                pDecode->SetLastSeqNum((UINT32) 0xFFFFFFFF);
                // Initialize the valid flag
                pDecode->SetValid(TRUE);
                // Set image dimensions
                rImageDim.cx = (INT32) pDecode->GetWidth();
                rImageDim.cy = (INT32) pDecode->GetHeight();
                // Add it to the map
                retVal = m_pMapManager->AddEntry((void*) pDecode, rulSessionHandle);
                if (SUCCEEDED(retVal))
                {
                    // AddRef it since it's in the map now
                    pDecode->AddRef();
                }
            }
        }
        else
        {
            retVal = HXR_OUTOFMEMORY;
        }
        HX_RELEASE(pDecode);
    }
    else
    {
        retVal = HXR_FAIL;
    }

    return retVal;
}

STDMETHODIMP CRealPixPNGRendererCodec::GetFrameInfo(UINT32           ulSessionHandle,
                                                    UINT32           ulFrameNum,
                                                    REF(HXxRect)     rFrameDim,
                                                    REF(IHXValues*) rpMoreInfo)
{
    HX_RESULT retVal = HXR_FAIL;

    if (ulSessionHandle && !ulFrameNum && m_pMapManager)
    {
        // Set defaults
        rFrameDim.left   = 0;
        rFrameDim.top    = 0;
        rFrameDim.right  = 0;
        rFrameDim.bottom = 0;
        rpMoreInfo       = NULL;
        // Get the PXPNGDecode object
        PXPNGDecode* pDecode = NULL;
        retVal               = m_pMapManager->GetEntry(ulSessionHandle, (void**) &pDecode);
        if (SUCCEEDED(retVal))
        {
            // Create an IHXValues object
            IHXValues* pInfo = NULL;
            retVal            = m_pCommonClassFactory->CreateInstance(CLSID_IHXValues,
                                                                      (void**) &pInfo);
            if (SUCCEEDED(retVal))
            {
                // Set the "UsesAlphaChannel" property
                pInfo->SetPropertyULONG32("UsesAlphaChannel", (pDecode->UsesAlphaChannel() ? 1 : 0));
                // Set the out parameters
                rFrameDim.right  = pDecode->GetWidth();
                rFrameDim.bottom = pDecode->GetHeight();
                rpMoreInfo       = pInfo;
                rpMoreInfo->AddRef();
            }
            HX_RELEASE(pInfo);
        }
    }

    return retVal;
}

STDMETHODIMP CRealPixPNGRendererCodec::SetDecompressParam(UINT32      ulSessionHandle,
                                                          UINT32      ulFrameNum,
                                                          IHXBuffer* pOutputBuffer,
                                                          HXxSize     cFrameDim,
                                                          UINT32      ulRowStride,
                                                          UINT32      ulBitsPerPixel,
                                                          UINT32      ulColorFormat,
                                                          BOOL        bRowsInverted,
                                                          IHXValues* pMoreParam)
{
    HX_RESULT retVal = HXR_FAIL;

    if (ulFrameNum     == 0  && pOutputBuffer &&
        ulBitsPerPixel == 32 && ulColorFormat == HX_RGB &&
        m_pMapManager)
    {
        PXPNGDecode* pDecode = NULL;
        retVal               = m_pMapManager->GetEntry(ulSessionHandle, (void**) &pDecode);
        if (SUCCEEDED(retVal))
        {
            BOOL bTransparentIsZero = FALSE;
            
            if( pMoreParam )
            {
            	ULONG32 valueProperty;
            	if( SUCCEEDED( pMoreParam->GetPropertyULONG32( "TransparentIsZero", valueProperty ) ) )
            	{
            		bTransparentIsZero = ( valueProperty != 0 );
            	}
            }
            
            // Make sure that we are decoding to 1/1
            if (cFrameDim.cx == (INT32) pDecode->GetWidth() &&
                cFrameDim.cy == (INT32) pDecode->GetHeight())
            {
                retVal = pDecode->SetDecompressParam(pOutputBuffer,
                                                     (UINT32) cFrameDim.cx,
                                                     (UINT32) cFrameDim.cy,
                                                     ulRowStride,
                                                     ulBitsPerPixel,
                                                     ulColorFormat,
                                                     bRowsInverted,
                                                     bTransparentIsZero);
            }
            else
            {
                retVal = HXR_INVALID_PARAMETER;
            }
        }
    }

    return retVal;
}

STDMETHODIMP CRealPixPNGRendererCodec::Decompress(UINT32      ulSessionHandle,
                                                  IHXBuffer* pBuffer,
                                                  IHXBuffer* pOpaquePacketData)
{
    HX_RESULT retVal = HXR_OK;

    if (ulSessionHandle && pBuffer && m_pMapManager)
    {
        PXPNGDecode* pDecode = NULL;
        retVal               = m_pMapManager->GetEntry(ulSessionHandle, (void**) &pDecode);
        if (SUCCEEDED(retVal))
        {
            // If the object has been labelled invalid, that means that we lost the
            // header packet - therefore we don't need to do any of this stuff.
            if (pDecode->GetValid())
            {
                if (pOpaquePacketData)
                {
                    BYTE* pBuf = pOpaquePacketData->GetBuffer();
                    if (pBuf)
                    {
                        // Unpack the sequence number from the opaque data
                        UINT32 ulSeqNum = 0;
                        UnPack32(pBuf, ulSeqNum);
                        // Check seq num
                        if (ulSeqNum != pDecode->GetLastSeqNum() + 1)
                        {
                            // All we can do is stop decoding
                            pDecode->SetValid(FALSE);
                        }
                        else
                        {
                            pDecode->SetLastSeqNum(ulSeqNum);
                        }
                    }
                }

                if (pDecode->GetValid())
                {
                    // Now call decompress for the object
                    retVal = pDecode->Decompress(pBuffer);
                }
            }
        }
    }
    else
    {
        retVal = HXR_INVALID_PARAMETER;
    }

    return retVal;
}

STDMETHODIMP CRealPixPNGRendererCodec::GetDecompressStatus(UINT32     ulSessionHandle,
                                                           REF(INT32) rlStatus)
{
    HX_RESULT retVal = HXR_FAIL;

    if (m_pMapManager)
    {
        PXPNGDecode* pDecode = NULL;
        retVal               = m_pMapManager->GetEntry(ulSessionHandle, (void**) &pDecode);
        if (SUCCEEDED(retVal))
        {
            if (!pDecode->GetValid())
            {
                rlStatus = DECOMPRESS_STATUS_ABORTED;
            }
            else if (pDecode->IsFinished())
            {
                rlStatus = DECOMPRESS_STATUS_FINISHED;
            }
            else
            {
                rlStatus = DECOMPRESS_STATUS_INPROGRESS;
            }
        }
    }

    return retVal;
}

STDMETHODIMP CRealPixPNGRendererCodec::FinishDecompress(UINT32 ulSessionHandle)
{
    HX_RESULT retVal = HXR_FAIL;

    if (m_pMapManager)
    {
        PXPNGDecode* pDecode = NULL;
        retVal               = m_pMapManager->DeleteEntry(ulSessionHandle, (void**) &pDecode);
        HX_RELEASE(pDecode);
    }

    return retVal;
}

HX_RESULT STDAPICALLTYPE CRealPixPNGRendererCodec::HXCreateInstance(IUnknown** ppIUnknown)
{
    HX_RESULT retVal = HXR_OK;

    if (ppIUnknown)
    {
        // Create renderer codec
        CRealPixPNGRendererCodec* pCodec = new CRealPixPNGRendererCodec();
        if (pCodec)
        {
            // QI for IUnknown
            retVal = pCodec->QueryInterface(IID_IUnknown, (void**) ppIUnknown);
        }
        else
        {
            retVal = HXR_OUTOFMEMORY;
        }
        if (FAILED(retVal))
        {
            HX_DELETE(pCodec);
        }
    }
    else
    {
        retVal = HXR_FAIL;
    }

    return retVal;
}

void CRealPixPNGRendererCodec::ReleaseAllDecodeObjects()
{
    if (m_pMapManager)
    {
        UINT32       ulHandle = 0;
        PXPNGDecode* pDecode = NULL;
        HX_RESULT    retVal   = m_pMapManager->GetFirstEntry(ulHandle, (void**) &pDecode);
        while (SUCCEEDED(retVal))
        {
            HX_RELEASE(pDecode);
            retVal = m_pMapManager->GetNextEntry(ulHandle, (void**) &pDecode);
        }
        m_pMapManager->DeleteAllEntries();
    }
}

