// Copyright (c) 2008-2009 Nokia Corporation and/or its subsidiary(-ies).
// All rights reserved.
// This component and the accompanying materials are made available
// under the terms of "Eclipse Public License v1.0"
// which accompanies this distribution, and is available
// at the URL "http://www.eclipse.org/legal/epl-v10.html".
//
// Initial Contributors:
// Nokia Corporation - initial contribution.
//
// Contributors:
//
// Description:
//

#include <w32std.h>
#include "wsdynamicresbase.h"
#include "teflogextensions.h"
#include "globalsettings.h"
#include <bitdraw.h>
#include <bitdrawinterfaceid.h>
#include <bitdrawsurface.h>
#include <graphics/surfaceconfiguration.h>
#include "regionextend.h"


#if defined(__X86GCC__)
extern "C" TInt atexit(void (*function)(void))
	{
	return KErrNone;
	}
#endif

CWsDynamicResBase::CWsDynamicResBase():
	iUtility(this)
	{
	}

CWsDynamicResBase::~CWsDynamicResBase()
	{
	}

/**
Common set up code for all tests.

Creates the session and window group for further windows, plus a simple white
background to obscure any unwanted stuff behind the test. Sets up the surface
update session and surface manager, to work with surfaces. Creates a screen
device for use in the tests.
*/
void CWsDynamicResBase::SetupL()
	{
	SetupL(EFalse);
	}
void CWsDynamicResBase::SetupL(TBool aUseOtherScreenForInfo)
	{
	CWsDynamicResWinBase::SetupL(aUseOtherScreenForInfo);
	
	
//clean-up if previous test abended
	if (PostTestCleanupInstance().iSharedUtility)
	{
//Temp removed - may be causing ONB fails!
//	if (PostTestCleanupInstance().iSharedUtility->DestroyAll())
//		{
//		INFO_PRINTF1(_L("Destroyed some surfaces from previous test."));
//
//		}
	}
	if (!PostTestCleanupInstance().iCleanedUpOnExit)
	PostTestCleanupInstance().iCleanedUpOnExit=EFalse;
	
	if (!GCEIsSupported())
		{
		INFO_PRINTF1(_L("Some Setup skipped: GCE support is not loaded"));
		return;
		}

	TRAPD(err_FailedToCreateSurfaceUtility, iUtility = CSurfaceUtility::NewL( PostTestCleanupInstance().iSharedUtility));
	ASSERT_EQUALS(err_FailedToCreateSurfaceUtility,KErrNone);

	
	}

/**
Common tear down code for all tests.

Windows, group and session created are closed. Screen device is destroyed.
Surfaces, manager and update session are closed.
*/
void CWsDynamicResBase::TearDownL()
	{
	CWsDynamicResWinBase::TearDownL();
	delete iUtility();
	PostTestCleanupInstance().iCleanedUpOnExit=ETrue;
	//Pause(1000);
	}
/**
 * Note that this is not the ideal mechanism.
 * A derived class may thinks its TearDown is safe to do from delete, but in the class it is derived from it may not be safe
 **/
void CWsDynamicResBase::TearDownFromDeleteL()	
	{
	CWsDynamicResBase::TearDownL();	
	}

//Allocating an instance of surface utility here means all test code instances will share the same instance of the utility class.
// Owns the singleton
/*static*/ const CWsDynamicResBase::TPostTestCleanup& CWsDynamicResBase::PostTestCleanupInstance()
	{
	static 
	class RPostTestCleanup:public TPostTestCleanup
		{
		public:
		RPostTestCleanup()
			{
			iSharedUtility=NULL;
			iCleanedUpOnExit=ETrue;
			}
		~RPostTestCleanup()
			{
	// I want to cleanly release the surface utility, but at this point the threads have already been pulled down!
	//		if (iSharedUtility)
	//			delete iSharedUtility;
			iSharedUtility=NULL;	//avoid phoenix behaviour
			}
		}	staticInstance;
		return staticInstance;
	}

void	CWsDynamicResBase::TPostTestCleanup::CreateSharedUtilityL()const
	{
	if (iSharedUtility==NULL)
		iSharedUtility=CSurfaceUtility::NewL();
	}

/**
Pause for the given number of milliseconds.

@param aMilliseconds	Time to wait in milliseconds.
*/
void CWsDynamicResBase::Pause(TInt aMilliseconds)
	{
	User::After(TTimeIntervalMicroSeconds32(aMilliseconds * 1000));
	}

/**	Creates the normal scenario used by these tests.
 *  With a large window behind and small window in front.
 *  If the windows already exist then they are just repositioned and redrawn.
 * 	@param	aOuterRect	Position of back window
 * 	@param	aOuterColor	Background colour for back window
 * 	@param	aInnerRect	Position of front window
 * 	@param	aInnerColor	Background colour for front window
 * 
 **/
void CWsDynamicResBase::MakeTestWindowPairL(TRect aOuterRect,TRgb aOuterColor,TRect aInnerRect,TRgb aInnerColor)
	{
	if (!iTestBack.WsHandle())
		{
		iTestBack=RWindow(iSession);
		ASSERT_EQUALS_X(iTestBack.Construct(iGroup, ++iWindowHandle), KErrNone);
#ifdef  FLICKER
		iTestBack.Activate();
#endif
		iTestBack.SetRequiredDisplayMode(iDisplayMode);
		iTestBack.SetBackgroundColor(aOuterColor);
		iTestBack.SetExtent(aOuterRect.iTl,aOuterRect.Size());
#ifndef FLICKER
		iTestBack.Activate();
#endif
		}
	else
		{
		iTestBack.SetRequiredDisplayMode(iDisplayMode);
		iTestBack.SetBackgroundColor(aOuterColor);
		iTestBack.SetExtent(aOuterRect.iTl,aOuterRect.Size());
		}
	iTestBack.BeginRedraw();
	ActivateWithWipe(iGc,iTestBack,aOuterColor);
	iGc->Deactivate();
	iTestBack.EndRedraw();
	iTestBack.SetVisible(ETrue);
	
	if (!iTestFront.WsHandle())
		{
		iTestFront=RWindow(iSession);
		ASSERT_EQUALS_X(iTestFront.Construct(iGroup, ++iWindowHandle), KErrNone);
#ifdef FLICKER
		iTestFront.Activate();
#endif
		iTestFront.SetRequiredDisplayMode(iDisplayMode);
		iTestFront.SetBackgroundColor(aInnerColor);	
		iTestFront.SetExtent(aInnerRect.iTl,aInnerRect.Size());

#ifndef FLICKER
		iTestFront.Activate();
#endif
		}
	else
		{
		iTestFront.SetRequiredDisplayMode(iDisplayMode);
		iTestFront.SetBackgroundColor(aInnerColor);
		iTestFront.SetExtent(aInnerRect.iTl,aInnerRect.Size());
		}
	
	DrawPlainUI(iTestFront,EFalse,aInnerColor);
	iTestFront.SetVisible(ETrue);
	
	}

void CWsDynamicResBase::LargerTestWindow(TInt aPercentOfBack)
	{
	TRect newPos=iTestPos;
	TSize backSize=iTestPos.Size();
	newPos.Grow((aPercentOfBack-100)*backSize.iWidth/200,(aPercentOfBack-100)*backSize.iHeight/200);
	iCenteredFrontWinRect=newPos;
	if (iTestFront.WsHandle())
		{
		iTestFront.SetExtent(newPos.iTl,newPos.Size());
		}
	
	}

/** Destroys some or all of the test windows so the test can loop (or test the state after destruction.
 *  The windows are destroyed when the test exits normally.
 * 	Child windows are automatically killed when parent window is killed!
 * 
 **/
void CWsDynamicResBase::DestroyTestWindowPair(TBool aKillTestBack,TBool aKillTestFront,TBool aKillTestChildren)
	{
	if (aKillTestBack && iTestBack.WsHandle())
		{
		iTestBack.Close();
		}
	if (aKillTestFront && iTestFront.WsHandle())
		{
		iTestFront.Close();
		}
	if (aKillTestChildren)
		{
		if (iTestChild.WsHandle())
			{
			iTestChild.Close();
			}
		if (iTestSecondChild.WsHandle())
			{
			iTestSecondChild.Close();
			}
		}
	}
/**	Creates a child window inside the front window of the usual test scenario
*  With a large window behind and small window in front.
 *  If the windows already exist then they are just repositioned and redrawn.
 * 	@param	aOuterRect	Position of back window
 * 	@param	aOuterColor	Background colour for back window
 * 	@param	aInnerRect	Position of front window
 * 	@param	aInnerColor	Background colour for front window
 * 	@param	aChildRect	Position of child window
 * 	@param	aChildColor	Background colour for child window
 * 	@param	aSecondChildRect	Position of Second child window
 * 	@param	aSecondChildColor	Background colour for Second child window
 * 
 **/
void CWsDynamicResBase::MakeTestWindowTripleL(TRect aOuterRect,TRgb aOuterColor,TRect aInnerRect,TRgb aInnerColor,TRect aChildRect,TRgb aChildColor,TRect aSecondChildRect,TRgb aSecondChildColor)
	{
	MakeTestWindowTripleL(aOuterRect,aOuterColor,aInnerRect,aInnerColor,aChildRect,aChildColor);

	MakeExtraChildWindowL(iTestChild,aSecondChildRect,aSecondChildColor);
	}

/**	Creates a child window inside the front window of the usual test scenario
*  With a large window behind and small window in front.
 *  If the windows already exist then they are just repositioned and redrawn.
 * 	@param	aOuterRect	Position of back window
 * 	@param	aOuterColor	Background colour for back window
 * 	@param	aInnerRect	Position of front window
 * 	@param	aInnerColor	Background colour for front window
 * 	@param	aChildRect	Position of child window
 * 	@param	aChildColor	Background colour for child window
 *  @param	aSecondChildRect	Position of second child window
 * 	@param	aSecondChildColor	Background colour for second child window
 * 
 **/
void CWsDynamicResBase::CreateTestWindowQuadL(TRect aOuterRect,TRgb aOuterColor,TRect aInnerRect,TRgb aInnerColor,TRect aChildRect,TRgb aChildColor,TRect aSecondChildRect,TRgb aSecondChildColor)
	{
	MakeTestWindowTripleL(aOuterRect,aOuterColor,aInnerRect,aInnerColor,aChildRect,aChildColor);
	
	MakeExtraChildWindowL(iTestFront,aSecondChildRect,aSecondChildColor);
	}

void CWsDynamicResBase::MakeExtraChildWindowL(const RWindowBase& aFromParent,TRect aChildRect,TRgb aChildColor)
	{
	ASSERT(aFromParent.WsHandle());
	if (!iTestSecondChild.WsHandle())
		{
		iTestSecondChild=RWindow(iSession);
		ASSERT_EQUALS_X(iTestSecondChild.Construct(aFromParent, ++iWindowHandle), KErrNone);
		iTestSecondChild.SetRequiredDisplayMode(iDisplayMode);
		}
	iTestSecondChild.SetBackgroundColor(aChildColor);
	iTestSecondChild.SetExtent(aChildRect.iTl,aChildRect.Size());
	iTestSecondChild.Activate();
	iTestSecondChild.BeginRedraw();
	iGc->Activate(iCompare);
	iGc->SetBrushColor(iBlue);
	iGc->Clear();
	iGc->Deactivate();
	iTestSecondChild.EndRedraw();
	iTestSecondChild.SetVisible(ETrue);
	}

CWsDynamicResBase::LoopingGcPtr  CWsDynamicResBase::LoopBeginActivateWithWipe(const TRegion& aRegion,RWindow& aWin,TRgb aColor)
	{
	InvalidateRegion(aRegion,aWin);
	return LoopingGcPtr(aRegion,aWin,aColor,GcForWindow(aWin));
	}

//This gets called each itteration of the while loop, and is used to step the rectangle count!
void CWsDynamicResBase::LoopingGcPtr::operator ++()
	{
	iPass++;
	if (iPass<iRegion.Count())
		{	//Invalidate all the rectangles in the region
		iWin.BeginRedraw(iRegion[iPass]);
		CWsDynamicResWinBase::ActivateWithWipe(iGc,iWin,iColor);
//		return true;
		}
	else
		{
		iGc=NULL;
//		return false;
		}
	}

CWindowGc*	CWsDynamicResBase::BeginActivateWithWipe(TBool aInvalidate,RWindow& aWin,TRgb aColor)
	{
	return CWsDynamicResWinBase::BeginActivateWithWipe(aInvalidate,aWin,aColor);
	}

CWindowGc*	CWsDynamicResBase::BeginActivateWithWipe(TBool aInvalidate,TRect aRect,RWindow& aWin,TRgb aColor)
	{
	return CWsDynamicResWinBase::BeginActivateWithWipe(aInvalidate,aRect,aWin,aColor);
	}

CWindowGc*	CWsDynamicResBase::BeginActivateWithWipe(const TRegion& aRegion,RWindow& aWin,TRgb aColor)
	{
	return CWsDynamicResWinBase::BeginActivateWithWipe(aRegion,aWin,aColor);
	}

/**	Creates a child window inside the front window of the usual test scenario
*  With a large window behind and small window in front.
 *  If the windows already exist then they are just repositioned and redrawn.
 * 	@param	aOuterRect	Position of back window
 * 	@param	aOuterColor	Background colour for back window
 * 	@param	aInnerRect	Position of front window
 * 	@param	aInnerColor	Background colour for front window
 * 	@param	aChildRect	Position of child window
 * 	@param	aChildColor	Background colour for child window
 * 
 **/
void CWsDynamicResBase::MakeTestWindowTripleL(TRect aOuterRect,TRgb aOuterColor,TRect aInnerRect,TRgb aInnerColor,TRect aChildRect,TRgb aChildColor)
	{
	MakeTestWindowPairL(aOuterRect,aOuterColor,aInnerRect,aInnerColor);
	ASSERT(iTestFront.WsHandle());
	
	if (!iTestChild.WsHandle())
		{
		iTestChild=RWindow(iSession);
		ASSERT_EQUALS_X(iTestChild.Construct(iTestFront, ++iWindowHandle), KErrNone);
		iTestChild.SetRequiredDisplayMode(iDisplayMode);
		}
	iTestChild.SetBackgroundColor(aChildColor);
	iTestChild.SetExtent(aChildRect.iTl,aChildRect.Size());
	iTestChild.Activate();
	iTestChild.BeginRedraw();
	iGc->Activate(iCompare);
	iGc->SetBrushColor(iBlue);
	iGc->Clear();
	iGc->Deactivate();
	iTestChild.EndRedraw();
	iTestChild.SetVisible(ETrue);
	}
/**	
 * 	Generates a valid surfaceID for the current display mode.
 * 	This is then used in negative testing.
 * 
 * 
 **/
void CWsDynamicResBase::UISurfaceL(TSurfaceId& aSurfaceId) const
	{
	CFbsDrawDevice* screenDevice=NULL;
	TDisplayMode displayMode=iScreenDevice->DisplayMode();
	screenDevice = CFbsDrawDevice::NewScreenDeviceL(TGlobalSettings::Instance().iScreen, displayMode);
	CleanupStack::PushL(screenDevice);
	TAny* p=NULL;
	User::LeaveIfError(screenDevice->GetInterface(KSurfaceInterfaceID, p));
	MSurfaceId* uiSurface = static_cast<MSurfaceId*>(p);
	uiSurface->GetSurface(aSurfaceId);
	CleanupStack::PopAndDestroy(screenDevice);
	}

/**
Common set up code for creating a surface based window.

Given window and surface objects and a color: creates the window and surface,
sets the window to a default size (the same as the surface), sets the background
to the bitwise inverse of the given color, sets the surface as the background,
fills the surface with the color and completes a redraw to prevent an event.

@param aWindow	The window object, connected to a session.
@param aSurface	The surface object, to be initialized.
@param aColor	The color to fill the surface with.
*/
void CWsDynamicResBase::CommonSurfaceWindowSetupL(RWindow& aWindow, TSurfaceId& aSurface, const TRgb& aColor)
	{
	TInt err = KErrNone;

	TRAP(err, aSurface = iUtility->CreateSurfaceL(TSize(KSurfaceWidth, KSurfaceHeight), 
			KSurfaceFormat, KSurfaceWidth * KBytesPerPixel));
	PRINT_ON_ERROR2_L(err, _L("Failed to create surface: %d"), err);
	ASSERT_EQUALS_X(aWindow.Construct(iGroup, ++iWindowHandle), KErrNone);

	aWindow.SetRequiredDisplayMode(iDisplayMode);
	aWindow.SetExtent(TPoint(0, 0), TSize(KSurfaceWidth, KSurfaceHeight));
	aWindow.SetBackgroundColor(TRgb(aColor.Value() ^ 0xFFFFFF));

	ASSERT_EQUALS_X(aWindow.SetBackgroundSurface(aSurface), KErrNone);

	TRAP(err, iUtility->FillSurfaceL(aSurface, aColor));
	PRINT_ON_ERROR2_L(err, _L("Failed to fill surface: %d"), err);
	DrawUIContent(aWindow);
	}

/**
Common set up code for resizing tests.

Similar to the common surface window code, but filling the surface with a grid
instead of a solid color. The grid lines are always black. Also, the background
color is always blue.
*/
void CWsDynamicResBase::ResizeTestCommonSetupL(RWindow& aWindow, const TRgb& aColor)
	{
	// Session and group created in SetUpL()

	TSurfaceId surface;
	TInt err = KErrNone;

	TRAP(err, surface = iUtility->CreateSurfaceL(TSize(KSurfaceWidth, KSurfaceHeight), 
			KSurfaceFormat, KSurfaceWidth * KBytesPerPixel));
	PRINT_ON_ERROR2_L(err, _L("Failed to create surface: %d"), err);

	ASSERT_EQUALS_X(aWindow.Construct(iGroup, ++iWindowHandle), KErrNone);

	aWindow.SetRequiredDisplayMode(iDisplayMode);
	aWindow.SetExtent(TPoint(0, 0), TSize(KSurfaceWidth, KSurfaceHeight));
	aWindow.SetBackgroundColor(iBlue);
	ASSERT_EQUALS_X(aWindow.SetBackgroundSurface(surface), KErrNone);

	TRAP(err, iUtility->GridFillSurfaceL(surface, aColor, TRgb(0)));
	PRINT_ON_ERROR2_L(err, _L("Failed to grid fill surface: %d"), err);
	DrawUIContent(aWindow);
	}

CFbsBitmap* CWsDynamicResBase::RotateBitmapLC(const CFbsBitmap* aSrcBitmap)
	{
	CFbsBitmap* rv=new CFbsBitmap;
	CleanupStack::PushL(rv);
	TSize srcSize=aSrcBitmap->SizeInPixels();
	rv->Create(TSize(srcSize.iHeight,srcSize.iWidth),EColor16MA);
	TRgb* linestore=new TRgb[srcSize.iHeight];
	TPtr8 buff((unsigned char*)linestore,srcSize.iHeight*sizeof(TRgb),srcSize.iHeight*sizeof(TRgb));
	for (TInt col=0;col<aSrcBitmap->SizeInPixels().iWidth;col++)
		{
		for (TInt row=0,brow=srcSize.iHeight-1;row<srcSize.iHeight;row++,brow--)
			{
			aSrcBitmap->GetPixel(linestore[row],TPoint(col,brow));
			}
		rv->SetScanLine(buff,col);
		}
	delete[] linestore;
	return rv;
	}


//
//
//
// Pattern checking. Is a given pattern still present?
// To make life interesting, the pattern is stored backwards!
// The pattern is fibonnacci sequence masked to byte:
//	1	2	3	5	8	13	21	34	55	89	144	233	121
//	98	219	61	24	85	109	194	47	241	32	17	49	66
//	115	181	40	221	5	226	231	201	176	121	41	162
//
//
void Pattern::Fill(void* aTrg,TInt aOffset,TInt aLength)
	{
	unsigned char* ptr=(unsigned char*)aTrg;
	TInt a=0;
	TInt b=1;
	while (--aLength)
		{
			TInt c=a+b;
			*(ptr+aOffset+aLength)=c&0xff;
			a=b;
			b=c;
		}
	}
TBool Pattern::Check(void* aTrg,TInt aOffset,TInt aLength)
	{
	unsigned char* ptr=(unsigned char*)aTrg;
	TInt a=0;
	TInt b=1;
	while (--aLength)
		{
			TInt c=a+b;
			if (*(ptr+aOffset+aLength)!=c&0xff)
				return EFalse;
			a=b;
			b=c;
		}
	return ETrue;
	}
TBool Pattern::CheckVal(void* aTrg,TInt aOffset,TInt aLength,char val)
	{
	unsigned char* ptr=(unsigned char*)aTrg;
	while (--aLength)
		{
			if (*(ptr+aOffset+aLength)!=val&0xff)
				return EFalse;
		}
	return ETrue;
	}

//I have removed these only because they use TRegionExtend
//
//TInt	CWsDynamicResBase::RegionDiffForUiLayer(TInt aUiLayer)
//	{
//	EWsDebugGetUILayerConfig,		//return: TSurfaceConfig		//Index UI layer via EWsDebugArgLayerMask
//	EWsDebugGetUILayerBase,			//return: TRect[]
//	
//	RRegion 	layerRegion;
//	TInt layerRegionLen=iSession.DebugInfo(EWsDebugGetUILayerBase,iSession.ObjInd(0,aUiLayer),layerRegion);
//	if (layerRegionLen==KErrCancel)
//		return TRegionExtend::EExact;
//	ASSERT_TRUE(layerRegionLen>=0);
//	TBuf8<sizeof(TSurfaceConfiguration)> configBuf(sizeof(TSurfaceConfiguration));
//	const TSurfaceConfiguration* config;
//	TInt configLen=iSession.DebugInfo(EWsDebugGetUILayerConfig,iSession.ObjInd(0,aUiLayer),configBuf,config);
//	ASSERT_TRUE(configLen>=0);
//	TRect layerExtent;
//	config->GetExtent(layerExtent);
//	TInt retVal=TRegionExtend::Cast(layerRegion).TestDifference(layerExtent.Size());
//	layerRegion.Close();
//	return retVal;
//	}
//CWsDynamicResBase::FastPathMode	CWsDynamicResBase::DeduceUiFastPathMode()
//	{
//	TInt blendedRegionState=RegionDiffForUiLayer(0);
//	TInt opaqueRegionState=RegionDiffForUiLayer(1);
//	if (blendedRegionState&TRegionExtend::ENoIntersect)
//		{
//		if (opaqueRegionState&TRegionExtend::ENoIntersect)
//			{
//			return EFpExternalOpaque;			//fullscreen fast-path external surface
//			}
//		else
//			if (opaqueRegionState&TRegionExtend::EAdd)
//				{
//				return (FastPathMode)(EFpUiOpaque|EFpUiRegions);	//windowed max-opt no blending
//				}
//			else
//				{
//				return EFpUiOpaque;		//full-screen fastpath
//				}
//		}
//	else
//		{
//		if (opaqueRegionState&TRegionExtend::ENoIntersect)
//			{
//			if (blendedRegionState&TRegionExtend::EAdd)
//				{
//				return (FastPathMode)(EFpUiBlended|EFpUiRegions);	//windowed max-opt no opaque
//				}
//			else
//				{
//				return (EFpUiBlended);	//full-screen blended
//				}
//			}
//		else
//			{
//			if ((blendedRegionState|opaqueRegionState)&TRegionExtend::EAdd)
//				{
//				return (FastPathMode)(EFpUiComplex|EFpUiRegions);	//moxed blending, opaque and external max optimisation
//				}
//			else
//				{
//				return EFpUiComplex;	//Error! blend and opaque both enabled and full-screen!
//				}
//			}
//		
//		}
//	}
