/* ***** BEGIN LICENSE BLOCK *****
 * Source last modified: $Id: wbmprend.cpp,v 1.3.16.1 2004/07/09 01:52:55 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 "hxfiles.h"
#include "hxcore.h"
#include "hxrendr.h"
#include "hxhyper.h"
#include "hxplugn.h"
#include "hxwin.h"
#include "hxasm.h"
#include "hxevent.h"
#include "hxvsurf.h"
#include "hxver.h"
#include "hxupgrd.h"
#include "hxengin.h"
#include "hxmon.h"
#include "hxprefs.h"
#include "hxerror.h"
#include "baseobj.h"
#include "baserend.h"
#include "vbasernd.h"
#include "debugout.h"
#include "hxassert.h"
#include "pximage.h"
#include "wbmphdr.h"

#include "wbmprend.h"
#include "wbmprend.ver"

#ifdef _AIX
#include "dllpath.h"
ENABLE_MULTILOAD_DLLACCESS_PATHS(wbmprend);
#endif

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


// Define whether we're row inverted or not
#if defined(_WINDOWS)
#define ROWS_INVERTED TRUE
#else
#define ROWS_INVERTED FALSE
#endif

const char* const CWBMPRenderer::m_pszName        = "WBMP";
const char* const CWBMPRenderer::m_pszDescription = "Helix Wireless BMP Renderer Plugin";
const char* const CWBMPRenderer::m_ppszMimeType[] = {"application/vnd.rn-wbmpstream", NULL};

CWBMPRenderer::CWBMPRenderer() : CRNVisualBaseRenderer()
{
    m_ulImageWidth  = 0;
    m_ulImageHeight = 0;
    m_pDisplayImage = NULL;
};

CWBMPRenderer::~CWBMPRenderer()
{
    HX_RELEASE(m_pDisplayImage);
};

STDMETHODIMP CWBMPRenderer::QueryInterface(REFIID riid, void** ppvObj)
{
    // If we ever have any interface we need to add
    // which are specific to the WBMP renderer, then
    // we would add them here. Until then, we just
    // proxy CRNVisualBaseRenderer.
    return CRNVisualBaseRenderer::QueryInterface(riid, ppvObj);
}

STDMETHODIMP_(UINT32) CWBMPRenderer::AddRef()
{
    return CRNVisualBaseRenderer::AddRef();
}


STDMETHODIMP_(UINT32) CWBMPRenderer::Release()
{
    return CRNVisualBaseRenderer::Release();
}

STDMETHODIMP CWBMPRenderer::OnHeader(IHXValues* pHeader)
{
    HX_RESULT retVal = HXR_FAIL;

    if (pHeader)
    {
        // Check presentation stream and content versions
        retVal = CheckStreamVersions(pHeader);
        if (SUCCEEDED(retVal))
        {
            // Get the opaque data from the stream header
            IHXBuffer* pOpaque = NULL;
            retVal              = pHeader->GetPropertyBuffer("OpaqueData", pOpaque);
            if (SUCCEEDED(retVal))
            {
                // Unpack the stream header
                UINT32 ulHdrSize = 0;
                retVal = ParseWBMPHeader((BYTE*) pOpaque->GetBuffer(),
                                         pOpaque->GetSize(),
                                         m_ulImageWidth,
                                         m_ulImageHeight,
                                         ulHdrSize);
                if (SUCCEEDED(retVal))
                {
                    // Create the display image
                    HX_RELEASE(m_pDisplayImage);
                    retVal = PXImage::CreateObject(&m_pDisplayImage);
                    if (SUCCEEDED(retVal))
                    {
                        // AddRef the object
                        m_pDisplayImage->AddRef();
                        // Init the display image
                        retVal = m_pDisplayImage->Create((INT32) m_ulImageWidth,  // width
                                                         (INT32) m_ulImageHeight, // height
                                                         32,                      // bpp
                                                         HX_RGB,                  // color format
                                                         ROWS_INVERTED,           // row inversion
                                                         TRUE);                   // always allocate
                        if (SUCCEEDED(retVal))
                        {
                            // Fill it initially with the background color
                            m_pDisplayImage->Fill32(0x00000000);
                        }
                    }
                }
            }
            HX_RELEASE(pOpaque);
        }
        else
        {
            AddMimeToUpgradeCollection(m_ppszMimeType[0]);
        }
    }

    return retVal;
}


STDMETHODIMP CWBMPRenderer::OnPacketNoOffset(IHXPacket* pPacket)
{
    HX_RESULT retVal = HXR_OK;

    if (pPacket && m_pDisplayImage)
    {
        IHXBuffer* pBuffer = pPacket->GetBuffer();
        if (pBuffer)
        {
            BYTE*  pBuf   = (BYTE*) pBuffer->GetBuffer();
            UINT32 ulSize = pBuffer->GetSize();
            if (pBuf && ulSize >= 4)
            {
                // Unpack the row number
                UINT32 ulRow = (pBuf[0] << 24) |
                               (pBuf[1] << 16) |
                               (pBuf[2] <<  8) |
                                pBuf[3];
                // Determine the number of rows in this buffer
                UINT32 ulBytesPerRow  = (m_ulImageWidth + 7) >> 3;
                if (ulBytesPerRow)
                {
                    // Compute the number of rows in this packet
                    UINT32 ulRowsInBuffer = (ulSize - 4) / ulBytesPerRow;
                    // Loop through the rows, unpacking into the display
                    for (UINT32 i = 0; i < ulRowsInBuffer; i++)
                    {
                        // Get the pointer to the beginning of the 1bpp row
                        BYTE* pPix1 = pBuf + 4 + i * ulBytesPerRow;
                        // Get the pointer to the beginning of the row
                        UINT32* pPix32 = (UINT32*) m_pDisplayImage->GetPixel(0, ulRow + i);
                        if (pPix32)
                        {
                            for (UINT32 j = 0; j < m_ulImageWidth; j++, pPix32++)
                            {
                                // We blacked out the whole image when we
                                // created it, so we just need to check if
                                // each pixel bit is set, and if it is, then
                                // set the corresponding 32bpp pixel to white.
                                if (pPix1[j >> 3] & (1 << (7 - (j & 0x07))))
                                {
                                    *pPix32 = MAKE_RGB32(255, 255, 255);
                                }
                            }
                        }
                    }
                }
            }
        }
        HX_RELEASE(pBuffer);
    }

    return retVal;
}

STDMETHODIMP CWBMPRenderer::OnTimeSyncOffset(UINT32 ulTime)
{
    // We should force a redraw ONLY on the first time sync after the image
    // has finished decoding
    if (m_bFirstDraw)
    {
        // Redraw our data by damaging the entire area of our data
        HXxSize size;
        m_pSite->GetSize(size);
        HXxRect damageRect = {0, 0, size.cx, size.cy};
        m_pSite->DamageRect(damageRect);
        m_pSite->ForceRedraw();
        // Clear the first draw flag
        m_bFirstDraw = FALSE;
    }

    return HXR_OK;
}

STDMETHODIMP CWBMPRenderer::GetWindowSize(REF(HXxSize) rSize)
{
    rSize.cx = m_ulImageWidth;
    rSize.cy = m_ulImageHeight;
    return HXR_OK;
}

STDMETHODIMP CWBMPRenderer::IsMouseOverActiveLink(INT16 x, INT16 y, REF(BOOL) rbActive, REF(IHXBuffer*) rpLink)
{
    HX_RESULT retVal = HXR_OK;

    // We are NOT over a link
    rbActive = FALSE;

    return retVal;
}

STDMETHODIMP CWBMPRenderer::RMASurfaceUpdate(IHXVideoSurface* pSurface)
{
    if (pSurface && m_pSite && m_pDisplayImage)
    {
        // Get the size of the site
        HXxSize size;
        m_pSite->GetSize(size);
        // Set up the dst rect
        HXxRect rDestRect = { 0, 0, size.cx, size.cy};
        // Set up the src rect
        HXxRect rSrcRect  = { 0,
                              0,
                              m_pDisplayImage->GetWidth(),
                              m_pDisplayImage->GetHeight() };
        // Set the values in the bitmap info header
        HXBitmapInfoHeader cHeader;
        cHeader.biSize          = 40;
        cHeader.biWidth         = m_pDisplayImage->GetWidth();
        cHeader.biHeight        = m_pDisplayImage->GetHeight();
        cHeader.biPlanes        = 1;
        cHeader.biBitCount      = 32;
        cHeader.biCompression   = HX_RGB;
        cHeader.biSizeImage     = 0;
        cHeader.biXPelsPerMeter = 0;
        cHeader.biYPelsPerMeter = 0;
        cHeader.biClrUsed       = 0;
        cHeader.biClrImportant  = 0;
        cHeader.rcolor          = 0;
        cHeader.gcolor          = 0;
        cHeader.bcolor          = 0;
        // Get the image store
        IHXBuffer* pBuffer = NULL;
        m_pDisplayImage->GetImageStore(&pBuffer);
        if (pBuffer)
        {
            // Blit to the video surface
            pSurface->Blt(pBuffer->GetBuffer(),
                          &cHeader,
                          rDestRect,
                          rSrcRect);
        }
        HX_RELEASE(pBuffer);
    }

    return HXR_OK;
}

STDMETHODIMP CWBMPRenderer::HandleClick(INT16 x, INT16 y)
{
    return HXR_OK;
}

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

    if (ppIUnknown)
    {
        // Set default
        *ppIUnknown = NULL;
        // Create the object
        CWBMPRenderer *pObj = new CWBMPRenderer();
        if (pObj)
        {
            // QI for IUnknown
            retVal = pObj->QueryInterface(IID_IUnknown, (void**) ppIUnknown);
        }
        else
        {
            retVal = HXR_OUTOFMEMORY;
        }
        if (FAILED(retVal))
        {
            HX_DELETE(pObj);
        }
    }
    else
    {
        retVal = HXR_FAIL;
    }

    return HXR_OK;
}

HX_RESULT CWBMPRenderer::RMASurfaceUpdate2(IHXSubRectVideoSurface* pSurface,
                                           HXxRect*                 pExtents,
                                           HXxBoxRegion*              pDirtyRegion )
{
    if (pSurface && m_pSite && m_pDisplayImage)
    {
        // Get the size of the site
        HXxSize size;
        m_pSite->GetSize(size);
        
        //Scale dirty rects.
        float fx = (float)m_pDisplayImage->GetWidth()/(float)size.cx;
        float fy = (float)m_pDisplayImage->GetHeight()/(float)size.cy;

        //Go through each rect in the dirty region and scale it to 
        //generate the src rects.
        HXBOX* pSrcRects = new HXBOX[pDirtyRegion->numRects];
        for( int i=0 ; i<pDirtyRegion->numRects; i++ )
        {
            pSrcRects[i].x1 = (float)pDirtyRegion->rects[i].x1*fx+.5;
            pSrcRects[i].x2 = (float)pDirtyRegion->rects[i].x2*fx+.5;
            pSrcRects[i].y1 = (float)pDirtyRegion->rects[i].y1*fy+.5;
            pSrcRects[i].y2 = (float)pDirtyRegion->rects[i].y2*fy+.5;
        }

        //Set up Src region.
        HXxBoxRegion srcRegion;
        srcRegion.numRects = pDirtyRegion->numRects;
        srcRegion.rects    = pSrcRects;
        
        // Set the values in the bitmap info header
        HXBitmapInfoHeader cHeader;
        cHeader.biSize          = 40;
        cHeader.biWidth         = m_pDisplayImage->GetWidth();
        cHeader.biHeight        = m_pDisplayImage->GetHeight();
        cHeader.biPlanes        = 1;
        cHeader.biBitCount      = 32;
        cHeader.biCompression   = HX_RGB;
        cHeader.biSizeImage     = 0;
        cHeader.biXPelsPerMeter = 0;
        cHeader.biYPelsPerMeter = 0;
        cHeader.biClrUsed       = 0;
        cHeader.biClrImportant  = 0;
        cHeader.rcolor          = 0;
        cHeader.gcolor          = 0;
        cHeader.bcolor          = 0;
        
        // Get the image store
        IHXBuffer* pBuffer = NULL;
        m_pDisplayImage->GetImageStore(&pBuffer);
        if (pBuffer)
        {
            // Blit to the video surface
            pSurface->BltSubRects(pBuffer->GetBuffer(),
                                  &cHeader,
                                  pDirtyRegion,
                                  &srcRegion, 1.0/fx, 1.0/fy);
        }
        HX_RELEASE(pBuffer);

        HX_VECTOR_DELETE(pSrcRects);
    }
    return HXR_OK;
}

void CWBMPRenderer::_AttachSite()
{
    //Lets subscribe to the sub rect messages, HX_SURFACE_UPDATE2.
    IHXSubRectSite* pSubRectSite = NULL;
    m_pSite->QueryInterface(IID_IHXSubRectSite, (void**)&pSubRectSite);
    if( pSubRectSite )
    {
        //If so, since IHXSubRectSite inheirits from IHXSite, lets
        //just swap the pointers and sign up for the service.
        HX_RELEASE( m_pSite );
        m_pSite = pSubRectSite;
        pSubRectSite->SendSubRectMessages(TRUE);
    }
}

STDMETHODIMP CWBMPRenderer::GetName(REF(const char*) rpszName)
{
    rpszName = (const char*) m_pszName;
    return HXR_OK;
}

STDMETHODIMP CWBMPRenderer::GetDescription(REF(const char*) rpszDescription)
{
    rpszDescription = (const char*) m_pszDescription;
    return HXR_OK;
}

STDMETHODIMP CWBMPRenderer::GetMimeTypes(REF(const char**) rppszMimeType)
{
    rppszMimeType = (const char**) m_ppszMimeType;
    return HXR_OK;
}

STDMETHODIMP_(UINT32) CWBMPRenderer::GetPluginVersion()
{
    return TARVER_ULONG32_VERSION;
}

STDMETHODIMP_(UINT32) CWBMPRenderer::GetInitialGranularity()
{
    return 200;
}
