// BOSS.CPP
//
// Copyright (c) 1998 Symbian Ltd.  All rights reserved.
//

#include <e32keys.h>

#include <coemain.h>

#include <eikenv.h>
#include <eikdef.h>
#include <eikcmds.hrh>
#include <eiklabel.h>
#include <eikfnlab.h>
#include <eiktbar.h>
#include <eikon.rsg>

#include <eikproc.h>
#include <coeutils.h>

#include <eikprtdg.h>
#include <eikprtpv.h>
#include <eikpprob.h> 


#include <boss.rsg>
#include "boss.hrh"
#include "boss.h"

#include "bossview.h"

void CBossAppView::ConstructL(const TRect& aRect, TBossPuzzle* aModel)
    {
	iModel=aModel;
    CreateWindowL();
    Window().SetShadowDisabled(ETrue);
    iContext=this;
   	iBrushStyle=CGraphicsContext::ESolidBrush;
    iBrushColor=KRgbWhite;
    SetRectL(aRect);
	ConstructViewL();
    ActivateL();
    }

CBossAppView::~CBossAppView()
    {
	delete iControl;
	delete iView;
    }
    
TInt CBossAppView::CountComponentControls() const
	{
	return 1;
	}

CCoeControl* CBossAppView::ComponentControl(TInt aIndex) const
	{
	switch (aIndex)
		{
	case 0: return iControl;
	default: return 0;
		};
	}

void CBossAppView::ConstructViewL()
	{
	// make view
	iView=new (ELeave) CBossView(iModel);
	iView->ConstructL(iCoeEnv->ScreenDevice());
	// make control
	TSize viewSize=iView->GetSizeInPixels();
	TInt x=(Rect().Width() - viewSize.iWidth) / 2;
	TInt y=(Rect().Height() - viewSize.iHeight) / 2;
	TRect controlRect(TPoint(x,y), viewSize);
	iControl=new (ELeave) CBossControl(iModel, iView, this);
	iControl->ConstructL(this,controlRect);
	}

void CBossAppView::NotifyStatus(const TDesC& aMessage)
	{
	iEikonEnv->InfoMsg(aMessage);
	}

TKeyResponse CBossAppView::OfferKeyEventL(const TKeyEvent& aKeyEvent,TEventCode aType)
    {
	if	(iControl)
   		return ((CCoeControl*)iControl)->OfferKeyEventL(aKeyEvent,aType);
	else
		return EKeyWasNotConsumed;
    }

void CBossAppView::Draw(const TRect& /*aRect*/) const
	{
	CWindowGc& gc = SystemGc();
	gc.SetPenStyle(CGraphicsContext::ENullPen);
	gc.SetBrushStyle(CGraphicsContext::ESolidBrush);
	gc.DrawRect(Rect());
	}

// handle commands

void CBossAppView::Move(TBossPuzzle::TMoveType aMoveType)
	{
	if (!iModel->CanMove(aMoveType))
		{
		TInt message=
			aMoveType==TBossPuzzle::EUp ? R_BOSS_CANNOT_MOVE_UP :
			aMoveType==TBossPuzzle::EDown ? R_BOSS_CANNOT_MOVE_DOWN :
			aMoveType==TBossPuzzle::ELeft ? R_BOSS_CANNOT_MOVE_LEFT :
			aMoveType==TBossPuzzle::ERight ? R_BOSS_CANNOT_MOVE_RIGHT :
				R_EIK_TBUF_NOT_AVAILABLE;
		iEikonEnv->InfoMsg(message);
		return;
		}
	MoveChecked(aMoveType);
	}

void CBossAppView::BCOMove(TInt aRow, TInt aCol)
	{
	if (iModel->IsBlank(aRow,aCol))
		{
		iEikonEnv->InfoMsg(R_BOSS_CANNOT_MOVE_BLANK);
		return;
		}
	if (!iModel->CanMove(aRow,aCol))
		{
		iEikonEnv->InfoMsg(R_BOSS_CANNOT_MOVE_PIECE);
		return;
		}
	MoveChecked(iModel->CanMoveType(aRow,aCol));
	}

void CBossAppView::BCOMove(TBossPuzzle::TMoveType aMoveType)
	{
	Move(aMoveType);
	}

void CBossAppView::MoveChecked(TBossPuzzle::TMoveType aMoveType)
	{ // assume all parameters checked
	iControl->FinishMove(); // finish any existing move
	// move and minimal redraw
	TInt oldRow, oldCol, newRow, newCol;
	iModel->LocateBlank(oldRow, oldCol);
	iModel->Move(aMoveType);
	iModel->LocateBlank(newRow, newCol);
	iControl->MoveTile(oldRow, oldCol, newRow, newCol);
	// check complete
	if (iModel->IsFullyOrdered())
		iEikonEnv->InfoMsg(R_BOSS_CONGRATULATIONS);
	}

void CBossAppView::SetFullyOrdered()
	{
	iControl->FinishMove();
	iModel->SetFullyOrdered();
	iControl->DrawNow();
	iEikonEnv->InfoMsg(R_BOSS_FULLY_ORDERED);
	}

void CBossAppView::SetBossOrdered()
	{
	iControl->FinishMove();
	iModel->SetBossOrdered();
	iControl->DrawNow();
	iEikonEnv->InfoMsg(R_BOSS_BOSS_ORDERED);
	}

//
// CBossAppUi
//

void CBossAppUi::ConstructL()
    {
    BaseConstructL();
	iModel=((CBossDocument*)iDocument)->Model();
    iAppView=new(ELeave) CBossAppView;
    iAppView->ConstructL(ClientRect(),iModel);
	AddToStackL(iAppView); // app view should go onto control stack
    }

CBossAppUi::~CBossAppUi()
	{
	if (iDoorObserver)
		iDoorObserver->NotifyExit(MApaEmbeddedDocObserver::EKeepChanges);
    delete iAppView;
	}

void CBossAppUi::HandleCommandL(TInt aCommand)
	{
	switch (aCommand)
		{
	// game control
	case EBossUp:
		iAppView->Move(TBossPuzzle::EUp);
		return;
	case EBossDown:
		iAppView->Move(TBossPuzzle::EDown);
		return;
	case EBossLeft:
		iAppView->Move(TBossPuzzle::ELeft);
		return;
	case EBossRight:
		iAppView->Move(TBossPuzzle::ERight);
		return;
	case EBossSetFullyOrdered:
		iAppView->SetFullyOrdered();
		return;
	case EBossSetBossOrdered:
		iAppView->SetBossOrdered();
		return;
	// printing
	case EBossCmdPageSetup:
		HandleCmdPageSetupL();  
		return;
	case EEikCmdPrintPrint:
		HandleCmdPrintL();
		return;
	case EEikCmdPrintSetup: 
		HandleCmdPrintSetupL();
		return;
	case EEikCmdPrintPreview:
		HandleCmdPrintPreviewL();
		return;
	// file/app
	case EEikCmdExit: 
		SaveL();
		Exit();
		return;
		}
	}

// file handling

TBool CBossAppUi::ProcessCommandParametersL(TApaCommand aCommand, TFileName& aDocumentName, const TDesC& /*aTail*/)
	{
	if (aCommand==EApaCommandCreate)
		return EFalse; // assume aDocumentName is valid
	if (aCommand==EApaCommandRun || aCommand==EApaCommandBackground)
		{
		TFileName fileName=iEikonEnv->Process()->LastUsedFileL(CEikProcess::ELastOpenFile);
		if (ConeUtils::FileExists(fileName))
			{
			aDocumentName=fileName;
			return(ETrue);
			}
		Application()->GetDefaultDocumentFileName(aDocumentName);
		}
	return ConeUtils::FileExists(aDocumentName);
	}

void CBossAppUi::HandleModelChangeL()
	{
	iAppView->DrawNow();
	// filename on toolbar
	iEikonEnv->UpdateTaskNameL();
	CEikFileNameLabel* filenameLabel=(CEikFileNameLabel*) iToolBar->ControlById(EBossCmdFileName);
	filenameLabel->UpdateL();
	filenameLabel->DrawNow();
	}

TFileName CBossAppUi::MainDocPath() const
	{
	TParsePtrC parser(Document()->Process()->MainDocFileName());
	return parser.DriveAndPath();
	}

// printing

void CBossAppUi::HandleCmdPageSetupL()
	{
	CPrintSetup* printSetup=BossDocument()->PrintSetup();
	CEikDialog* pageSetup=new(ELeave) CEikPageSetupDialog(printSetup);
	pageSetup->ExecuteLD(R_EIK_DIALOG_PAGE_SPEC);
	}

void CBossAppUi::HandleCmdPrintPreviewL()
	{
	TInt numPages=BossDocument()->NumPages(); // will only ever be 1
	TInt numBands=1; // single band
	CEikDialog* dialog=new(ELeave) CEikPrintPreviewDialog(
			*BossDocument()->PrintSetup(),
			*BossDocument()->BossBandPrinter(),
			numPages,this,numBands
			);
	if (dialog->ExecuteLD(R_EIK_DIALOG_PRINT_PREVIEW))
		{ // user chose to print; PageSetupChangedL() may have been called
		DoPrintL(BossDocument()->iPrintParameters); 
		}
	}

void CBossAppUi::HandleCmdPrintL()
	{
	if (!DoPrintSetup(BossDocument()->PrintSetup(), ETrue)) // print setup, really want to print
		return; // cancelled
	DoPrintL(BossDocument()->iPrintParameters);
	}

void CBossAppUi::HandleCmdPrintSetupL()
	{
	DoPrintSetup(BossDocument()->PrintSetup(), EFalse); // do print setup, don't print
	}

void CBossAppUi::DoPrintL(const TPrintParameters& aPrintParameters)
	{
	CEikDialog* progressDialog=new(ELeave) CEikPrintProgressDialog(
			BossDocument()->PrintSetup(),BossDocument()->BossBandPrinter(),aPrintParameters
			);
	progressDialog->ExecuteLD(R_EIK_DIALOG_PRINT_PROGRESS);
	}

TBool CBossAppUi::DoPrintSetup(CPrintSetup* aPrintSetup, TBool aPrintWanted)
	{
	TUid initialPrinterModelUid=aPrintSetup->PrinterDevice()->Model().iUid;
	TUid printerModelUid=initialPrinterModelUid;
	CEikDialog* dialog=new(ELeave) CEikPrintRangeDialog(
		BossDocument()->iPrintParameters,aPrintSetup,printerModelUid,
		aPrintWanted
		);
	if (!dialog->ExecuteLD(R_EIK_DIALOG_PRINT_RANGE_SETUP))
		return EFalse; // cancelled
	if (printerModelUid!=initialPrinterModelUid)
		aPrintSetup->CreatePrinterDeviceL(printerModelUid,iEikonEnv->FsSession());
	return ETrue;
	}

TBool CBossAppUi::RunPrintRangeDialogL(CPrintSetup* aPrintSetup, TInt& aNumPagesInDoc)
	{ // Called when user chooses print setup from print preview dialog 
	if (!DoPrintSetup(aPrintSetup, EFalse)) // do print setup, don't print
		return EFalse; // cancelled
	aNumPagesInDoc=BossDocument()->NumPages(); // potentially, update number of pages
	return EFalse; // number of pages hasn't changed?
	}

TBool CBossAppUi::PageSetupChangedL(CPrintSetup* /*aPrintSetup*/, TInt& aNumOfPages)
	{ // Called from print preview when page setup changes
	aNumOfPages=BossDocument()->NumPages();
	return EFalse; // hasn't changed?
	}

//
// CBossDocument
//

void CBossDocument::ConstructL()
	{
	InitializePrintSetupL(); // Initialize
	iBossPrinter= new(ELeave) CBossBandPrinter(&iModel, iPrintSetup);
	iBossPrinter->ConstructL();
	}

CBossDocument::~CBossDocument()
	{
	delete iPrintSetup;
	delete iBossPrinter;
	}

void CBossDocument::InitializePrintSetupL()
	{
	// constant declarations
	_LIT(KSearchPath,"\\system\\printers\\");
	// delete old setup, if any
	delete iPrintSetup;
	iPrintSetup=0;
	// allocate new setup
	iPrintSetup=CPrintSetup::NewL(); // Allocate and construct, no initialization
	// put in initial values
	RFs fs;
	User::LeaveIfError(fs.Connect()); // Connect to file server session
	TFileName searchPath=KSearchPath;
	iPrintSetup->AddPrinterDriverDirL(searchPath); 
		// provides search path for printer drivers
	// get list of models - causes list to be allocated
	iPrintSetup->ModelNameListL(fs);
	fs.Close();
	// create a printer device - first from list - random choice
	iPrintSetup->CreatePrinterDeviceL(0);
	// free models list
	iPrintSetup->FreeModelList();
	TPageMargins margins;									 
	margins.iMargins.iLeft=1440;
	margins.iMargins.iRight=1440;			
	margins.iMargins.iTop=1440;		   
	margins.iMargins.iBottom=1440;
	margins.iHeaderOffset=720;
	margins.iFooterOffset=720;
	iPrintSetup->iNumOfFirstPage=1;			 	
	iPrintSetup->Header()->CreateTextL();
	iPrintSetup->Footer()->CreateTextL();
	TPageSpec pageSpec;
	pageSpec.iPortraitPageSize=TSize(11906,16838); // A4 	
	iPrintSetup->iPageMarginsInTwips=margins;
  	iPrintSetup->PrinterDevice()->SelectPageSpecInTwips(pageSpec);// A4
	// now set how many pages
	iNumPages=1; // Document consists of a single page only
	}

CEikAppUi* CBossDocument::CreateAppUiL()
	{
    return(new(ELeave) CBossAppUi);
	}

void CBossDocument::RestoreL(const CStreamStore& aStore,const CStreamDictionary& aStreamDic)
	{
	TStreamId id;
	// restore model
	id=aStreamDic.At(KUidBossApp);
	iModel.RestoreL(aStore, id);
	// restore print setup
	id=aStreamDic.At(KUidBossPrintSetupStream);
	if (id==KNullStreamId)
		InitializePrintSetupL();
	else
		iPrintSetup->RestoreL(aStore,id);
	}

void CBossDocument::StoreL(CStreamStore& aStore,CStreamDictionary& aStreamDic) const
	{
	TStreamId id=iModel.StoreL(aStore);
	aStreamDic.AssignL(KUidBossApp,id);
	if (iPrintSetup)
		{
		id=iPrintSetup->StoreL(aStore);
		aStreamDic.AssignL(KUidBossPrintSetupStream,id);
		}
	}

CApaDocument::TCapability CBossDocument::Capability()const
	{
	TCapability capability;
	capability.SetCanDrawGlass();
	return capability;
	}

CPicture* CBossDocument::GlassPictureL()
	{
	CBossGlassDoor* door=new(ELeave) CBossGlassDoor(&iModel);
	CleanupStack::PushL(door);
	door->ConstructL();
	CleanupStack::Pop();
	return door;
	}

//
// CBossGlassDoor
//

CBossGlassDoor::CBossGlassDoor(TBossPuzzle* aModel)
	{
	iModel=aModel;
	iScaleFactorWidth=1000;
	iScaleFactorHeight=1000;
	}

void CBossGlassDoor::ConstructL()
	{
	iView=new (ELeave) CBossView(iModel);
	iView->ConstructL(0); // no device map
	}

CBossGlassDoor::~CBossGlassDoor()
	{
	delete iView;
	}

void CBossGlassDoor::GetOriginalSizeInTwips(TSize& aSize) const
	{
	iView->GetOriginalSizeInTwips(aSize);
	}

TPictureCapability CBossGlassDoor::Capability() const
	{
	return TPictureCapability(TPictureCapability::EFullyScaleable, EFalse);
		// supports scaling but no cropping
	}

void CBossGlassDoor::SetScaleFactor(TInt aScaleFactorWidth,TInt aScaleFactorHeight)
	{
	iScaleFactorWidth=aScaleFactorWidth;
	iScaleFactorHeight=aScaleFactorHeight;
	}

TInt CBossGlassDoor::ScaleFactorWidth() const
	{
	return(iScaleFactorWidth);
	}

TInt CBossGlassDoor::ScaleFactorHeight() const
	{
	return(iScaleFactorHeight);
	}

void CBossGlassDoor::Draw(CGraphicsContext& aGc,const TPoint& aTopLeft, const TRect& aClipRect, MGraphicsDeviceMap* aMap) const
	{
	// this function might leave!! - but name doesn't end in L!!
	iView->SetDeviceMapL(aMap);
	iView->Draw(aGc, aClipRect, aTopLeft);
	iView->SetDeviceMapL(0); // because map will soon be deallocated
	}

//
// CBossBandPrinter
//

CBossBandPrinter::CBossBandPrinter(TBossPuzzle* aModel,CPrintSetup* aPrintSetup)
	{
	iModel=aModel;
	iPrintSetup=aPrintSetup;
	}

void CBossBandPrinter::ConstructL()
	{
	iView=new (ELeave) CBossView(iModel);
	iView->ConstructL(0); // no device map
	}

CBossBandPrinter::~CBossBandPrinter()
	{
	delete iView;
	}

void CBossBandPrinter::PrintBandL(CGraphicsDevice* aLayoutDevice,TInt /*aPageNo*/, const TBandAttributes& /*aBand*/)
	{
	// Draw board at top left hand corner of printable page (the area to draw to),
	// taking into account the page margins, and converting all coordinates 
	// from twips to pixels
	CGraphicsContext* gc;
	aLayoutDevice->CreateContext(gc);
	CleanupStack::PushL(gc);
	iView->SetDeviceMapL(aLayoutDevice);
	// Get physical size of printable page, taking into account 
	// page orientation, then convert to pixels
	TSize orientedPageSize=iPrintSetup->PrinterDevice()->CurrentPageSpecInTwips().OrientedPageSize();
	orientedPageSize.iWidth=aLayoutDevice->HorizontalTwipsToPixels(orientedPageSize.iWidth);
    orientedPageSize.iHeight=aLayoutDevice->VerticalTwipsToPixels(orientedPageSize.iHeight);
	TRect printablePageRect(TPoint(0,0),orientedPageSize); // rectangle to draw to
	// remove margin widths from the printable page
    TInt leftMarginWidthInPixels=aLayoutDevice->HorizontalTwipsToPixels(
			iPrintSetup->iPageMarginsInTwips.iMargins.iLeft);
    TInt rightMarginWidthInPixels=aLayoutDevice->HorizontalTwipsToPixels(
			iPrintSetup->iPageMarginsInTwips.iMargins.iRight);
    TInt topMarginWidthInPixels=aLayoutDevice->VerticalTwipsToPixels(
			iPrintSetup->iPageMarginsInTwips.iMargins.iTop);
    TInt bottomMarginWidthInPixels=aLayoutDevice->VerticalTwipsToPixels(
			iPrintSetup->iPageMarginsInTwips.iMargins.iBottom);
	printablePageRect.iTl+=TPoint(leftMarginWidthInPixels,topMarginWidthInPixels);
	printablePageRect.iBr-=TPoint(rightMarginWidthInPixels,bottomMarginWidthInPixels);

	// Offset required to avoid drawing the board over top and left margins
	TPoint offsetInPixels(printablePageRect.iTl); 
	gc->SetClippingRect(printablePageRect); // clip all drawing to printable page area
	// Draw to printable page in pixels. 
	iView->Draw(*(gc),printablePageRect,offsetInPixels);
	// cleanup the GC
	iView->SetDeviceMapL(0); // because map will soon be deallocated
	CleanupStack::PopAndDestroy(); // gc
	}

//
// CBossApplication
//

TUid CBossApplication::AppDllUid() const
	{
	return KUidBossApp;
	}

CApaDocument* CBossApplication::CreateDocumentL()
	{
	CBossDocument* document=new(ELeave) CBossDocument(*this);
	CleanupStack::PushL(document);
	document->ConstructL();
	CleanupStack::Pop();
	return document;
	}

//
// EXPORTed functions
//

EXPORT_C CApaApplication* NewApplication()
	{
	return new CBossApplication;
	}

GLDEF_C TInt E32Dll(TDllReason)
	{
	return KErrNone;
	}
