// BOSSVIEW.CPP
//
// Copyright (c) 1998 Symbian Ltd.  All rights reserved.
//

#include "bossview.h"
#include "bosseng.h"

#include <coemain.h>

EXPORT_C CBossView::CBossView(TBossPuzzle* aModel)
	: iModel(aModel)
	{
	}

EXPORT_C CBossView::CBossView()
	{
	}

EXPORT_C void CBossView::ConstructL(MGraphicsDeviceMap* aDeviceMap)
	{
	SetDeviceMapL(aDeviceMap);
	}

CBossView::~CBossView()
	{
	SetDeviceMapL(0);
	}

// constants

#define KBossTileSizeInTwips TPoint(480,480) // 1/3 inch square
#define KBossTileBorderInTwips TPoint(40,40) // 2 points
#define KBossBoardMarginInTwips TPoint(120,120) // 6 points = 1/12 inch

// handy utility function
TPoint operator*(TInt aScale, const TPoint& aPoint)
	{ return TPoint(aScale*aPoint.iX, aScale*aPoint.iY); }

EXPORT_C void CBossView::SetDeviceMapL(MGraphicsDeviceMap* aDeviceMap)
	{
	//constant declaration
	_LIT(KFontArial,"Arial");

	// check for significant change
	const TPoint p(10000,10000);
	if (iDeviceMap && aDeviceMap && aDeviceMap->TwipsToPixels(p)==iDeviceMap->TwipsToPixels(p))
		return;
	// release old font
	ReleaseTileFont();
	// set new device map
	iDeviceMap=aDeviceMap;
	// quit if no map
	if (!iDeviceMap)
		return;
	// tile size
	iTileSizeInPixels=iDeviceMap->TwipsToPixels(KBossTileSizeInTwips);
	iTileBorderInPixels=iDeviceMap->TwipsToPixels(KBossTileBorderInTwips);
	// board margin and size
	iBoardMarginInPixels=iDeviceMap->TwipsToPixels(KBossBoardMarginInTwips);
	iBoardSizeInPixels=4*iTileSizeInPixels+2*iBoardMarginInPixels;
	TFontSpec bossTileFontSpec (KFontArial, 12*20); // 12 points = 1/6 inch
	bossTileFontSpec.iTypeface.SetIsProportional(ETrue); // Arial is proportional 
	// tile font
	User::LeaveIfError(iDeviceMap->GetNearestFontInTwips(iTileFont,bossTileFontSpec));
	}

void CBossView::GetBoardRect(TRect& aBoardRect) const
	{
	aBoardRect=TRect(iTopLeft, iBoardSizeInPixels.AsSize());
	}

void CBossView::DrawBackground(const TRect& aRect) const
	{
	TRect boardOuterRect;
	GetBoardRect(boardOuterRect);
	iGc->SetPenStyle(CGraphicsContext::ENullPen);
	iGc->SetBrushStyle(CGraphicsContext::ESolidBrush);
	iGc->SetBrushColor(KRgbGray);
	if (boardOuterRect.iBr.iX < aRect.iBr.iX) // fill to the right
		{
		TRect r(TPoint(boardOuterRect.iBr.iX, boardOuterRect.iTl.iY),
			TPoint(aRect.iBr.iX, boardOuterRect.iBr.iY));
		iGc->DrawRect(r);
		}
	if (boardOuterRect.iBr.iY < aRect.iBr.iY) // fill below, and below-right if needed
		{
		TRect r(TPoint(boardOuterRect.iTl.iX, boardOuterRect.iBr.iY), aRect.iBr);
		iGc->DrawRect(r);
		}
	}

void CBossView::DrawBoard() const
	{
	// calculate rectangle to draw into
	TRect boardOuterRect;
	GetBoardRect(boardOuterRect);
	TRect boardInnerRect=boardOuterRect;
	boardInnerRect.Shrink(iBoardMarginInPixels.AsSize()-TSize(1,1));
	// draw board border
	iGc->SetBrushStyle(CGraphicsContext::ESolidBrush);
	iGc->SetBrushColor(KRgbDarkGray);
	iGc->SetPenStyle(CGraphicsContext::ESolidPen);
	iGc->SetPenColor(KRgbBlack);
	iGc->DrawRect(boardOuterRect);
	// nice white inner border
	iGc->SetBrushStyle(CGraphicsContext::ENullBrush);
	iGc->SetPenColor(KRgbWhite);
	iGc->DrawRect(boardInnerRect);
	}

void CBossView::GetTileRect(TRect& aTileRect, TInt aRow, TInt aCol) const
	{
	TPoint tileTopLeft=iTopLeft+iBoardMarginInPixels;
	aTileRect=
		TRect(
			tileTopLeft+TPoint(aCol*iTileSizeInPixels.iX,aRow*iTileSizeInPixels.iY),
			iTileSizeInPixels.AsSize()
			);
	}

void CBossView::DrawTile(TInt aRow, TInt aCol) const
	{ 
	// constant declaration
	_LIT(KNumFormat,"%d");

	// tile or blank
	TRect tileRect;
	GetTileRect(tileRect, aRow, aCol);
	if (iModel->Tile(aRow,aCol)!=0) // real tile
		{
		iGc->UseFont(iTileFont);
		// draw rectangle
		iGc->SetBrushStyle(CGraphicsContext::ESolidBrush);
		iGc->SetBrushColor(KRgbGray);
		iGc->SetPenStyle(CGraphicsContext::ESolidPen);
		iGc->SetPenColor(KRgbBlack);
		iGc->DrawRect(tileRect);
		iGc->SetPenColor(KRgbWhite);
		iGc->DrawLine(TPoint(tileRect.iTl.iX,tileRect.iBr.iY-2),tileRect.iTl);
		iGc->DrawLine(tileRect.iTl,TPoint(tileRect.iBr.iX-1,tileRect.iTl.iY));
		tileRect.Shrink(iTileBorderInPixels.AsSize());
		iGc->SetBrushColor(KRgbWhite);
		iGc->SetPenColor(KRgbBlack);
		// draw number
		TInt ascent=
				(tileRect.Height() - iTileFont->HeightInPixels())/2 +
					iTileFont->AscentInPixels();
		// draw text in rectangle
		TBuf<2> number;
		number.Format(KNumFormat,TInt(iModel->Tile(aRow,aCol)));
		iGc->DrawText(number, tileRect, ascent, CGraphicsContext::ECenter, 0);
		iGc->DiscardFont();
		}
	else // blank
		{
		iGc->SetBrushStyle(CGraphicsContext::ESolidBrush);
		iGc->SetBrushColor(KRgbGray);
		iGc->SetPenStyle(CGraphicsContext::ENullPen);
		iGc->DrawRect(tileRect);
		}
	}

EXPORT_C void CBossView::GetOriginalSizeInTwips(TSize& aSize) const
	{
	TPoint size=4*KBossTileSizeInTwips+2*KBossBoardMarginInTwips;
	aSize=size.AsSize();
	}

void CBossView::ReleaseTileFont()
	{
	if (iTileFont)
		{
		iDeviceMap->ReleaseFont(iTileFont);
		iTileFont=0;
		}
	}

EXPORT_C void CBossView::Draw(CGraphicsContext& aGc, const TRect& aRect, const TPoint& aTopLeft) const
	{
	// remember values
	CBossView* mutableThis=(CBossView*) this;
	mutableThis->iGc=&aGc;
	mutableThis->iTopLeft=aTopLeft;
	// draw components
	DrawBackground(aRect);
	DrawBoard();
	for (TInt row=0; row<4; row++)
		for (TInt col=0; col<4; col++)
			DrawTile(row,col);
	}

/*
	CBossControl
*/

EXPORT_C CBossControl::CBossControl(TBossPuzzle* aModel, CBossView* aView)
	: iModel(aModel), iView(aView)
	{
	}

EXPORT_C CBossControl::CBossControl()
	{
	}

EXPORT_C void CBossControl::ConstructL(const CCoeControl* aParent, const TRect& aRect)
	{
	// create window - a lodger control
	CreateWindowL(aParent);
	SetRectL(aRect);
	// go for it
	ActivateL();
	}

CBossControl::~CBossControl()
	{
	}

void CBossControl::Draw(const TRect& /* aRect */) const
	{
	// clear board altogether
	CWindowGc& gc=SystemGc();
	gc.DrawRect(Rect());
	iView->Draw(gc, Rect(), TPoint(2,2));
   	}

EXPORT_C void CBossControl::MoveTile(TInt aOldBlankRow, TInt aOldBlankCol, TInt aNewBlankRow, TInt aNewBlankCol) const
	{
	// find bounding rectangle of old and new
	TRect oldBlankRect;
	iView->GetTileRect(oldBlankRect, aOldBlankRow, aOldBlankCol);
	TRect newBlankRect;
	iView->GetTileRect(newBlankRect, aNewBlankRow, aNewBlankCol);
	TRect invalidRect=oldBlankRect;
	invalidRect.BoundingRect(newBlankRect);
	// draw-now sequence, with invalid region set to bounding rectangle
	ActivateGc();
	Window().Invalidate(invalidRect);
	Window().BeginRedraw(invalidRect);
	iView->DrawTile(aOldBlankRow, aOldBlankCol); // no longer blank
	iView->DrawTile(aNewBlankRow, aNewBlankCol); // now blank
	Window().EndRedraw();
	DeactivateGc();
	}

// required as a DLL

EXPORT_C TInt E32Dll(TDllReason)
	{
	return 0;
	}
