// EIKCONCL.CPP
//
// Copyright (c) 1997-1999 Symbian Ltd.  All rights reserved.
//

#include <eikconcl.h>
#include <eikconcl.pan>
#include <eikconso.h>
#include <eikenv.h>
#include <eikappui.h>  
#include <basched.h>
#include <apgwgnam.h>

GLDEF_C void Panic(TEikConClPanic aPanic)
	{
	User::Panic(_L("EIKON-CONCL"),aPanic);
	}

enum
	{
	EExit,
	ERead,
	EReadCancel,
	EWrite,
	ESetCursorPosAbs,
	ESetCursorPosRel,
	ESetCursorHeight,
	ESetTitle,
	EClearScreen,
	EClearToEndOfLine
	};

//
// class CEikConsMessager
//

class CEikConsMessager : public CActive
	{
public:
	CEikConsMessager(CEikConsoleScreen* aScreen,RThread aParentThread);
	~CEikConsMessager();
	void ConstructL(CEikConsoleClient* aClient);
	void HandleKeyEvent(const TKeyEvent& aKeyEvent);
private: // overridden
	void RunL();
	void DoCancel();
private: // internal
	void CompleteReadRequest();
private:
	CEikConsoleScreen* iScreen;
	RThread iParentThread;
	TRequestStatus* iReadStatus;
	TKeyEvent* iKeyEvent;
	TInt iMessage;
	const TAny* iParam;
	TRequestStatus* iReplyStatus;
	CCirBuf<TKeyEvent>* iKeyQ;
	};

CEikConsMessager::CEikConsMessager(CEikConsoleScreen* aScreen,RThread aParentThread)
	: CActive(EActivePriorityIpcEventsHigh),
	iParentThread(aParentThread)
	{
	iScreen=aScreen;
	}

CEikConsMessager::~CEikConsMessager()
	{
	iParentThread.Close();
	delete iKeyQ;
	}

void CEikConsMessager::ConstructL(CEikConsoleClient* aClient)
	{
	iKeyQ=new(ELeave) CCirBuf<TKeyEvent>;
	iKeyQ->SetLengthL(40);	// buffer length, too high? too low?
	iKeyEvent=(&aClient->iKeyEvent);
	aClient->iThreadStatus=(&iStatus);
	aClient->iMessage=(&iMessage);
	aClient->iParam=(&iParam);
	aClient->iReplyStatus=(&iReplyStatus);
	aClient->iScreen=iScreen;
	CActiveScheduler::Add(this);
	iStatus=KRequestPending;
	SetActive();
	}

void CEikConsMessager::DoCancel()
	{
	}

void CEikConsMessager::RunL()
	{
	switch (iMessage)
		{
	case EExit:
		CBaActiveScheduler::Exit();
		break;
	case ERead:
		if (iReadStatus)
			Panic(EEikConClPanicReadAlreadyOutstanding);
		iReadStatus=(TRequestStatus*)iParam;
		if (iKeyQ->Count()>0) // already a buffered event
			CompleteReadRequest();
		break;
	case EReadCancel:
		if (iReadStatus)
			iParentThread.RequestComplete(iReadStatus,KErrCancel);
		break;
	case EWrite:
		iScreen->Write(*(const TDesC*)iParam);
		break;
	case ESetCursorPosAbs:
		iScreen->SetCursorPosAbs(*(const TPoint*)iParam);
		break;
	case ESetCursorPosRel:
		iScreen->SetCursorPosRel(*(const TPoint*)iParam);
		break;
	case ESetCursorHeight:
		iScreen->SetCursorHeight((TInt)iParam);
		break;
	case EClearScreen:
		iScreen->ClearScreen();
		break;
	case EClearToEndOfLine:
		iScreen->ClearToEndOfLine();
		break;
		}
	iStatus=KRequestPending;
	SetActive();
	iParentThread.RequestComplete(iReplyStatus,0);
	}

void CEikConsMessager::CompleteReadRequest()
	{
	if (iReadStatus)
		{ 
		iKeyQ->Remove(iKeyEvent);;
		iParentThread.RequestComplete(iReadStatus,0);
		}
	}

void CEikConsMessager::HandleKeyEvent(const TKeyEvent& aKeyEvent)
	{
	TInt ret=iKeyQ->Add(&aKeyEvent);
	if (ret==0)
		CEikonEnv::Beep();
	if (iKeyQ->Count()==1) // client may be waiting on this key event
		CompleteReadRequest();
	}

//
// class CEikConsAppUi
//

struct SCommandLine
	{
	RThread iParentThread;
	TRequestStatus* iStatus;
	CEikConsoleClient* iClient;
	TSize iSize;
	const TDesC* iTitle;
	};

class CEikConsAppUi : public CEikAppUi
	{
public:
	void ConstructL(const SCommandLine* aComLine);
	~CEikConsAppUi();
private: // overridden
	void HandleKeyEventL(const TKeyEvent& aKeyEvent,TEventCode aType);
	void HandleForegroundEventL(TBool aForeground);
	void SetAndDrawFocus(TBool aFocus);
private:
	CEikConsoleScreen* iScreen;
	CEikConsoleControl* iControl;
	CEikConsMessager* iMessager;
	};

CEikConsAppUi::~CEikConsAppUi()
	{
	delete(iScreen);
	delete(iMessager);
	}
	
void CEikConsAppUi::ConstructL(const SCommandLine* aComLine)
	{
    CEikAppUi::BaseConstructL(ENoAppResourceFile);
    iScreen=new(ELeave) CEikConsoleScreen;
	iScreen->ConstructL(*(aComLine->iTitle),0);
	iControl=iScreen->ConsoleControl();
	iControl->SetFocus(ETrue,EDrawNow);
	iMessager=new(ELeave) CEikConsMessager(iScreen,aComLine->iParentThread);
	iMessager->ConstructL(aComLine->iClient);
	RThread().SetPriority(EPriorityMore);
	}

void CEikConsAppUi::HandleForegroundEventL(TBool aForeground)
	{
	if (aForeground)
		RThread().SetPriority(EPriorityMore);
	}

void CEikConsAppUi::SetAndDrawFocus(TBool aFocus)
	{
	if (iControl)
		iControl->SetFocus(aFocus,EDrawNow);
	}

void CEikConsAppUi::HandleKeyEventL(const TKeyEvent& aKeyEvent,TEventCode aType)
	{
	if (aType==EEventKey)
		iMessager->HandleKeyEvent(aKeyEvent);
	}

//
// class CConsEikonEnv
//

class CConsEikonEnv : public CEikonEnv
	{
public:
	void ConstructConsoleEnvironmentL(const SCommandLine* aComLine);
	};

void CConsEikonEnv::ConstructConsoleEnvironmentL(const SCommandLine* aComLine)
	{
	ConstructL();
	CEikConsAppUi* appUi=new(ELeave) CEikConsAppUi;
	appUi->ConstructL(aComLine);
	CApaWindowGroupName* wgName=CApaWindowGroupName::NewLC(iWsSession);
	TPtrC caption=*(aComLine->iTitle);
	wgName->SetCaptionL(caption);
	wgName->SetRespondsToShutdownEvent(EFalse);
	wgName->SetRespondsToSwitchFilesEvent(EFalse);
	wgName->SetWindowGroupName(iRootWin);
	CleanupStack::PopAndDestroy(); // wgName
#if defined(__EPOC32__)
    RProcess().Rename(caption);
#endif
	RThread().Rename(caption);
	}
	
TInt ConsoleClientStartFunction(TAny* aParam)
	{
	const SCommandLine* comLine=(const SCommandLine*)aParam;
	TInt err=KErrNoMemory;
    CConsEikonEnv* coe=new CConsEikonEnv;
	if (coe)
		TRAP(err,coe->ConstructConsoleEnvironmentL(comLine));
	TRequestStatus* pS=(comLine->iStatus);
	comLine->iParentThread.RequestComplete(pS,err);
    if (!err)
        coe->ExecuteD();
	return(0);
	}

//
// class CEikConsoleClient
//

CEikConsoleClient::~CEikConsoleClient()
	{
	if (iLogonStatus.Int()==KRequestPending && iReplyStatus)
		SendReceive(EExit,NULL);
	iThread.Close();
	}

CEikConsoleClient::CEikConsoleClient()
	{
	}
	
const TInt KMaxHeapSize=0x1000*254; // chunks are a megabyte anyway

TInt CEikConsoleClient::Create(const TDesC& aTitle,TSize aSize)
	{ 
	TInt err;
	TRequestStatus status=KRequestPending;
	SCommandLine comLine;
	comLine.iStatus=(&status);
	comLine.iClient=this;
	comLine.iSize=aSize;
	comLine.iTitle=&aTitle;
	TBuf<20> threadName;
	TInt num=0;
	do
		{
		threadName.Format(_L("UI%02d"),num++); // !! review the title
		err=iThread.Create(threadName,ConsoleClientStartFunction,KDefaultStackSize,KMinHeapSize,KMaxHeapSize,&comLine,EOwnerThread);
		} while(err==KErrAlreadyExists);
	if (!err)
		{
		iThread.Logon(iLogonStatus);
		comLine.iParentThread.Duplicate(iThread);
		iThread.Resume();
		User::WaitForRequest(status,iLogonStatus);
		err=status.Int();
		}
	return(err);
	}

void CEikConsoleClient::SendReceive(TInt aMessage,const TAny* aParam)
	{
	if (iLogonStatus.Int()!=KRequestPending)
		User::Exit(KErrCancel);
	*iMessage=aMessage;
	*iParam=aParam;
	TRequestStatus replyStatus=KRequestPending;
	*iReplyStatus=(&replyStatus);
	TRequestStatus* pS=iThreadStatus;
	iThread.RequestComplete(pS,0);
	User::WaitForRequest(replyStatus,iLogonStatus);
	}

void CEikConsoleClient::Read(TRequestStatus& aStatus)
	{
	aStatus=KRequestPending;
	SendReceive(ERead,&aStatus);
	}

void CEikConsoleClient::ReadCancel()
	{
	SendReceive(EReadCancel,NULL);
	}

void CEikConsoleClient::Write(const TDesC& aDes)
	{
	SendReceive(EWrite,&aDes);
	}

TPoint CEikConsoleClient::CursorPos() const
	{
	return(iScreen->CursorPos());
	}

void CEikConsoleClient::SetCursorPosAbs(const TPoint& aPosition)
	{
	SendReceive(ESetCursorPosAbs,&aPosition);
	}

void CEikConsoleClient::SetCursorPosRel(const TPoint &aVector)
	{
	SendReceive(ESetCursorPosRel,&aVector);
	}

void CEikConsoleClient::SetCursorHeight(TInt aPercentage)
	{
	SendReceive(ESetCursorHeight,aPercentage);
	}
		
void CEikConsoleClient::SetTitle(const TDesC& aTitle)
	{
	SendReceive(ESetTitle,&aTitle);
	}

void CEikConsoleClient::ClearScreen()
	{
	SendReceive(EClearScreen,NULL);
	}

void CEikConsoleClient::ClearToEndOfLine()
	{
	SendReceive(EClearToEndOfLine,NULL);
	}

TSize CEikConsoleClient::ScreenSize() const
	{
	return(iScreen->ScreenSize());
	}

TKeyCode CEikConsoleClient::KeyCode() const
	{
	return((TKeyCode)iKeyEvent.iCode);
	}

TUint CEikConsoleClient::KeyModifiers() const
	{
	return(iKeyEvent.iModifiers);
	}

extern "C" {
EXPORT_C TAny* NewConsole()
	{
#if defined(__WINS__)
	// return null if the graphical window server thread is not running
	TFindThread findT(_L("Wserv"));
	TFullName name;
	if (findT.Next(name)!=KErrNone)
		return(NULL);
#endif
	return(new CEikConsoleClient);
	}
}

GLDEF_C TInt E32Dll(TDllReason)
	{
	return(KErrNone);
	}
