// TSFORM.CPP
//
// Copyright (c) 1997-1999 Symbian Ltd.  All rights reserved.
//

#include <eikenv.h>
#include <eiksform.h>
#include <eikappui.h>
#include <e32keys.h>
#include <e32math.h>
#include <eikdef.h>
#include <basched.h>
#include <badesca.h>
#include <eikbutb.h>
#include <eikchlst.h>
#include <eikchlst.hrh>
#include <eikcmds.hrh>
#include <eikctrls.hrh>
#include <eikdialg.hrh>
#include <eikcapc.h>
#include <eikcapca.h>
#include <coeccntx.h>
#include <eikdialg.h>
#include <eikedwin.h>
#include <eikedwin.hrh>
#include <eikdoc.h>
#include <eikapp.h>

#include <eikon.rsg>
#include <tsform.rsg>
#include "tsform.hrh"

typedef CArrayVarFlat<TFieldType> CFieldTypeArray;
typedef CArrayVarFlat<TAny> CBabyDatabase;

struct SPlainTextTypeEntry
	{
	TFieldType iType;
	TBuf<10> iPlainText;
	TBuf<20> iPrompt;
	};

struct SRichTextTypeEntry
	{
	TFieldType iType;
	TBuf<25> iRichText;
	TBuf<20> iPrompt;
	};

struct SNumericTypeEntry
	{
	TFieldType iType;
	TInt iNumber;
	TBuf<20> iPrompt;
	};

struct SFloatingPointTypeEntry
	{
	TFieldType iType;
	TReal iReal;
	TBuf<20> iPrompt;
	};

struct STimeTypeEntry
	{
	TFieldType iType;
	TTime iTime;
	TBuf<20> iPrompt;
	};

struct SDateTypeEntry
	{
	TFieldType iType;
	TTime iDate;
	TBuf<20> iPrompt;
	};

struct SCheckTypeEntry
	{
	TFieldType iType;
	TBool iSet;
	TBuf<20> iPrompt;
	};

struct SListTypeEntry
	{
	TFieldType iType;
	TInt iChoice;
	TBuf<20> iPrompt;
	};

enum TTSFormPanic
	{
	ETSFormPanicInvalidFieldType,
	ETSFormPanicInvalidRecord
	};

LOCAL_D void Panic(TTSFormPanic aPanic)
	{
	User::Panic(_L("TSFORM"),aPanic);
	}

class CTestScrollableForm : public CEikScrollableForm, public MCoeControlBrushContext
	{
public:
	CTestScrollableForm(const CFieldTypeArray* aFieldDefinitions);
	~CTestScrollableForm();
	void ConstructL(const CCoeControl& aParent);
	void SaveFormDataToDatabaseL(CBabyDatabase* aDatabase);
	void LoadFormDataFromDatabaseL(const CBabyDatabase* aDatabase,TInt aRecord);
	void ClearFormL();
private:
	void CreateControlsL();
private: // framework
	TKeyResponse OfferKeyEventL(const TKeyEvent& aKeyEvent,TEventCode aType);
private:
	CDesCArray* iArray;
	const CFieldTypeArray* iFieldDefinitions;
	};

class CContainer : public CEikScrollableFormContainer, public MCoeControlObserver
    {
public:
    void ConstructL(const TRect& aRect,const CFieldTypeArray* aFieldDefinitions);
	CTestScrollableForm* ScrollableForm();
protected:
	virtual void HandleControlEventL(CCoeControl* aControl,TCoeEvent aEventType);
    };

/////////////////////////////////////////////////////////////

CTestScrollableForm::CTestScrollableForm(const CFieldTypeArray* aFieldDefinitions)
	: iFieldDefinitions(aFieldDefinitions)
	{
	iCurrentLine=0;
	}

CTestScrollableForm::~CTestScrollableForm()
	{
	delete iArray;
	}

void CTestScrollableForm::ConstructL(const CCoeControl& aParent)
	{
	CEikScrollableForm::ConstructL(aParent);
	iBrushStyle=CGraphicsContext::ESolidBrush;
	iBrushColor=/*KRgbGray*/KRgb1in4DitheredGray;
    iContext=this;
    Window().SetShadowDisabled(ETrue);
	SetBlank();
	Window().SetBackgroundColor(/*KRgbGray*/KRgb1in4DitheredGray);

	iArray=new(ELeave) CDesCArrayFlat(5);
	iArray->AppendL(_L("One"));
	iArray->AppendL(_L("Two"));
	iArray->AppendL(_L("Three"));
	iArray->AppendL(_L("Four"));
	iArray->AppendL(_L("Five"));

	iLines=new(ELeave) CEikCapCArray(4);
	CreateControlsL();
	}

void CTestScrollableForm::CreateControlsL()
	{
	const TInt count=iFieldDefinitions->Count();
	TInt ii=0;
	while (ii<count)
		{
		const TFieldType* field=(&(*iFieldDefinitions)[ii++]);
		switch (*field)
			{
		case EFieldTypePlainText:
			{
			SPlainTextTypeEntry* entry=(SPlainTextTypeEntry*)field;
			TDes* plainText=(&entry->iPlainText);
			if (entry->iPrompt.Length()==0)
				entry->iPrompt=_L("Plain text");
			AddAutoTextEditorL(entry->iPrompt,ii,0,plainText->MaxLength(),1,plainText);
			break;
			}
		case EFieldTypeRichText:
			{
			SRichTextTypeEntry* entry=(SRichTextTypeEntry*)field;
			TDes* richText=(&entry->iRichText);
			if (entry->iPrompt.Length()==0)
				entry->iPrompt=_L("Rich text");
			AddAutoTextEditorL(entry->iPrompt,ii,0,richText->MaxLength(),3,richText);
			CEikEdwin* edwin=(CEikEdwin*)Control(ii);
			edwin->CreateScrollBarFrameL();
			edwin->ScrollBarFrame()->SetScrollBarVisibilityL(CEikScrollBarFrame::EOff,CEikScrollBarFrame::EAuto);
			break;
			}
		case EFieldTypeNumeric:
			{
			SNumericTypeEntry* entry=(SNumericTypeEntry*)field;
			if (entry->iPrompt.Length()==0)
				entry->iPrompt=_L("Number");
			AddAutoNumberEditorL(entry->iPrompt,ii,0,10000,&entry->iNumber);
			break;
			}
		case EFieldTypeFloatingPoint:
			{
			SFloatingPointTypeEntry* entry=(SFloatingPointTypeEntry*)field;
			if (entry->iPrompt.Length()==0)
				entry->iPrompt=_L("Floating point");
			AddAutoFloatEditorL(entry->iPrompt,ii,0,10000,&entry->iReal);
			break;
			}
		case EFieldTypeTime:
			{
			STimeTypeEntry* entry=(STimeTypeEntry*)field;
			TTime min=Time::MinTTime();
			TTime max=Time::MaxTTime();
			if (entry->iPrompt.Length()==0)
				entry->iPrompt=_L("Time");
			AddAutoTimeEditorL(entry->iPrompt,ii,min,max,EFalse,&entry->iTime);
			break;
			}
		case EFieldTypeDate:
			{
			SDateTypeEntry* entry=(SDateTypeEntry*)field;
			TTime min=TDateTime(0,EJanuary,0,0,0,0,0);
			TTime max=TDateTime(2200,EJanuary,0,0,0,0,0);
			if (entry->iPrompt.Length()==0)
				entry->iPrompt=_L("Date");
			AddAutoDateEditorL(entry->iPrompt,ii,min,max,EFalse,&entry->iDate);
			break;
			}
		case EFieldTypeCheck:
			{
			SCheckTypeEntry* entry=(SCheckTypeEntry*)field;
			if (entry->iPrompt.Length()==0)
				entry->iPrompt=_L("Check box");
			AddAutoCheckBoxL(entry->iPrompt,ii,(CEikButtonBase::TState*)&entry->iSet);
			break;
			}
		case EFieldTypeList:
			{
			SListTypeEntry* entry=(SListTypeEntry*)field;
			if (entry->iPrompt.Length()==0)
				entry->iPrompt=_L("List");
			AddAutoChoiceListL(entry->iPrompt,ii,EEikChlistArrayOwnedExternally,0,iArray,&entry->iChoice);
			break;
			}
		default:
			Panic(ETSFormPanicInvalidFieldType);
			}
		}
	}

void CTestScrollableForm::SaveFormDataToDatabaseL(CBabyDatabase* aDatabase)
	{
	GetAutoValuesFromPage(iLines);
	TInt count=iFieldDefinitions->Count();
	TInt ii=0;
	while (ii<count)
		{
		const TFieldType* field=(&(*iFieldDefinitions)[ii]);
		TInt length=0;
		switch (*field)
			{
		case EFieldTypePlainText:
			length=sizeof(TBuf<10>);
			break;
		case EFieldTypeRichText:
			length=sizeof(TBuf<25>);
			break;
		case EFieldTypeNumeric:
		case EFieldTypeList:
			length=sizeof(TInt);
			break;
		case EFieldTypeFloatingPoint:
			length=sizeof(TReal);
			break;
		case EFieldTypeTime:
		case EFieldTypeDate:
			length=sizeof(TTime);
			break;
		case EFieldTypeCheck:
			length=sizeof(TBool);
			break;
		default:
			Panic(ETSFormPanicInvalidFieldType);
			};
		aDatabase->AppendL((*iLines)[ii++]->iReturnValue,length);
		}
	TInt& recordCount=*((TInt*)aDatabase->At(0));
	recordCount++;	//Increment count
	*((TInt*)aDatabase->At(1))=recordCount-1;	//Set current to last
	}

void CTestScrollableForm::LoadFormDataFromDatabaseL(const CBabyDatabase* aDatabase,TInt aRecord)
	{
	__ASSERT_DEBUG(aRecord>=0,ETSFormPanicInvalidRecord);
	TInt count=iFieldDefinitions->Count();
	TInt recordOffset=aRecord*count+2;	// +2 'cos first items are rec count and rec no
	TInt ii=0;
	while (ii<count)
		{
		const TFieldType* field=(&(*iFieldDefinitions)[ii]);
		const TAny* data = aDatabase->At(recordOffset+ii++);
		switch (*field)
			{
		case EFieldTypePlainText:
		case EFieldTypeRichText:
			SetEdwinTextL(ii,(const TDesC*)data);
			break;
		case EFieldTypeNumeric:
			SetNumberEditorValue(ii,*(TInt*)data);
			break;
		case EFieldTypeList:
			SetChoiceListCurrentItem(ii,*(TInt*)data);
			break;
		case EFieldTypeFloatingPoint:
			SetFloatEditorValueL(ii,*(TReal*)data);
			break;
		case EFieldTypeTime:
		case EFieldTypeDate:
			SetTTimeEditorValue(ii,*(TTime*)data);			
			break;
		case EFieldTypeCheck:
			SetCheckBoxState(ii,*(CEikButtonBase::TState*)data);
			break;
		default:
			Panic(ETSFormPanicInvalidFieldType);
			}
		}
	*((TInt*)aDatabase->At(1))=aRecord;
	}

void CTestScrollableForm::ClearFormL()
	{
	TInt count=iFieldDefinitions->Count();
	TInt ii=0;
	while (ii<count)
		{
		const TFieldType* field=(&(*iFieldDefinitions)[ii++]);
		switch (*field)
			{
		case EFieldTypePlainText:
		case EFieldTypeRichText:
			{
			TPtrC ptr;
			SetEdwinTextL(ii,&ptr);
			break;
			}
		case EFieldTypeNumeric:
			SetNumberEditorValue(ii,0);
			break;
		case EFieldTypeList:
			SetChoiceListCurrentItem(ii,0);
			break;
		case EFieldTypeFloatingPoint:
			SetFloatEditorValueL(ii,TReal(0));
			break;
		case EFieldTypeTime:
		case EFieldTypeDate:
			SetTTimeEditorValue(ii,iCoeEnv->LastEvent().Time());			
			break;
		case EFieldTypeCheck:
			SetCheckBoxState(ii,CEikButtonBase::EClear);
			break;
		default:
			Panic(ETSFormPanicInvalidFieldType);
			}
		}
	}

TKeyResponse CTestScrollableForm::OfferKeyEventL(const TKeyEvent& aKeyEvent,TEventCode aType)
    {
    if (aType==EEventKey)
        {
        switch (aKeyEvent.iCode)
            {
        case EKeyUpArrow:
            RotateFocusByL(-1);
            return(EKeyWasConsumed);
        case EKeyDownArrow:
            RotateFocusByL(+1);
            return(EKeyWasConsumed);
            }
        }
	(*iLines)[iCurrentLine]->iControl->OfferKeyEventL(aKeyEvent,aType);
	ExposeLine(iCurrentLine);
	ReportEventL(MCoeControlObserver::EEventStateChanged);
//	UpdateScrollBarL();
    return(EKeyWasConsumed);
    }

///////////////////////////////////////////////////////////////

void CContainer::ConstructL(const TRect& aRect,const CFieldTypeArray* aFieldDefinitions)
    {
    CreateWindowL();
    Window().SetShadowDisabled(ETrue);
	iScrollableForm = new(ELeave) CTestScrollableForm(aFieldDefinitions);
	((CTestScrollableForm*)iScrollableForm)->ConstructL(*this);
	iScrollableForm->SetObserver(this);
	CreateScrollBarL();
    SetRectL(aRect);
    ActivateL();
	UpdateScrollBarL();
//    (*iLines)[0]->SetCurrent(ETrue);
	}

void CContainer::HandleControlEventL(CCoeControl* aControl,TCoeEvent aEventType)
	{
	if (aControl==iScrollableForm && aEventType==EEventStateChanged)
		UpdateScrollBarL();
	}

CTestScrollableForm* CContainer::ScrollableForm()
	{
	return (CTestScrollableForm*)iScrollableForm;
	}

//
// CSFormDatabaseTypeDlg
//

class CSFormDatabaseTypeDlg : public CEikDialog
	{
public:
	CSFormDatabaseTypeDlg(TDatabaseType& aDatabaseType,TInt& aNoOfFields);
private:
	virtual TBool OkToExitL(TInt aButtonId);
	virtual void HandleControlStateChangeL(TInt aControlId);
private:
	TDatabaseType& iDatabaseType;
	TInt& iNoOfFields;
	};

CSFormDatabaseTypeDlg::CSFormDatabaseTypeDlg(TDatabaseType& aDatabaseType,TInt& aNoOfFields)
	: iDatabaseType(aDatabaseType),
	iNoOfFields(aNoOfFields)
	{}

TBool CSFormDatabaseTypeDlg::OkToExitL(TInt /*aButtonId*/)
	{
	TInt item=ChoiceListCurrentItem(EDatabaseTypeChoiceId);
	iDatabaseType=*((TDatabaseType*)&item);	// Bit dodgy
	iNoOfFields=NumberEditorValue(EDatabaseTypeNumberOfFieldsId);
	return ETrue;
	}

void CSFormDatabaseTypeDlg::HandleControlStateChangeL(TInt aControlId)
	{
	if (aControlId==EDatabaseTypeChoiceId)
		{
		TBool isRandom=(ChoiceListCurrentItem(EDatabaseTypeChoiceId)==EDatabaseTypeRandom);
		MakeLineVisible(EDatabaseTypeNumberOfFieldsId,isRandom);
		}
	}

//
// CSFormAppUi
//

class CSFormAppUi : public CEikAppUi
    {
public:
    void ConstructL();
	~CSFormAppUi();
private: // framework
    void HandleCommandL(TInt aCommand);
private: // internal functions
	void GetDatabaseDefinitionL();
	void AppendTypeEntryL(TFieldType aFieldType,const TDesC& aPrompt);
	TInt RecordCount();
	TInt CurrentRecord();
	void HandleJumpToRecordL();
private:
	CFieldTypeArray* iFieldDefinitions;
	CBabyDatabase* iBabyDatabase;
    CContainer* iContainer;
    };

TInt CSFormAppUi::RecordCount()
	{
	return *((TInt*)iBabyDatabase->At(0));
	}

TInt CSFormAppUi::CurrentRecord()
	{
	return *((TInt*)iBabyDatabase->At(1));
	}

void CSFormAppUi::AppendTypeEntryL(TFieldType aFieldType,const TDesC& aPrompt)
	{
	switch (aFieldType)
		{
	case EFieldTypePlainText:
		{
		SPlainTextTypeEntry entry;
		entry.iType=EFieldTypePlainText;
		entry.iPrompt=aPrompt;
		iFieldDefinitions->AppendL(entry.iType,sizeof(entry));
		break;
		}
	case EFieldTypeRichText:
		{
		SRichTextTypeEntry entry;
		entry.iType=EFieldTypeRichText;
		entry.iPrompt=aPrompt;
		iFieldDefinitions->AppendL(entry.iType,sizeof(entry));
		break;
		}
	case EFieldTypeNumeric:
		{
		SNumericTypeEntry entry;
		entry.iType=EFieldTypeNumeric;
		entry.iNumber=0;
		entry.iPrompt=aPrompt;
		iFieldDefinitions->AppendL(entry.iType,sizeof(entry));
		break;
		}
	case EFieldTypeFloatingPoint:
		{
		SFloatingPointTypeEntry entry;
		entry.iType=EFieldTypeFloatingPoint;
		entry.iReal=0;
		entry.iPrompt=aPrompt;
		iFieldDefinitions->AppendL(entry.iType,sizeof(entry));
		break;
		}
	case EFieldTypeTime:
		{
		STimeTypeEntry entry;
		entry.iType=EFieldTypeTime;
		entry.iTime.HomeTime();
		entry.iPrompt=aPrompt;
		iFieldDefinitions->AppendL(entry.iType,sizeof(entry));
		break;
		}
	case EFieldTypeDate:
		{
		SDateTypeEntry entry;
		entry.iType=EFieldTypeDate;
		entry.iDate.HomeTime();
		entry.iPrompt=aPrompt;
		iFieldDefinitions->AppendL(entry.iType,sizeof(entry));
		break;
		}
	case EFieldTypeCheck:
		{
		SCheckTypeEntry entry;
		entry.iType=EFieldTypeCheck;
		entry.iSet=EFalse;
		entry.iPrompt=aPrompt;
		iFieldDefinitions->AppendL(entry.iType,sizeof(entry));
		break;
		}
	case EFieldTypeList:
		{
		SListTypeEntry entry;
		entry.iType=EFieldTypeList;
		entry.iChoice=0;
		entry.iPrompt=aPrompt;
		iFieldDefinitions->AppendL(entry.iType,sizeof(entry));
		break;
		}
	default:
		Panic(ETSFormPanicInvalidFieldType);
		}
	}


void CSFormAppUi::GetDatabaseDefinitionL()
	{
	iFieldDefinitions=new(ELeave) CFieldTypeArray(16);
	TDatabaseType databaseType=EDatabaseTypeRandom;
	TInt numFields=1;
	FOREVER
		{
		CEikDialog* databaseDlg=new(ELeave) CSFormDatabaseTypeDlg(databaseType,numFields);
		if (databaseDlg->ExecuteLD(R_TSFR_DLG_DATABASE_TYPE))
			{
			if (databaseType==EDatabaseTypeUserDefined)
				break;
			TInt64 seed=iCoeEnv->LastEvent().Time().Int64();
			for (TInt ii=0;ii<numFields;ii++)
				{
				TInt fieldNo=Math::Rand(seed)%KNumberOfFieldTypes;
				TFieldType fieldType=*((TFieldType*)&fieldNo);
				AppendTypeEntryL(fieldType,TPtrC());
				}
			return;
			}
		else
			{
			if (iEikonEnv->QueryWinL(R_TSFR_TBUF_NO_FIELDS_QUERY_EXIT))
				CBaActiveScheduler::Exit();
			continue;
			}
		}
	numFields=1;
	FOREVER
		{
		CEikDialog* typeDialog=new(ELeave) CEikDialog;
		typeDialog->PrepareLC(R_TSFR_DLG_FIELD_TYPE);
		TBuf<40> title;
		iCoeEnv->Format128(title,R_TSFR_TBUF_DEFINE_FIELD_N,numFields);
		typeDialog->SetTitleL(title);
		TFieldType fieldType=EFieldTypePlainText;
		typeDialog->DeclareAutoChoiceList(EFieldTypeChoiceId,(TInt*)(&fieldType));
		TBuf<20> prompt;
		typeDialog->DeclareAutoTextEditorL(EFieldTypePromptId,&prompt);
		if (typeDialog->RunLD())
			{
			AppendTypeEntryL(fieldType,prompt);
			numFields++;
			}
		else
			{ // user cancelled
			if (numFields==1)
				{
				if (iEikonEnv->QueryWinL(R_TSFR_TBUF_NO_FIELDS_QUERY_EXIT))
					CBaActiveScheduler::Exit();
				continue;
				}
			TBuf<60> line1;
			iCoeEnv->ReadResource(line1,R_TSFR_TBUF_END_DEFN_QUERY_1);
			TBuf<60> line2;
			iCoeEnv->Format128(line2,R_TSFR_TBUF_END_DEFN_QUERY_2,numFields-1);
			if (iEikonEnv->QueryWinL(line1,line2))
				return;
			}
		}
	}

void CSFormAppUi::ConstructL()
    {
    BaseConstructL();
	GetDatabaseDefinitionL();
    iContainer=new(ELeave) CContainer;
    iContainer->ConstructL(ClientRect(),iFieldDefinitions);
    AddToStackL(iContainer->ScrollableForm());
	iBabyDatabase=new(ELeave)CBabyDatabase(1);
	TInt count=0;
	iBabyDatabase->AppendL(&count,sizeof(TInt));	//Record count
	count=-1; // No current record to start with
	iBabyDatabase->AppendL(&count,sizeof(TInt));	//Current record
    }

CSFormAppUi::~CSFormAppUi()
	{
    delete iContainer;
	delete iFieldDefinitions;
	delete iBabyDatabase;
	}

void CSFormAppUi::HandleCommandL(TInt aCommand)
    {
	switch (aCommand)
		{
	case EEikCmdExit:
        CBaActiveScheduler::Exit();
		break;
	case ETSFormCmdSave:
		iContainer->ScrollableForm()->SaveFormDataToDatabaseL(iBabyDatabase);
		iEikonEnv->InfoMsg(R_TSFR_TBUF_RECORD_N_SAVED,RecordCount());
		break;
	case ETSFormCmdJump:
		HandleJumpToRecordL();
		break;
	case ETSFormCmdClearForm:
		iContainer->ScrollableForm()->ClearFormL();
		break;
	case ETSFormCmdInfo:
		{
		TInt recordCount=RecordCount();
		if (recordCount>0)
			iEikonEnv->InfoMsg(R_TSFR_TBUF_CURRENT_IS_X_OF_N,CurrentRecord()+1,recordCount);
		else
			iEikonEnv->InfoMsg(R_TSFR_TBUF_NO_RECORDS_SAVED);
		}
		break;
		}
    }

void CSFormAppUi::HandleJumpToRecordL()
	{
	TInt recordCount=RecordCount();
	if (recordCount<=0)
		{
		iEikonEnv->InfoMsg(R_TSFR_TBUF_NO_RECORDS_SAVED);
		return;
		}
	CEikDialog* dialog = new(ELeave) CEikDialog;
	dialog->ConstructAutoDialogLC(EEikDialogFlagWait,R_EIK_BUTTONS_CANCEL_OK);
	dialog->SetTitleL(_L("Jump to"));
	TInt currentRecord=CurrentRecord()+1;	//User perceives 1 as first
	dialog->AddAutoNumberEditorL(_L("Record number"),1,1,recordCount,&currentRecord);
	if (dialog->RunLD())
		iContainer->ScrollableForm()->LoadFormDataFromDatabaseL(iBabyDatabase,currentRecord-1);
	}

//
// CSFormDocument
//

class CSFormDocument : public CEikDocument
	{
public:
	CSFormDocument(CEikApplication& aApp): CEikDocument(aApp) { }
private: // from CApaDocument
	CEikAppUi* CreateAppUiL();
	};

CEikAppUi* CSFormDocument::CreateAppUiL()
	{
    return(new(ELeave) CSFormAppUi);
	}

//
// CSFormApplication
//

class CSFormApplication : public CEikApplication
	{
private: // from CApaApplication
	CApaDocument* CreateDocumentL();
	TUid AppDllUid() const;
	};

const TUid KUidSFormApp={236};

TUid CSFormApplication::AppDllUid() const
	{
	return(KUidSFormApp);
	}

CApaDocument* CSFormApplication::CreateDocumentL()
	{
	return(new(ELeave) CSFormDocument(*this));
	}

//
// EXPORTed functions
//

EXPORT_C CApaApplication* NewApplication()
	{
	return(new CSFormApplication);
	}

GLDEF_C TInt E32Dll(TDllReason)
	{
	return(KErrNone);
	}
