// DIGIT.CPP
//
// Copyright (c) 1997-1999 Symbian Ltd.  All rights reserved.
//

#include <apaflrec.h>
#include <eikenv.h>
#include <coecntrl.h>
#include <eikappui.h>
#include <eikmsg.h>
#include <e32keys.h>
#include <eikdef.h>
#include <eikdutil.h>
#include <eikbordr.h>
#include <coeutils.h>
#include <eikdoc.h>
#include <eikapp.h>
#include <e32hal.h>
#include <eikdialg.h>
#include <eiklabel.h>
#include <coealign.h>
#include <basched.h>

#include <eikcmds.hrh>
#include <digit.rsg>
#include "digit.hrh"

const TInt KNumberOfControl=1;
const KInitialTolerance = 20;
const TInt KDraw4Circles = 19;
const TInt KDraw3Circles = 15;
const TInt KDraw2Circles = 11;
const TInt KDraw1Circle = 7;
const TInt KMaxArray = 4;
const TInt KNumberOfPoints=3;
const TInt K3IntArray[KMaxArray] = {KDraw1Circle, KDraw2Circles, KDraw3Circles, KDraw4Circles};

enum TTDigitPanic
	{
	ETDigitPanicGetiTouched
	};

LOCAL_C void Panic(TTDigitPanic aPanic)
	{
	User::Panic(_L("Digit-Calib"), aPanic);
	}

//
// class CSimpleControl
//

class CSimpleControl : public CCoeControl
    {
	friend class CDigitInfoDialog;
public:
	~CSimpleControl();
    void ConstructL();
	void Reset();
	TInt &Tolerance();
protected:
	void HandlePointerEventL(const TPointerEvent& aPointerEvent);
private: // framework
	void Draw(const TRect& aRect) const;
    TInt CountComponentControls() const;
    CCoeControl* ComponentControl(TInt /* aIndex */)const;
private: // new function
	TPoint& GetiTouched(TInt aNum);
	const TPoint& GetiCal(TInt aNum) const;
	void DrawOneBlob(TInt aPos,const TRgb &aColor) const;
	void DrawCircle(TRect &aRect,const TRgb &aColor) const;
	void InitRect();
	void PointerDraw(const TPtrC &aStr) const;
//	void InfoMsg(TInt aResourceId,...);
	static TInt TimerCallBack(TAny* aInstance);
private:
	CDigitInfoDialog *iDigitInfoDialog;
	TDigitizerCalibration iCal;
	TDigitizerCalibration iTouched;
	TInt iCurrent;
	TInt iTolerance;
	TPoint iLast;
	CPeriodic* iTimer;
	TInt iCSize;
	};

class CDigitInfoDialog : public CEikDialog
	{
public:
	CDigitInfoDialog(CSimpleControl* aSimpleControl):iSimpleControl(aSimpleControl) {}
	void SetSizeAndPositionL(const TSize& aSize);
	// new functions
	void SetLineText(TInt aNum);
private:
	void PreLayoutDynInitL();
	void PostLayoutDynInitL();
private:
	CSimpleControl* iSimpleControl;
	};

void CDigitInfoDialog::PreLayoutDynInitL()
	{
	TBuf<80> formatstr,str;
	iEikonEnv->ReadResource(formatstr,R_DIGIT_LAST_POINT_TOUCHED);
	str.Format(formatstr,9,999,999);
	SetLabelL(EDigitText1,str);

	iEikonEnv->ReadResource(formatstr,R_DIGIT_POINT_X);
	str.Format(formatstr,1,iSimpleControl->iCal.iTl.iX,iSimpleControl->iCal.iTl.iY);
	SetLabelL(EDigitText2,str);

	iEikonEnv->ReadResource(formatstr,R_DIGIT_POINT_X);
	str.Format(formatstr,2,iSimpleControl->iCal.iBl.iX,iSimpleControl->iCal.iBl.iY);
	SetLabelL(EDigitText3,str);

	iEikonEnv->ReadResource(formatstr,R_DIGIT_POINT_X);
	str.Format(formatstr,3,iSimpleControl->iCal.iBr.iX,iSimpleControl->iCal.iBr.iY);
	SetLabelL(EDigitText4,str);
	}

void CDigitInfoDialog::PostLayoutDynInitL()
	{
	TBuf<80> formatstr,str;
	iEikonEnv->ReadResource(formatstr,R_DIGIT_TOUCH_POINT_X);
	str.Format(formatstr,1);
	SetLabelL(EDigitText1,str);
	}

void CDigitInfoDialog::SetLineText(TInt aNum)
	{
	TBuf<80> formatstr,str;
	TInt item;

	if (iSimpleControl->iLast.iX==0)  // nothing touched at the start
		return;

	if (aNum==2)
		item=R_DIGIT_COMPLETED;
	else
		item=R_DIGIT_LAST_POINT_TOUCHED;
	iEikonEnv->ReadResource(formatstr,item);
	TInt pointX=iSimpleControl->iCurrent+1;
	if (aNum!=-1)
		pointX++;
	str.Format(formatstr,pointX,iSimpleControl->iLast.iX,iSimpleControl->iLast.iY);
	SetLabelL(EDigitText1,str);

	if (aNum==-1)
		return;

	iEikonEnv->ReadResource(formatstr,R_DIGIT_TOUCH_X);
	
	switch (aNum)
		{
	case 0:
		item=EDigitText2;
		str.Format(formatstr,aNum+1,iSimpleControl->iCal.iTl.iX,
									iSimpleControl->iCal.iTl.iY,
									iSimpleControl->iTouched.iTl.iX,
									iSimpleControl->iTouched.iTl.iY);
		break;
	case 1:
		item=EDigitText3;
		str.Format(formatstr,aNum+1,iSimpleControl->iCal.iBl.iX,
									iSimpleControl->iCal.iBl.iY,
									iSimpleControl->iTouched.iBl.iX,
									iSimpleControl->iTouched.iBl.iY);
		break;
	case 2:
	default:
		item=EDigitText4;
		str.Format(formatstr,aNum+1,iSimpleControl->iCal.iBr.iX,
									iSimpleControl->iCal.iBr.iY,
									iSimpleControl->iTouched.iBr.iX,
									iSimpleControl->iTouched.iBr.iY);
		}
	SetLabelL(item,str);
	}

void CDigitInfoDialog::SetSizeAndPositionL(const TSize& aSize)
	{
    SetCornerAndSizeL(EHRightVTop,aSize);
	}

/*********************************************************************************/

CSimpleControl::~CSimpleControl()
	{
	delete iDigitInfoDialog;
	delete(iTimer);
	}

TInt CSimpleControl::CountComponentControls() const
	{
	return KNumberOfControl;
	}

CCoeControl* CSimpleControl::ComponentControl(TInt /* aIndex */ ) const
	{
	return (iDigitInfoDialog);
	}

const TInt KEikBusyMsgDelay=500000;
const TInt KFrequency=1000;
const TInt KDuration=100000;

void CSimpleControl::ConstructL()
    {
	iTimer=CPeriodic::NewL(0);
	CreateWindowL();
	iTolerance=KInitialTolerance;
	InitRect();
	iDigitInfoDialog=new(ELeave) CDigitInfoDialog(this);
	iDigitInfoDialog->ExecuteLD(R_DIGIT_INFO_DIALOG);
    SetExtentToWholeScreenL();
	ActivateL();
	iTimer->Start(KEikBusyMsgDelay,KEikBusyMsgDelay,TCallBack(TimerCallBack,this));
	}

TInt CSimpleControl::TimerCallBack(TAny* aThis)
	{ // static
	CSimpleControl* thisptr=(CSimpleControl*)aThis;
	if (thisptr->iCurrent<KNumberOfPoints)
		{
		thisptr->ActivateGc();
		thisptr->DrawOneBlob(thisptr->iCurrent,KRgbBlack);
		thisptr->DeactivateGc();
		thisptr->iCSize=((thisptr->iCSize+1)%KMaxArray);
		}
	return(NULL); // since TCallBack insists on a TInt return function!
	}

TInt& CSimpleControl::Tolerance()
	{
	return iTolerance;
	}

void CSimpleControl::Reset()
	{
	InitRect();
	ActivateGc();
	Draw(Rect());
	DeactivateGc();
	}

void CSimpleControl::InitRect() 
	{
	iCurrent=0;
	iLast=TPoint(0,0);
	iCSize=0;
	UserHal::CalibrationPoints(iCal);
	}
	
void CSimpleControl::Draw(const TRect& /*aRect*/) const 
	{
	CWindowGc& gc = SystemGc();
	gc.SetBrushStyle(CGraphicsContext::ESolidBrush);
	gc.DrawRect(Rect());  

	gc.UseFont(iCoeEnv->NormalFont());
	gc.SetPenColor(KRgbBlack);
	gc.DrawText(_L("1"),TPoint(iCal.iTl.iX+K3IntArray[KMaxArray-1],iCal.iTl.iY));
	gc.DrawText(_L("2"),TPoint(iCal.iBl.iX+K3IntArray[KMaxArray-1],iCal.iBl.iY));
	gc.DrawText(_L("3"),TPoint(iCal.iBr.iX+K3IntArray[KMaxArray-1],iCal.iBr.iY));
	
	for (TInt i=0;i<KNumberOfPoints;i++)
		{
		if (i==iCurrent)
			DrawOneBlob(i,KRgbBlack);
		else
			DrawOneBlob(i,KRgbGray);
		}
	}
	
void CSimpleControl::DrawCircle(TRect &aRect,const TRgb &aColor) const
	{
	CWindowGc& gc = SystemGc();
	gc.SetBrushStyle(CGraphicsContext::ESolidBrush);
	gc.SetBrushColor(aColor);
	gc.SetPenStyle(CGraphicsContext::ENullPen);  // don't draw outline 
	aRect.Shrink(2,2);
	gc.DrawEllipse(aRect);
	}

void CSimpleControl::DrawOneBlob(TInt aPos,const TRgb &aColor) const
	{
	TPoint rPoint=GetiCal(aPos);
	TRect rect=TRect(rPoint,TSize(1,1));
	TInt circleIndx;
	if (aColor==KRgbGray)
		circleIndx=KMaxArray-1;
	else
		circleIndx=iCSize;

	if (circleIndx<KMaxArray-1)
		{  /* clear old drawing */
		rect.Grow(K3IntArray[KMaxArray-1],K3IntArray[KMaxArray-1]);
		DrawCircle(rect,KRgbWhite);
		rect.Shrink(K3IntArray[KMaxArray-1],K3IntArray[KMaxArray-1]); // restore rect size
		}

	rect.Grow(K3IntArray[circleIndx],K3IntArray[circleIndx]);
	for (TInt i=0;i<=circleIndx;i++)
		{
		DrawCircle(rect,aColor);
		DrawCircle(rect,KRgbWhite);
		}
	DrawCircle(rect,aColor);
	}

const TPoint& CSimpleControl::GetiCal(TInt aNum) const
	{
//	__ASSERT_ALWAYS(aNum<=2 && aNum>=0, Panic(ETDigitPanicGetPoint));
	switch (aNum)
		{
	case 0:
		return (iCal.iTl);
	case 1:
		return (iCal.iBl);
	default:
		return (iCal.iBr);
		}
	}

TPoint& CSimpleControl::GetiTouched(TInt aNum) 
	{
	__ASSERT_ALWAYS(aNum<=2 && aNum>=0, Panic(ETDigitPanicGetiTouched));
	switch (aNum)
		{
	case 0:
		return ( iTouched.iTl);
	case 1:
		return ( iTouched.iBl);
	default:
		return ( iTouched.iBr);
		}
	}

void CSimpleControl::HandlePointerEventL(const TPointerEvent& aPointerEvent)
	{
	if (aPointerEvent.iType==TPointerEvent::EButton1Down)
		{
		iEikonEnv->InfoMsgCancel();
		iLast=aPointerEvent.iPosition;

		TPoint rPoint = GetiCal(iCurrent);
		TRect currect=TRect(rPoint,TSize(1,1));
		currect.Grow(iTolerance,iTolerance);
		if (!currect.Contains(aPointerEvent.iPosition))
			{
			iDigitInfoDialog->SetLineText(-1);
			iEikonEnv->InfoMsgWithAlignment(EHRightVTop,R_DIGIT_TOO_FAR_FROM_PT,iCurrent+1);
			CEikonEnv::Beep();
			}
		else
			{
			User::Beep(KFrequency,TTimeIntervalMicroSeconds32(KDuration));
			GetiTouched(iCurrent) = iLast;
			iDigitInfoDialog->SetLineText(iCurrent);
			iCurrent++;
			if (iCurrent==KNumberOfPoints)
				{	
				iTimer->Cancel();
				DrawNow();
				UserHal::SetXYInputCalibration(iTouched);
				iEikonEnv->InfoWinL(R_DIGIT_COMPLETED,0);
				CBaActiveScheduler::Exit();
				}
			}
		DrawNow();
		}
	}


// 
// CSetToleranceDialog
//

class CSetToleranceDialog : public CEikDialog 
	{
public:
	 CSetToleranceDialog(TInt &aPTolerance) : iPTolerance(aPTolerance) {}
private: 
	void PreLayoutDynInitL();
	TBool OkToExitL(TInt aKeycode);
private:
	TInt &iPTolerance;
	};

void CSetToleranceDialog::PreLayoutDynInitL()
	{
	SetNumberEditorValue(ESetToleranceEditor,iPTolerance);
	}

TBool CSetToleranceDialog::OkToExitL(TInt /* aKeyId */ )
	{
	iPTolerance=NumberEditorValue(ESetToleranceEditor);
	return ETrue;
	}

//
// CSimpleAppUi
//

class CSimpleAppUi : public CEikAppUi
    {
public:
    void ConstructL();
	~CSimpleAppUi();
private: // framework
	void HandleCommandL(TInt aCommand);
private: //new
	void CmdSetToleranceDialog();
private:
    CSimpleControl* iSimpleControl;
    };

void CSimpleAppUi::ConstructL()
    {
    BaseConstructL();
	iSimpleControl=new(ELeave) CSimpleControl;
	iSimpleControl->ConstructL();
	AddToStackL(iSimpleControl);
    }

CSimpleAppUi::~CSimpleAppUi()
	{
    delete iSimpleControl;
	}

void CSimpleAppUi::HandleCommandL(TInt aCommand)
	{
    switch (aCommand)
        {
    case EEikCmdExit:
	    CBaActiveScheduler::Exit();
        break;
	case ECmdSetTolerance:
		CmdSetToleranceDialog();
		}
    }

void CSimpleAppUi::CmdSetToleranceDialog()
	{
	CSetToleranceDialog* dialog=new(ELeave) CSetToleranceDialog(iSimpleControl->Tolerance());
	if (dialog->ExecuteLD(R_DIGIT_SET_TOLERANCE_DIALOG))
		{
		iSimpleControl->Reset();	
		}	
	}

//
// CSimpleDocument
//

class CSimpleDocument : public CEikDocument
	{
public:
	CSimpleDocument(CEikApplication& aApp): CEikDocument(aApp) { }
private: // from CEikDocument
	CEikAppUi* CreateAppUiL();
	};

CEikAppUi* CSimpleDocument::CreateAppUiL()
	{
    return(new(ELeave) CSimpleAppUi);
	}

//
// CDigitApplication
//

class CDigitApplication : public CEikApplication
	{
private: // from CApaApplication
	CApaDocument* CreateDocumentL();
	TUid AppDllUid() const;
	};

const TUid KUidSimpleApp={225};

TUid CDigitApplication::AppDllUid() const
	{
	return(KUidSimpleApp);
	}
	
CApaDocument* CDigitApplication::CreateDocumentL()
	{
	return(new(ELeave) CSimpleDocument(*this));
	}

//
// EXPORTed functions
//

EXPORT_C CApaApplication* NewApplication()
	{
	return(new CDigitApplication);
	}

GLDEF_C TInt E32Dll(TDllReason)
	{
	return(KErrNone);
	}

