/**************************************************************
 * 
 * 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_toolkit.hxx"

#include <toolkit/awt/vclxgraphics.hxx>
#include <toolkit/awt/vclxdevice.hxx>
#include <toolkit/awt/vclxfont.hxx>
#include <toolkit/helper/macros.hxx>
#include <toolkit/helper/vclunohelper.hxx>
#include <cppuhelper/typeprovider.hxx>
#include <rtl/memory.h>
#include <rtl/uuid.h>

#include <vcl/svapp.hxx>
#include <vcl/outdev.hxx>
#include <vcl/image.hxx>
#include <vcl/gradient.hxx>
#include <tools/debug.hxx>

using namespace com::sun::star;

//	----------------------------------------------------
//	class VCLXGraphics
//	----------------------------------------------------

// uno::XInterface
uno::Any VCLXGraphics::queryInterface( const uno::Type & rType ) throw(uno::RuntimeException)
{
	uno::Any aRet = ::cppu::queryInterface( rType,
										SAL_STATIC_CAST( awt::XGraphics*, this ),
										SAL_STATIC_CAST( lang::XTypeProvider*, this ),
										SAL_STATIC_CAST( lang::XUnoTunnel*, this ) );
	return (aRet.hasValue() ? aRet : OWeakObject::queryInterface( rType ));
}

// lang::XUnoTunnel
IMPL_XUNOTUNNEL( VCLXGraphics )

// lang::XTypeProvider
IMPL_XTYPEPROVIDER_START( VCLXGraphics )
	getCppuType( ( uno::Reference< awt::XGraphics>* ) NULL )
IMPL_XTYPEPROVIDER_END

VCLXGraphics::VCLXGraphics() : mrMutex( Application::GetSolarMutex() )
{
	mpOutputDevice = NULL;
	mpClipRegion = NULL;
}

VCLXGraphics::~VCLXGraphics()
{
	List* pLst = mpOutputDevice ? mpOutputDevice->GetUnoGraphicsList() : NULL;
	if ( pLst )
		pLst->Remove( this );

	delete mpClipRegion;
}

void VCLXGraphics::SetOutputDevice( OutputDevice* pOutDev ) 
{ 
	mpOutputDevice = pOutDev; 
	mxDevice = NULL;
    initAttrs();
}

void VCLXGraphics::Init( OutputDevice* pOutDev )
{
	DBG_ASSERT( !mpOutputDevice, "VCLXGraphics::Init allready has pOutDev !" );
	mpOutputDevice 	= pOutDev;

    initAttrs();
    mpClipRegion    = NULL;

	// Register at OutputDevice
	List* pLst = mpOutputDevice->GetUnoGraphicsList();
	if ( !pLst )
		pLst = mpOutputDevice->CreateUnoGraphicsList();
	pLst->Insert( this, LIST_APPEND );
}

void VCLXGraphics::initAttrs()
{
    if ( !mpOutputDevice )
        return;

    maFont          = mpOutputDevice->GetFont();
    maTextColor     = mpOutputDevice->GetTextColor(); /* COL_BLACK */
    maTextFillColor = mpOutputDevice->GetTextFillColor(); /* COL_TRANSPARENT */
    maLineColor     = mpOutputDevice->GetLineColor(); /* COL_BLACK */
    maFillColor     = mpOutputDevice->GetFillColor(); /* COL_WHITE */
    meRasterOp      = mpOutputDevice->GetRasterOp(); /* ROP_OVERPAINT */
}

void VCLXGraphics::InitOutputDevice( sal_uInt16 nFlags )
{
	if(mpOutputDevice) 
	{
		vos::OGuard aVclGuard( Application::GetSolarMutex()  );

		if ( nFlags & INITOUTDEV_FONT )
		{
			mpOutputDevice->SetFont( maFont );
			mpOutputDevice->SetTextColor( maTextColor );
			mpOutputDevice->SetTextFillColor( maTextFillColor );
		}

		if ( nFlags & INITOUTDEV_COLORS )
		{
			mpOutputDevice->SetLineColor( maLineColor );
			mpOutputDevice->SetFillColor( maFillColor );
		}

		if ( nFlags & INITOUTDEV_RASTEROP )
		{
			mpOutputDevice->SetRasterOp( meRasterOp );
		}

		if ( nFlags & INITOUTDEV_CLIPREGION )
		{
			if( mpClipRegion )
				mpOutputDevice->SetClipRegion( *mpClipRegion );
			else
				mpOutputDevice->SetClipRegion();
		}
	}
}

uno::Reference< awt::XDevice > VCLXGraphics::getDevice() throw(uno::RuntimeException)
{
	::vos::OGuard aGuard( GetMutex() );

	if( !mxDevice.is() && mpOutputDevice )
	{
		VCLXDevice* pDev = new VCLXDevice;
		pDev->SetOutputDevice( mpOutputDevice );
		mxDevice = pDev;
	}
	return mxDevice;
}

awt::SimpleFontMetric VCLXGraphics::getFontMetric() throw(uno::RuntimeException)
{
	::vos::OGuard aGuard( GetMutex() );

	awt::SimpleFontMetric aM;
	if( mpOutputDevice ) 
	{
		mpOutputDevice->SetFont( maFont );
		aM = VCLUnoHelper::CreateFontMetric( mpOutputDevice->GetFontMetric() );
	}
	return aM;
}

void VCLXGraphics::setFont( const uno::Reference< awt::XFont >& rxFont ) throw(uno::RuntimeException)
{
	::vos::OGuard aGuard( GetMutex() );

	maFont = VCLUnoHelper::CreateFont( rxFont );
}

uno::Reference< awt::XFont > VCLXGraphics::getFont() throw(uno::RuntimeException)
{
    uno::Reference< awt::XFont > xFont;
    uno::Reference< awt::XDevice > xDevice( getDevice() );

    ::vos::OGuard aGuard( GetMutex() );

    if ( xDevice.is() )
    {
        VCLXFont *pFont = new VCLXFont;
        pFont->Init( *xDevice.get(), maFont );
        xFont.set( static_cast< ::cppu::OWeakObject* >( pFont ), uno::UNO_QUERY );
    }

    return xFont;
}

void VCLXGraphics::selectFont( const awt::FontDescriptor& rDescription ) throw(uno::RuntimeException)
{
	::vos::OGuard aGuard( GetMutex() );

	maFont = VCLUnoHelper::CreateFont( rDescription, Font() );
}

void VCLXGraphics::setTextColor( sal_Int32 nColor ) throw(uno::RuntimeException)
{
	::vos::OGuard aGuard( GetMutex() );

	maTextColor = Color( (sal_uInt32)nColor );
}

::sal_Int32 VCLXGraphics::getTextColor() throw(uno::RuntimeException)
{
    ::vos::OGuard aGuard( GetMutex() );
    return maTextColor.GetColor();
}

void VCLXGraphics::setTextFillColor( sal_Int32 nColor ) throw(uno::RuntimeException)
{
	::vos::OGuard aGuard( GetMutex() );

	maTextFillColor = Color( (sal_uInt32)nColor );
}

::sal_Int32 VCLXGraphics::getTextFillColor() throw(uno::RuntimeException)
{
    ::vos::OGuard aGuard( GetMutex() );
    return maTextFillColor.GetColor();
}

void VCLXGraphics::setLineColor( sal_Int32 nColor ) throw(uno::RuntimeException)
{
	::vos::OGuard aGuard( GetMutex() );

	maLineColor = Color( (sal_uInt32)nColor );
}

::sal_Int32 VCLXGraphics::getLineColor() throw(uno::RuntimeException)
{
    ::vos::OGuard aGuard( GetMutex() );
    return maLineColor.GetColor();
}

void VCLXGraphics::setFillColor( sal_Int32 nColor ) throw(uno::RuntimeException)
{
	::vos::OGuard aGuard( GetMutex() );

	maFillColor = Color( (sal_uInt32)nColor );
}

::sal_Int32 VCLXGraphics::getFillColor() throw(uno::RuntimeException)
{
    ::vos::OGuard aGuard( GetMutex() );
    return maFillColor.GetColor();
}

void VCLXGraphics::setRasterOp( awt::RasterOperation eROP ) throw(uno::RuntimeException)
{
	::vos::OGuard aGuard( GetMutex() );

	meRasterOp = (RasterOp)eROP;
}

awt::RasterOperation VCLXGraphics::getRasterOp()
throw(uno::RuntimeException)
{
    ::vos::OGuard aGuard( GetMutex() );
    return (awt::RasterOperation) meRasterOp;
}

void VCLXGraphics::setClipRegion( const uno::Reference< awt::XRegion >& rxRegion ) throw(uno::RuntimeException)
{
	::vos::OGuard aGuard( GetMutex() );

	delete mpClipRegion;
	if ( rxRegion.is() )
		mpClipRegion = new Region( VCLUnoHelper::GetRegion( rxRegion ) );
	else
		mpClipRegion = NULL;
}

void VCLXGraphics::intersectClipRegion( const uno::Reference< awt::XRegion >& rxRegion ) throw(uno::RuntimeException)
{
	::vos::OGuard aGuard( GetMutex() );

	if ( rxRegion.is() )
	{
		Region aRegion( VCLUnoHelper::GetRegion( rxRegion ) );
		if ( !mpClipRegion )
			mpClipRegion = new Region( aRegion );
		else
			mpClipRegion->Intersect( aRegion );
	}
}

void VCLXGraphics::push(  ) throw(uno::RuntimeException)
{
	::vos::OGuard aGuard( GetMutex() );


	if( mpOutputDevice )
		mpOutputDevice->Push();
}

void VCLXGraphics::pop(  ) throw(uno::RuntimeException)
{
	::vos::OGuard aGuard( GetMutex() );


	if( mpOutputDevice )
		mpOutputDevice->Pop();
}

void VCLXGraphics::clear(
    const awt::Rectangle& aRect )
throw(uno::RuntimeException)
{
    ::vos::OGuard aGuard( GetMutex() );

    if( mpOutputDevice )
    {
        const ::Rectangle aVCLRect = VCLUnoHelper::ConvertToVCLRect( aRect );
        mpOutputDevice->Erase( aVCLRect );
    }
}

void VCLXGraphics::copy( const uno::Reference< awt::XDevice >& rxSource, sal_Int32 nSourceX, sal_Int32 nSourceY, sal_Int32 nSourceWidth, sal_Int32 nSourceHeight, sal_Int32 nDestX, sal_Int32 nDestY, sal_Int32 nDestWidth, sal_Int32 nDestHeight ) throw(uno::RuntimeException)
{
	::vos::OGuard aGuard( GetMutex() );

	if ( mpOutputDevice ) 
	{
		VCLXDevice* pFromDev = VCLXDevice::GetImplementation( rxSource );
		DBG_ASSERT( pFromDev, "VCLXGraphics::copy - invalid device" );
		if ( pFromDev )
		{
			InitOutputDevice( INITOUTDEV_CLIPREGION|INITOUTDEV_RASTEROP );
			mpOutputDevice->DrawOutDev( Point( nDestX, nDestY ), Size( nDestWidth, nDestHeight ), 
									Point( nSourceX, nSourceY ), Size( nSourceWidth, nSourceHeight ), *pFromDev->GetOutputDevice() );
		}
	}
}

void VCLXGraphics::draw( const uno::Reference< awt::XDisplayBitmap >& rxBitmapHandle, sal_Int32 nSourceX, sal_Int32 nSourceY, sal_Int32 nSourceWidth, sal_Int32 nSourceHeight, sal_Int32 nDestX, sal_Int32 nDestY, sal_Int32 nDestWidth, sal_Int32 nDestHeight ) throw(uno::RuntimeException)
{
	::vos::OGuard aGuard( GetMutex() );

	if( mpOutputDevice )
	{
		InitOutputDevice( INITOUTDEV_CLIPREGION|INITOUTDEV_RASTEROP);
		uno::Reference< awt::XBitmap > xBitmap( rxBitmapHandle, uno::UNO_QUERY );
		BitmapEx aBmpEx = VCLUnoHelper::GetBitmap( xBitmap );
		
		Point aPos(nDestX - nSourceX, nDestY - nSourceY);
  		Size aSz = aBmpEx.GetSizePixel();

		if(nDestWidth != nSourceWidth) 
		{
			float zoomX = (float)nDestWidth / (float)nSourceWidth;
			aSz.Width() = (long) ((float)aSz.Width() * zoomX); 
		}

		if(nDestHeight != nSourceHeight) 
		{
			float zoomY = (float)nDestHeight / (float)nSourceHeight;
			aSz.Height() = (long) ((float)aSz.Height() * zoomY);
		}

		if(nSourceX || nSourceY || aSz.Width() != nSourceWidth || aSz.Height() != nSourceHeight) 
			mpOutputDevice->IntersectClipRegion(Region(Rectangle(nDestX, nDestY, nDestX + nDestWidth - 1, nDestY + nDestHeight - 1)));

		mpOutputDevice->DrawBitmapEx( aPos, aSz, aBmpEx );
	}
}

void VCLXGraphics::drawPixel( sal_Int32 x, sal_Int32 y ) throw(uno::RuntimeException)
{
	::vos::OGuard aGuard( GetMutex() );

	if( mpOutputDevice )
	{
		InitOutputDevice( INITOUTDEV_CLIPREGION|INITOUTDEV_RASTEROP|INITOUTDEV_COLORS );
		mpOutputDevice->DrawPixel( Point( x, y ) );
	}
}

void VCLXGraphics::drawLine( sal_Int32 x1, sal_Int32 y1, sal_Int32 x2, sal_Int32 y2 ) throw(uno::RuntimeException)
{
	::vos::OGuard aGuard( GetMutex() );

	if( mpOutputDevice )
	{
		InitOutputDevice( INITOUTDEV_CLIPREGION|INITOUTDEV_RASTEROP|INITOUTDEV_COLORS );
		mpOutputDevice->DrawLine( Point( x1, y1 ), Point( x2, y2 ) );
	}
}

void VCLXGraphics::drawRect( sal_Int32 x, sal_Int32 y, sal_Int32 width, sal_Int32 height ) throw(uno::RuntimeException)
{
	::vos::OGuard aGuard( GetMutex() );

	if( mpOutputDevice )
	{
		InitOutputDevice( INITOUTDEV_CLIPREGION|INITOUTDEV_RASTEROP|INITOUTDEV_COLORS );
		mpOutputDevice->DrawRect( Rectangle( Point( x, y ), Size( width, height ) ) );
	}
}

void VCLXGraphics::drawRoundedRect( sal_Int32 x, sal_Int32 y, sal_Int32 width, sal_Int32 height, sal_Int32 nHorzRound, sal_Int32 nVertRound ) throw(uno::RuntimeException)
{
	::vos::OGuard aGuard( GetMutex() );

	if( mpOutputDevice )
	{
		InitOutputDevice( INITOUTDEV_CLIPREGION|INITOUTDEV_RASTEROP|INITOUTDEV_COLORS );
		mpOutputDevice->DrawRect( Rectangle( Point( x, y ), Size( width, height ) ), nHorzRound, nVertRound );
	}
}

void VCLXGraphics::drawPolyLine( const uno::Sequence< sal_Int32 >& DataX, const uno::Sequence< sal_Int32 >& DataY ) throw(uno::RuntimeException)
{
	::vos::OGuard aGuard( GetMutex() );

	if( mpOutputDevice )
	{
		InitOutputDevice( INITOUTDEV_CLIPREGION|INITOUTDEV_RASTEROP|INITOUTDEV_COLORS );
		mpOutputDevice->DrawPolyLine( VCLUnoHelper::CreatePolygon( DataX, DataY ) );
	}
}

void VCLXGraphics::drawPolygon( const uno::Sequence< sal_Int32 >& DataX, const uno::Sequence< sal_Int32 >& DataY ) throw(uno::RuntimeException)
{
	::vos::OGuard aGuard( GetMutex() );

	if( mpOutputDevice )
	{
		InitOutputDevice( INITOUTDEV_CLIPREGION|INITOUTDEV_RASTEROP|INITOUTDEV_COLORS );
		mpOutputDevice->DrawPolygon( VCLUnoHelper::CreatePolygon( DataX, DataY ) );
	}
}

void VCLXGraphics::drawPolyPolygon( const uno::Sequence< uno::Sequence< sal_Int32 > >& DataX, const uno::Sequence< uno::Sequence< sal_Int32 > >& DataY ) throw(uno::RuntimeException)
{
	::vos::OGuard aGuard( GetMutex() );

	if( mpOutputDevice )
	{
		InitOutputDevice( INITOUTDEV_CLIPREGION|INITOUTDEV_RASTEROP|INITOUTDEV_COLORS );
		sal_uInt16 nPolys = (sal_uInt16) DataX.getLength();
		PolyPolygon aPolyPoly( nPolys );
		for ( sal_uInt16 n = 0; n < nPolys; n++ )
			aPolyPoly[n] = VCLUnoHelper::CreatePolygon( DataX.getConstArray()[n], DataY.getConstArray()[n] );

		mpOutputDevice->DrawPolyPolygon( aPolyPoly );
	}
}

void VCLXGraphics::drawEllipse( sal_Int32 x, sal_Int32 y, sal_Int32 width, sal_Int32 height ) throw(uno::RuntimeException)
{
	::vos::OGuard aGuard( GetMutex() );

	if( mpOutputDevice )
	{
		InitOutputDevice( INITOUTDEV_CLIPREGION|INITOUTDEV_RASTEROP|INITOUTDEV_COLORS );
		mpOutputDevice->DrawEllipse( Rectangle( Point( x, y ), Size( width, height ) ) );
	}
}

void VCLXGraphics::drawArc( sal_Int32 x, sal_Int32 y, sal_Int32 width, sal_Int32 height, sal_Int32 x1, sal_Int32 y1, sal_Int32 x2, sal_Int32 y2 ) throw(uno::RuntimeException)
{
	::vos::OGuard aGuard( GetMutex() );

	if( mpOutputDevice )
	{
		InitOutputDevice( INITOUTDEV_CLIPREGION|INITOUTDEV_RASTEROP|INITOUTDEV_COLORS );
		mpOutputDevice->DrawArc( Rectangle( Point( x, y ), Size( width, height ) ), Point( x1, y1 ), Point( x2, y2 ) );
	}
}

void VCLXGraphics::drawPie( sal_Int32 x, sal_Int32 y, sal_Int32 width, sal_Int32 height, sal_Int32 x1, sal_Int32 y1, sal_Int32 x2, sal_Int32 y2 ) throw(uno::RuntimeException)
{
	::vos::OGuard aGuard( GetMutex() );

	if( mpOutputDevice )
	{
		InitOutputDevice( INITOUTDEV_CLIPREGION|INITOUTDEV_RASTEROP|INITOUTDEV_COLORS );
		mpOutputDevice->DrawPie( Rectangle( Point( x, y ), Size( width, height ) ), Point( x1, y1 ), Point( x2, y2 ) );
	}
}

void VCLXGraphics::drawChord( sal_Int32 x, sal_Int32 y, sal_Int32 width, sal_Int32 height, sal_Int32 x1, sal_Int32 y1, sal_Int32 x2, sal_Int32 y2 ) throw(uno::RuntimeException)
{
	::vos::OGuard aGuard( GetMutex() );

	if( mpOutputDevice )
	{
		InitOutputDevice( INITOUTDEV_CLIPREGION|INITOUTDEV_RASTEROP|INITOUTDEV_COLORS );
		mpOutputDevice->DrawChord( Rectangle( Point( x, y ), Size( width, height ) ), Point( x1, y1 ), Point( x2, y2 ) );
	}
}

void VCLXGraphics::drawGradient( sal_Int32 x, sal_Int32 y, sal_Int32 width, sal_Int32 height, const awt::Gradient& rGradient ) throw(uno::RuntimeException)
{
	::vos::OGuard aGuard( GetMutex() );

	if( mpOutputDevice )
	{
		InitOutputDevice( INITOUTDEV_CLIPREGION|INITOUTDEV_RASTEROP|INITOUTDEV_COLORS );
		Gradient aGradient((GradientStyle)rGradient.Style, rGradient.StartColor, rGradient.EndColor);
		aGradient.SetAngle(rGradient.Angle);
		aGradient.SetBorder(rGradient.Border);
		aGradient.SetOfsX(rGradient.XOffset);
		aGradient.SetOfsY(rGradient.YOffset);
		aGradient.SetStartIntensity(rGradient.StartIntensity);
		aGradient.SetEndIntensity(rGradient.EndIntensity);
		aGradient.SetSteps(rGradient.StepCount);
		mpOutputDevice->DrawGradient( Rectangle( Point( x, y ), Size( width, height ) ), aGradient );
	}
}

void VCLXGraphics::drawText( sal_Int32 x, sal_Int32 y, const ::rtl::OUString& rText ) throw(uno::RuntimeException)
{
	::vos::OGuard aGuard( GetMutex() );

	if( mpOutputDevice )
	{
		InitOutputDevice( INITOUTDEV_CLIPREGION|INITOUTDEV_RASTEROP|INITOUTDEV_COLORS |INITOUTDEV_FONT);
		mpOutputDevice->DrawText( Point( x, y ), rText );
	}
}

void VCLXGraphics::drawTextArray( sal_Int32 x, sal_Int32 y, const ::rtl::OUString& rText, const uno::Sequence< sal_Int32 >& rLongs ) throw(uno::RuntimeException)
{
	::vos::OGuard aGuard( GetMutex() );

	if( mpOutputDevice )
	{
		InitOutputDevice( INITOUTDEV_CLIPREGION|INITOUTDEV_RASTEROP|INITOUTDEV_COLORS|INITOUTDEV_FONT );
		mpOutputDevice->DrawTextArray( Point( x, y ), rText, rLongs.getConstArray() );
	}
}


void VCLXGraphics::drawImage( sal_Int32 x, sal_Int32 y, sal_Int32 width, sal_Int32 height, sal_Int16 nStyle, const uno::Reference< graphic::XGraphic >& xGraphic ) throw(uno::RuntimeException)
{
    ::vos::OGuard aGuard( GetMutex() );

    if( mpOutputDevice && xGraphic.is() )
    {
        Image aImage( xGraphic );
        if ( !!aImage )
        {
            InitOutputDevice( INITOUTDEV_CLIPREGION|INITOUTDEV_RASTEROP|INITOUTDEV_COLORS );
            mpOutputDevice->DrawImage( Point( x, y ), Size( width, height ), aImage, nStyle );
        }
    }
}
