/**************************************************************
 * 
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 * 
 *   http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 * 
 *************************************************************/



// MARKER(update_precomp.py): autogen include statement, do not remove
#include "precompiled_vcl.hxx"

#include <tools/svwin.h>

#include <vcl/sysdata.hxx>

#include <win/wincomp.hxx>
#include <win/saldata.hxx>
#include <win/salinst.h>
#include <win/salgdi.h>
#include <win/salvd.h>

// =======================================================================

static HBITMAP ImplCreateVirDevBitmap( HDC hDC, long nDX, long nDY,
									   sal_uInt16 nBitCount )
{
	HBITMAP hBitmap;

 	if ( nBitCount == 1 )
 	{
 		hBitmap = CreateBitmap( (int)nDX, (int)nDY, 1, 1, NULL );
 	}
 	else
 	{
        // #146839# Don't use CreateCompatibleBitmap() - there seem to
        // be build-in limits for those HBITMAPs, at least this fails
        // rather often on large displays/multi-monitor setups.
 		BITMAPINFO aBitmapInfo;
 		aBitmapInfo.bmiHeader.biSize = sizeof( BITMAPINFOHEADER );
 		aBitmapInfo.bmiHeader.biWidth = nDX;
 		aBitmapInfo.bmiHeader.biHeight = nDY;
 		aBitmapInfo.bmiHeader.biPlanes = 1;
 		aBitmapInfo.bmiHeader.biBitCount = (WORD)GetDeviceCaps( hDC,
 														        BITSPIXEL );
 		aBitmapInfo.bmiHeader.biCompression = BI_RGB;
 		aBitmapInfo.bmiHeader.biSizeImage = 0;
 		aBitmapInfo.bmiHeader.biXPelsPerMeter = 0;
 		aBitmapInfo.bmiHeader.biYPelsPerMeter = 0;
 		aBitmapInfo.bmiHeader.biClrUsed = 0;
 		aBitmapInfo.bmiHeader.biClrImportant = 0;
 
 		void* pDummy;
 		hBitmap = CreateDIBSection( hDC, &aBitmapInfo, 
 									DIB_RGB_COLORS, &pDummy, NULL,
 									0 );
 	}

	return hBitmap;
}

// =======================================================================

SalVirtualDevice* WinSalInstance::CreateVirtualDevice( SalGraphics* pSGraphics,
                                                       long nDX, long nDY,
                                                       sal_uInt16 nBitCount,
                                                       const SystemGraphicsData* pData )
{
    WinSalGraphics* pGraphics = static_cast<WinSalGraphics*>(pSGraphics);

	HDC 	hDC = NULL;
	HBITMAP hBmp = NULL;
    sal_Bool    bOk = FALSE;

    if( pData )
    {
        hDC = pData->hDC;
        hBmp = NULL;
        bOk = (hDC != NULL);
    }
    else
    {
	    hDC 	= CreateCompatibleDC( pGraphics->mhDC );
        if( !hDC )
            ImplWriteLastError( GetLastError(), "CreateCompatibleDC in CreateVirtualDevice" );

	    hBmp	= ImplCreateVirDevBitmap( pGraphics->mhDC,
		                                nDX, nDY, nBitCount );
        if( !hBmp )
            ImplWriteLastError( GetLastError(), "ImplCreateVirDevBitmap in CreateVirtualDevice" );
        // #124826# continue even if hBmp could not be created
        // if we would return a failure in this case, the process
        // would terminate which is not required

        DBG_ASSERT( hBmp, "WinSalInstance::CreateVirtualDevice(), could not create Bitmap!" );

        bOk = (hDC != NULL);
    }

	if ( bOk )
	{
		WinSalVirtualDevice*	pVDev = new WinSalVirtualDevice;
		SalData*                pSalData = GetSalData();
		WinSalGraphics*         pVirGraphics = new WinSalGraphics;
        pVirGraphics->SetLayout( 0 );   // by default no! mirroring for VirtualDevices, can be enabled with EnableRTL()
		pVirGraphics->mhDC	   = hDC;
		pVirGraphics->mhWnd	   = 0;
		pVirGraphics->mbPrinter = FALSE;
		pVirGraphics->mbVirDev  = TRUE;
		pVirGraphics->mbWindow  = FALSE;
		pVirGraphics->mbScreen  = pGraphics->mbScreen;
		if ( pSalData->mhDitherPal && pVirGraphics->mbScreen )
		{
			pVirGraphics->mhDefPal = SelectPalette( hDC, pSalData->mhDitherPal, TRUE );
			RealizePalette( hDC );
		}
		ImplSalInitGraphics( pVirGraphics );

		pVDev->mhDC		    = hDC;
		pVDev->mhBmp		= hBmp;
        if( hBmp )
		    pVDev->mhDefBmp	= SelectBitmap( hDC, hBmp );
        else
            pVDev->mhDefBmp = NULL;
		pVDev->mpGraphics	= pVirGraphics;
		pVDev->mnBitCount	= nBitCount;
		pVDev->mbGraphics	= FALSE;
        pVDev->mbForeignDC  = (pData != NULL);

		// insert VirDev in VirDevList
		pVDev->mpNext = pSalData->mpFirstVD;
		pSalData->mpFirstVD = pVDev;

		return pVDev;
	}
	else
	{
		if ( hDC && !pData )
			DeleteDC( hDC );
		if ( hBmp )
			DeleteBitmap( hBmp );
		return NULL;
	}
}

// -----------------------------------------------------------------------

void WinSalInstance::DestroyVirtualDevice( SalVirtualDevice* pDevice )
{
	delete pDevice;
}

// =======================================================================

WinSalVirtualDevice::WinSalVirtualDevice()
{
	mhDC = (HDC) NULL;			// HDC or 0 for Cache Device
	mhBmp = (HBITMAP) NULL;		// Memory Bitmap
	mhDefBmp = (HBITMAP) NULL;	// Default Bitmap
	mpGraphics = NULL; 			// current VirDev graphics
	mpNext = NULL; 				// next VirDev
	mnBitCount = 0; 			// BitCount (0 or 1)
	mbGraphics = FALSE; 		// is Graphics used
    mbForeignDC = FALSE;        // uses a foreign DC instead of a bitmap
}

// -----------------------------------------------------------------------

WinSalVirtualDevice::~WinSalVirtualDevice()
{
    // remove VirDev from list of virtual devices
    SalData* pSalData = GetSalData();
    WinSalVirtualDevice** ppVirDev = &pSalData->mpFirstVD;
    for(; (*ppVirDev != this) && *ppVirDev; ppVirDev = &(*ppVirDev)->mpNext );
    if( *ppVirDev )
        *ppVirDev = mpNext;

    // destroy saved DC
    if( mpGraphics->mhDefPal )
        SelectPalette( mpGraphics->mhDC, mpGraphics->mhDefPal, TRUE );
    ImplSalDeInitGraphics( mpGraphics );
    if( mhDefBmp )
        SelectBitmap( mpGraphics->mhDC, mhDefBmp );
    if( !mbForeignDC )
        DeleteDC( mpGraphics->mhDC );
    if( mhBmp )
        DeleteBitmap( mhBmp );
    delete mpGraphics;
    mpGraphics = NULL;
}

// -----------------------------------------------------------------------

SalGraphics* WinSalVirtualDevice::GetGraphics()
{
	if ( mbGraphics )
		return NULL;

	if ( mpGraphics )
		mbGraphics = TRUE;

	return mpGraphics;
}

// -----------------------------------------------------------------------

void WinSalVirtualDevice::ReleaseGraphics( SalGraphics* )
{
	mbGraphics = FALSE;
}

// -----------------------------------------------------------------------

sal_Bool WinSalVirtualDevice::SetSize( long nDX, long nDY )
{
    if( mbForeignDC || !mhBmp )
        return TRUE;    // ???
    else
    {
	    HBITMAP hNewBmp = ImplCreateVirDevBitmap( mhDC, nDX, nDY,
											  mnBitCount );
	    if ( hNewBmp )
	    {
		    SelectBitmap( mhDC, hNewBmp );
		    DeleteBitmap( mhBmp );
		    mhBmp = hNewBmp;
		    return TRUE;
	    }
	    else
        {
            ImplWriteLastError( GetLastError(), "ImplCreateVirDevBitmap in SetSize" );
            return FALSE;
        }
    }
}

void WinSalVirtualDevice::GetSize( long& rWidth, long& rHeight )
{
    rWidth = GetDeviceCaps( mhDC, HORZRES );
    rHeight= GetDeviceCaps( mhDC, VERTRES );
}
