/* ***** BEGIN LICENSE BLOCK *****
 * Source last modified: $Id: linux_fb_surf.cpp,v 1.1.10.1 2004/07/09 01:59:14 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 "minisite.h"
#include "hxvsurf.h"
#include "coloracc.h"

#include "minisurf.h"
#include "platform/unix/linux_fb_surf.h"

#include "minifmt.h"

//#include <unistd.h>
#include <fcntl.h>
#include <linux/fb.h>
//#include <linux/kd.h>
#include <sys/ioctl.h>
#include <sys/mman.h>
//#include <sys/time.h>
//#include <sys/types.h>
//#include <sys/select.h>

static CFmtObj ImageHelper;

CLinuxFrameBufferSurface::CLinuxFrameBufferSurface(IUnknown* pContext, 
					 CMiniBaseSite* pSite)
    :  CMiniBaseSurface(pContext, pSite)
    , m_nScreenNumber(0)
    , m_unDepth(0)
    , m_nBitsPerPixel(0)
    , m_nCompositionSize(0)
    , m_pCompositionSurface(NULL)
    , m_frameBufferFD(-1)
    , m_pFrameBuffer(0)
{}

CLinuxFrameBufferSurface::~CLinuxFrameBufferSurface()
{
    if (m_frameBufferFD != -1)
    {
	m_pFrameBuffer = 0;

	close(m_frameBufferFD);
	m_frameBufferFD = -1;
	
    }

    HX_FREE(m_pCompositionSurface);
    m_nCompositionSize  = 0;
    m_nCompositionPitch = 0;
    memset(&m_surfaceSize, 0, sizeof( m_surfaceSize ) );
}


HX_RESULT CLinuxFrameBufferSurface::_CreateDestBuffer(int cidIn,
                                              int nWidth,
                                              int nHeight,
                                              int& nCount)
{
    HX_RESULT res = HXR_OK;

    HX_ASSERT( m_pSite );
    //HX_ASSERT( m_unDepth );

    //For now, we just support 1 format
    HX_ASSERT( cidIn == CID_I420 );
    
    //Get the size of this site.
    m_pSite->GetSize(m_surfaceSize);

    //Malloc the room to hold the actual bits.
    HXBitmapInfo bmiTemp;
    ImageHelper.MakeBitmap( &bmiTemp,
                      sizeof(bmiTemp),
                      GetDstCID(),
                      m_surfaceSize.cx,
                      m_surfaceSize.cy,
                      NULL,
                      0);
    
    m_nCompositionPitch = ImageHelper.GetBitmapPitch(&bmiTemp);
    int imageSize = bmiTemp.bmiHeader.biSizeImage;
    res = _ResizeVideoBuffer(imageSize);

    HX_ASSERT( ImageHelper.GetBitmapColor(&bmiTemp) == GetDstCID() );

    if (!m_pFrameBuffer)
    {
	res = _OpenFrameBuffer();
    }
    
    return res;
}

         
HX_RESULT CLinuxFrameBufferSurface::_LockDestBuffer(UCHAR** ppDestPtr,
					       LONG32* pnDestPitch,
					       int& cid,
					       REF(HXxSize) srcSize,
					       int nIndex)
{
    *ppDestPtr = m_pCompositionSurface;
    *pnDestPitch = m_nCompositionPitch;
    cid = GetDstCID();
    
    return HXR_OK;
}

         
HX_RESULT CLinuxFrameBufferSurface::_TransferToDestBuffer(UCHAR* pSrcBuffer,
                                                  HXBitmapInfoHeader* pBitmapInfo,
                                                  HXxRect* prSrcRect,
                                                  HXxRect* prDstRect,
                                                  UCHAR* pDstBuffer,
                                                  LONG32 nDstPitch)
{
    HX_RESULT res = HXR_OK;
    int nCidIn = ImageHelper.GetBitmapColor( (HXBitmapInfo*)pBitmapInfo );
    //This minisite, as written, is just doing a blind I420->RGB32
    //color convert. Feel free to add whatever you want...
    HX_ASSERT( nCidIn == CID_I420 );
    if( nCidIn == CID_I420 )
    {
        //This calls the m_fpColorConverter init'ed in the base class.
        res =  CMiniBaseSurface::_TransferToDestBuffer(pSrcBuffer,
                                                       pBitmapInfo,
                                                       prSrcRect,
                                                       prDstRect,
                                                       pDstBuffer,
                                                       nDstPitch);
    }

    return res;
}

         
HX_RESULT CLinuxFrameBufferSurface::_UnlockDestBuffer(UCHAR* pSurfPtr, int nIndex=0)
{
    return HXR_OK;
}

         
HX_RESULT CLinuxFrameBufferSurface::_RenderDestBuffer(HXxRect* prSrcRect,
						 HXxRect* prDestRect,
						 int nIndex)
{
    HX_RESULT res = HXR_FAILED;

    int imgWidth  = prDestRect->right - prDestRect->left;
    int imgHeight = prDestRect->bottom - prDestRect->top;
    

    if (m_pFrameBuffer)
    {
#ifdef HELIX_FEATURE_CC_RGB32out
	UINT32* pSrc = (UINT32*)m_pCompositionSurface;
#elif defined(HELIX_FEATURE_CC_RGB565out)
	UINT16* pSrc = (UINT16*)m_pCompositionSurface;
#endif
	UINT16* pDest = m_pFrameBuffer;

	int srcDelta = 0;
	int dstDelta = 0;

	// Compute how much of the image we want to copy
	int lines = (imgHeight < m_fbHeight) ? imgHeight : m_fbHeight;
	int copyWidth = imgWidth;

	if (imgWidth < m_fbWidth)
	{
	    dstDelta = m_fbWidth - imgWidth;
	}
	else if (imgWidth > m_fbWidth)
	{
	    srcDelta = imgWidth - m_fbWidth;
	    copyWidth = m_fbWidth;
	}
	
	// Center image
	int left = (m_fbWidth / 2) - (copyWidth / 2);
	int top = (m_fbHeight / 2) - (lines / 2);
	pDest += top * m_fbWidth + left;
	
	int i = 0;
	for (int y = 0; y < lines; y++)
	{
	    for (int x = 0; x < copyWidth; x++)
	    {
#ifdef HELIX_FEATURE_CC_RGB32out
		// We need to convert RGB32 -> RGB565
		UINT32 src   = *pSrc++;
		UINT32 dst = (((src & 0x00f80000) >> 8) | 
			      ((src & 0x0000fc00) >> 5) | 
			      ((src & 0x000000f8) >> 3));
		*pDest++ = (UINT16)dst;
#elif defined(HELIX_FEATURE_CC_RGB565out)
		*pDest++ = *pSrc++;
#endif
	    }
	    pSrc += srcDelta;
	    pDest += dstDelta;
	}

	res = HXR_OK;
    }
    
    return res;
}

         
HX_RESULT CLinuxFrameBufferSurface::_DestroyDestBuffer(int cid, int nCount)
{
  HX_FREE(m_pCompositionSurface);
    m_nCompositionSize  = 0;
    m_nCompositionPitch = 0;
    memset(&m_surfaceSize, 0, sizeof( m_surfaceSize ) );
    
    return HXR_OK;
}

int CLinuxFrameBufferSurface::GetDstCID(int nIndex)
{
    //Right now the mini site just always outputs in RGB32. 
#ifdef HELIX_FEATURE_CC_RGB32out
    return CID_RGB32;
#elif defined(HELIX_FEATURE_CC_RGB565out)
    return CID_RGB565;
#endif
}

HX_RESULT CLinuxFrameBufferSurface::_ResizeVideoBuffer( INT32 nSize)
{
    HX_RESULT retVal=HXR_OK;

    //XXXgfw. Trade off here. We can use lots of mem if we just return and
    //the user has scaled the image up very much and then goes back down.
    //If we don't just return we can do tons and tons of mallocs. Maybe we
    //should add a timed callback to reclaim some of this mem after a few
    //seconds.
    if(nSize <= m_nCompositionSize)
        return retVal;

    if(m_pCompositionSurface == NULL)
    {
        m_pCompositionSurface = (UCHAR*) malloc(nSize);
    }
    else
    {
        m_pCompositionSurface = (UCHAR*) realloc(m_pCompositionSurface, nSize);
    }
    if( m_pCompositionSurface )
    {
        m_nCompositionSize = nSize;
    }
    else
    {
        HX_ASSERT("We can't alloc the composition surface." == NULL );
        m_nCompositionSize = 0;
    }

    return retVal;
}

HX_RESULT CLinuxFrameBufferSurface::_OpenFrameBuffer()
{
    HX_RESULT res = HXR_FAILED;

    struct fb_var_screeninfo vinfo;
    struct fb_fix_screeninfo finfo;
    char *fbp = 0;
    int x = 0, y = 0;
    long int location = 0;

  /* Open the file for reading and writing */
    m_frameBufferFD = open("/dev/fb0", O_RDWR);

    if (m_frameBufferFD == -1) 
    {
	printf("Error: cannot open framebuffer device.\n");
    }
    else if (ioctl(m_frameBufferFD, FBIOGET_FSCREENINFO, &finfo)) 
    {
	printf("Error reading fixed information.\n");
    }
    else if (ioctl(m_frameBufferFD, FBIOGET_VSCREENINFO, &vinfo)) 
    {
	printf("Error reading variable information.\n");
    }
    else
    {
	printf("xres:%i , yres:%i , BBP:%i\n ", 
	       vinfo.xres, vinfo.yres, vinfo.bits_per_pixel);

	m_fbWidth = vinfo.xres;
	m_fbHeight = vinfo.yres;
	int screen_size  = vinfo.xres * vinfo.yres * vinfo.bits_per_pixel / 8;
		
		
	/* Map the device to memory */
	m_pFrameBuffer = (UINT16*)mmap(0, screen_size, 
				       PROT_READ | PROT_WRITE, MAP_SHARED,
				       m_frameBufferFD, 0);       
	if ((int)m_pFrameBuffer == -1) 
	{ 
	    printf("Error: failed to map framebuffer device to memory.\n"); 
	}
	else
	{
	    res = HXR_OK;
	}
    }

    return res;
}
