// TSTAPP.CPP
//
// Copyright (c) 1997-1999 Symbian Ltd.  All rights reserved.
//

#include <e32uid.h>
//#include <apadll.h>
#include <s32std.h>
#include <s32stor.h>
#include <s32file.h>
#include <apgicnfl.h>
#include <apfdef.h>
#include "tstapp.h"
#include <apfdef.h>

#define KTestAppCaption _L("Test App")
#define KTempFilePath _L("c:\\system\\temp\\")

GLDEF_C TInt E32Dll(TDllReason /*aReason*/)
// DLL entry point
//
	{
	return KErrNone;
	}


EXPORT_C CApaApplication* NewApplication()
// The ordinal 1 export
//
	{
	CApaApplication* thing=NULL;
	TRAPD(ret,thing=CTestApp::NewL());
	if (ret!=KErrNone)
		thing=NULL;
	return thing;
	}

/////////////////////////////////////////
// CTestApp
/////////////////////////////////////////

CTestApp* CTestApp::NewL()
// The gated function
//
	{
	return new(ELeave) CTestApp();
	}


CTestApp::CTestApp()
	:iCaption(KTestAppCaption)
	{}


CTestApp::~CTestApp()
	{
	delete iCapabilityBuf;
	}


void CTestApp::PreDocConstructL()
	{
	delete iCapabilityBuf;
	iCapabilityBuf = NULL;
	iCapabilityBuf = new(ELeave) TApaAppCapabilityBuf();
	CApaAppInfoFileReader* reader;
	TRAPD(ret,reader=OpenAppInfoFileL());
	if (ret==KErrNone)
		{
		reader->Capability(*iCapabilityBuf);
		delete reader;
		}
	}


CApaDocument* CTestApp::CreateDocumentL(CApaProcess* aProcess)
	{
	__ASSERT_ALWAYS(aProcess,User::Panic(_L("TSTAPP"),KErrGeneral));

	return CTestAppDoc::NewL(*this,*aProcess);
	}


TDesC& CTestApp::Caption()
// return the app title in current system language
//
	{
	return iCaption;
	}


TUid CTestApp::AppDllUid()const
	{
	return KUidTestApp;
	}


CApaAppInfoFileReader* CTestApp::OpenAppInfoFileLC() const
	{
	TParse parser;
	parser.SetNoWild(KAppInfoFileExtension,&AppFullName(),NULL); // *.aif
	CApaAppInfoFileReader* reader=CApaAppInfoFileReader::NewLC(parser.FullName(),AppDllUid());
	return reader;
	}


CDictionaryStore* CTestApp::OpenIniFileLC(RFs& aFs) const
// Opens the applications ini file if it exists, otherwise creates a new one
// The ini file is located on KIniFileDrive (c:), in the same directory as the app dll
	{
	// get the path of the ini file
	TParse parser;
	User::LeaveIfError( parser.SetNoWild(KIniFileExtension,&_L("c:"),&AppFullName()) );
	// ensure that all directories in the path exist
	aFs.MkDirAll(parser.FullName()); // ignore the error
	//
	// open the ini file if it exists, otherwise create a new one
	return CDictionaryFileStore::OpenLC(aFs,parser.FullName(),AppDllUid());
	}


void CTestApp::Capability(TDes8& aInfo)const
	{
	TApaAppCapability::CopyCapability(aInfo,*iCapabilityBuf);
	}


/////////////////////////////////////////
// CTestAppDoc
/////////////////////////////////////////

CTestAppDoc* CTestAppDoc::NewL(CApaApplication& aApp,CApaProcess& aProcess)
	{
	CTestAppDoc* self=new(ELeave) CTestAppDoc(aApp,aProcess);
	CleanupStack::PushL(self);
	self->ConstructL();
	CleanupStack::Pop();
	return self;
	}


CTestAppDoc::CTestAppDoc(CApaApplication& aApp,CApaProcess& aProcess)
	: CApaDocument(aApp,aProcess),
	iValue(0)
	{}

	
void CTestAppDoc::ConstructL()
	{
	iEmbedList = new(ELeave) CArrayFixFlat<TPictureHeader>(1);
	}


CTestAppDoc::~CTestAppDoc()
	{
	// delete all the embedded doc's first...
	if (iEmbedList)
		{
		for (TInt i=iEmbedList->Count()-1 ; i>=0 ; i--)
			delete EmbeddedDoor(i); // destroys the door (which in turn removes doc from the process and deletes it)
		}
	// then...
	delete iEmbedList;
	delete iStore;
	}

/*
CApaDoor* CTestAppDoc::EmbeddedDoor(TInt aDocNum)const
	{
	__ASSERT_ALWAYS(aDocNum<iEmbedList->Count(),User::Panic(_L("TSTAPP"),1));
	//
	return (CApaDoor*)((*iEmbedList)[aDocNum].iPicture.AsPtr());
	}
*/

TBool CTestAppDoc::IsEmpty()const
// return ETrue if the document is empty
//
	{
	if (iValue==0)
		return ETrue;
	else 
		return EFalse;
	}


TBool CTestAppDoc::HasChanged()const
	{
	return iHasChanged;
	}


void CTestAppDoc::NewDocumentL()
// builds a new embedded or main document without loading from a store (may create the content from 
//	eg code or a template file).
//
	{}


CFileStore* CTestAppDoc::CreateFileStoreLC(RFs& aFs,const TDesC& aFileName)
// Create a document file & store of the specified name, storing into the store.
// On system startup the name might not have a filename component in which case this function should parse the 
//  passed name with a related default file name appropriate to this application and in an appropriate language
//
	{
	// set the default name just in case...
	TFileName defaultName(_L("temp"));
	TParse parser;
	User::LeaveIfError(parser.Set(aFileName,&defaultName,NULL));
	// create the file store
	CFileStore* store;
	store = CDirectFileStore::CreateLC(aFs,parser.FullName(),EFileWrite);
	store->SetTypeL(TUidType(KDirectFileStoreLayoutUid,KUidAppDllDoc,KUidTestApp));
	CStreamDictionary* streamDic=CStreamDictionary::NewL();
	CleanupStack::PushL(streamDic);
	StoreL(*store,*streamDic); 
	Process()->WriteRootStreamL(*store,*streamDic,*Application());
	store->CommitL(); // now the store will be fully initialised
	CleanupStack::PopAndDestroy(); // streamDic
	return store;
	}


void CTestAppDoc::EditL(MApaEmbeddedDocObserver* aContainer,TBool aReadOnly)
	{
	aContainer = iContainer;
	if (aReadOnly)
		{
		if (iContainer)
			{
			iContainer->NotifyExit(MApaEmbeddedDocObserver::ENoChanges);
			iContainer = NULL; // iContainer must be nulled when editing is finished
			}
		}
	else
		{
		iValue++;
		iHasChanged = ETrue;
		if (iContainer)
			{
			iContainer->NotifyExit(MApaEmbeddedDocObserver::EKeepChanges);
			iContainer = NULL; // iContainer must be nulled when editing is finished
			}
		}
	}

/*
inline void CTestAppDoc::EditEmbeddedDocL(TInt aDocNum)
	{
	__ASSERT_ALWAYS(aDocNum<iEmbedList->Count(),User::Panic(_L("TSTAPP"),1));
	//
	// if I'm the main doc call Save() to put me in a safe state
	// call edit on the embedded doc
	EmbeddedDoor(aDocNum)->DocumentL(*iStore)->EditL(*iStore,this);
	}
*/

void CTestAppDoc::NotifyExit(MApaEmbeddedDocObserver::TExitMode aMode)
	{
	switch (aMode)
		{
		case EKeepChanges:
			iHasChanged = ETrue; // note that my contents have changed
			break;
		case ERevertToSaved:
			// reload whole document (panic if I'm not the main doc)
			break;
		case ENoChanges:
			// no changes
			break;
		case EEmpty:
			// the embedded doc is empty
			break;
		}
	}


void CTestAppDoc::PrintL(const CStreamStore& /*aSourceStore*/)
	{}


void CTestAppDoc::SaveL()
	{
	iStore->CommitL();

	CDirectFileStore* store;
	TParse newFilePath;
	// create temp file
	User::LeaveIfError( newFilePath.Set(Process()->MainDocFileName(),NULL,NULL) ); // abuse new file path
	TBuf<2> drive=newFilePath.Drive();
	User::LeaveIfError( newFilePath.Set(drive,&KTempFilePath,NULL) );
	Process()->FsSession().MkDirAll(newFilePath.DriveAndPath());
	TFileName tempName;
	store = CDirectFileStore::TempLC(Process()->FsSession(),newFilePath.DriveAndPath(),tempName,EFileWrite);
	store->SetTypeL(((CFileStore*)iStore)->Type());

	// store main in temp
	CStreamDictionary* streamDic=CStreamDictionary::NewL();
	CleanupStack::PushL(streamDic);
	StoreL(*store,*streamDic);
	// write root stream
	Process()->WriteRootStreamL(*store,*streamDic,*Application());
	CleanupStack::PopAndDestroy(); // streamDic
	// close the new store
	store->CommitL();
	CleanupStack::PopAndDestroy(); // store
	// close the old store
	delete iStore;
	iStore = NULL;
	// replace the old file
	User::LeaveIfError( Process()->FsSession().Replace(tempName,Process()->MainDocFileName()) );
	// open new file
	iStore = CDirectFileStore::OpenL(Process()->FsSession(),Process()->MainDocFileName(),EFileShareExclusive);
	}


void CTestAppDoc::StoreL(CStreamStore& aStore,CStreamDictionary& aStreamDic)const
	{
	//
	// create map and store embedded doc's
	CStoreMap* map=CStoreMap::NewLC(aStore);
	StoreComponentsL(aStore,*map);
	// store the headstream
	RStoreWriteStream stream(*map);
	TStreamId id=stream.CreateLC(aStore); 
	ExternalizeL(stream);
	stream.CommitL();
	// assign the headstream in the dictionary
	aStreamDic.AssignL(KUidTestAppHeadStream,id);
	// tidy up
	map->Reset();
	CleanupStack::PopAndDestroy(2); // map,stream
	}


void CTestAppDoc::RestoreL(const CStreamStore& aStore,const CStreamDictionary& aStreamDic)
	{
	//
	TStreamId headStreamId=aStreamDic.At(KUidTestAppHeadStream);
	RStoreReadStream stream;
	stream.OpenLC(aStore,headStreamId); 
	InternalizeL(stream);
	CleanupStack::PopAndDestroy(); // stream
	// restore the embedded bits
	RestoreComponentsL(aStore);
	}


void CTestAppDoc::DetachFromStoreL(CPicture::TDetach aDegree)
	{
	for (TInt i=0 ; i<iEmbedList->Count() ; i++)
		EmbeddedDoor(i)->DetachFromStoreL(aDegree);
	}


void CTestAppDoc::StoreComponentsL(CStreamStore& aStore,CStoreMap& aMap)const
	{
	for (TInt i=0 ; i<iEmbedList->Count() ; i++)
		{
		TStreamId id=EmbeddedDoor(i)->StoreL(aStore);
		aMap.BindL((*iEmbedList)[i].iPicture,id);
		}
	}


void CTestAppDoc::RestoreComponentsL(const CStreamStore& aStore)
	{
	TApaPictureFactory factory(Process());
	for (TInt i=0 ; i<iEmbedList->Count() ; i++)
		{
		TRAPD(ret, factory.NewPictureL((*iEmbedList)[i],aStore) );
		if ((ret!=KErrNone)&&(ret!=KErrNotFound))
			User::Leave(ret);
		}
	}


void CTestAppDoc::ExternalizeL(RWriteStream& aStream)const
	{
	aStream.WriteInt32L(iValue);
	// externalize num of embedded objects
	TInt numObjects=iEmbedList->Count();
	aStream.WriteInt32L(numObjects);
	// externalize the doors
	for (TInt i=0 ; i<numObjects ; i++)
		aStream<< (*iEmbedList)[i].iPicture;
	}


void CTestAppDoc::InternalizeL(RReadStream& aStream)
	{
	// reset();
	iValue = aStream.ReadInt32L();
	TInt numObjects=aStream.ReadInt32L();
	for (TInt i=0 ; i<numObjects ; i++)
		{
		TPictureHeader header;
		header.iPictureType = KUidPictureTypeDoor;
		aStream>> header.iPicture;
		iEmbedList->AppendL(header);
		}
	}

/*
inline void CTestAppDoc::EmbedNewDocL(const TDesC& aDllFileName)
	{
	TPictureHeader header;
	header.iPictureType = KUidPictureTypeDoor;
	header.iPicture = CApaDoor::NewLC(*Process()->AddNewDocumentL(aDllFileName),TSize(16,16));
	iEmbedList->AppendL(header);
	CleanupStack::Pop(); // iPicture
	}
*/
/*
void CTestAppDoc::EmbedExistingDocL(const TFullName& aFileName)
	{
	// create a header to store the new door in
	TPictureHeader header;
	header.iPicture = CApaDoor::NewLC();
	// read the dll header of the document to be embedded
	CFileStore* store;
	TDllHeader dllHeader=iAppProcess->ReadRootStreamL(store,aFileName);
	CleanupStack::PushL(store);
	// create and embed an empty document of the correct type
	((CApaDoor*)header.iPicture.AsPtr())->iAppDoc = iAppProcess->AddNewDocumentL(dllHeader.iDllName,dllHeader.iDllUid);
	// import the existing file into the new doc
	((CApaDoor*)header.iPicture.AsPtr())->iAppDoc->ImportL(*(CStreamStore*)store,dllHeader.iHeadStreamId);
	// add the new entry and tidy up
	iEmbedList->AppendL(header);
	CleanupStack::PopAndDestroy(); // store
	CleanupStack::Pop(); // iPicture
	}
*/
/*
inline void CTestAppDoc::RemoveDocL(TInt index)
	{
	delete EmbeddedDoor(index); // destroy the door, which in turn removes and deletes the document
	iEmbedList->Delete(index); // remove the entry from the array
	}
*/