// server.cpp
// 
// Copyright (c) 2009 - 2010 Accenture. All rights reserved.
// This component and the accompanying materials are made available
// under the terms of the "Eclipse Public License v1.0"
// which accompanies this distribution, and is available
// at the URL "http://www.eclipse.org/legal/epl-v10.html".
// 
// Initial Contributors:
// Accenture - Initial contribution
//

#include <consoleproxy.h>
#include "server.h"

_LIT(KPronxyConsolePanic, "proxyconsole");
	
void Panic(TConsoleProxyPanic aReason)
	{
	User::Panic(KPronxyConsolePanic, aReason);
	}


void ServerThreadL(TServerParams* aParams)
	{
	CActiveScheduler* as = new(ELeave)CActiveScheduler;
	CleanupStack::PushL(as);
	CActiveScheduler::Install(as);
	
	CConsoleProxyServer* server = (aParams->iServerNewL)(aParams->iServerParams);
	CleanupStack::PushL(server);
	
	aParams->iServer = server->Server();
	RThread::Rendezvous(KErrNone);
	
	CActiveScheduler::Start();
	
	CleanupStack::PopAndDestroy(2, as);
	}
		

TInt ServerThreadFunction(TAny* aArgs)
	{
	__UHEAP_MARK;
	User::SetCritical(User::ENotCritical);
	
	TServerParams* params = (TServerParams*)aArgs;
	
	CTrapCleanup* cleanup = CTrapCleanup::New();
	if (!cleanup)
		{
		return KErrNoMemory;
		}
		
	TRAPD(err, ServerThreadL(params));
		
	delete cleanup;
	__UHEAP_MARKEND;
	return err;
	}
		
//______________________________________________________________________________
//						CConsoleProxyServer
CConsoleProxyServer* CConsoleProxyServer::NewL(TAny* aParams)
	{
	TConsoleProxyServerNewLParams* params = (TConsoleProxyServerNewLParams*)aParams;
	return NewL(params->iName, params->iAoPriority, params->iConsoleCreate);
	}
	
EXPORT_C CConsoleProxyServer* CConsoleProxyServer::NewL(const TDesC& aName, TInt aAoPriority, TConsoleCreateFunction aConsoleCreate)
	{
	CConsoleProxyServer* self = new(ELeave)CConsoleProxyServer(aConsoleCreate, aAoPriority);
	CleanupStack::PushL(self);
	self->ConstructL(aName);
	CleanupStack::Pop(self);
	return self;
	}
	
EXPORT_C void CConsoleProxyServer::ConstructL(const TDesC& aName)
	{
	StartL(aName);
	iShutdownTimer = CShutdownTimer::NewL(*this);
	iShutdownTimer->Start();
	}

EXPORT_C CConsoleProxyServer::CConsoleProxyServer(TConsoleCreateFunction aConsoleCreate, TInt aAoPriority)
	: CServer2(aAoPriority, CServer2::ESharableSessions)
	, iConsoleCreate(aConsoleCreate)
	{
	}

EXPORT_C CConsoleProxyServer::~CConsoleProxyServer()
	{
	delete iShutdownTimer;
	iShutdownTimer = NULL; // Can be referenced by session destructors which run after this, as part of ~CServer2, which will call ~CConsoleProxySession which can call DropSession...
	}
	
EXPORT_C void CConsoleProxyServer::ShutdownTimerExpired()
	{
	CActiveScheduler::Stop();
	}

EXPORT_C CSession2* CConsoleProxyServer::NewSessionL(const TVersion&,const RMessage2&) const
	{
	return new(ELeave)CConsoleProxySession(iConsoleCreate);
	}

void CConsoleProxyServer::AddSession()
	{
	iShutdownTimer->Cancel();
	++iSessionCount;
	}

void CConsoleProxyServer::DropSession()
	{
	--iSessionCount;
	if ((iSessionCount==0) && iShutdownTimer && (!iShutdownTimer->IsActive()))
		{
		iShutdownTimer->Start();
		}
	}

//______________________________________________________________________________
//						CConsoleProxySession
EXPORT_C CConsoleProxySession::CConsoleProxySession(TConsoleCreateFunction aConsoleCreate)
	: iConsoleCreate(aConsoleCreate)
	{
	}
	
EXPORT_C CConsoleProxySession::CConsoleProxySession(MProxiedConsole* aConsole)
	: iConsole(aConsole)
	{
	iConsole->Open();
	}

EXPORT_C CConsoleProxySession::~CConsoleProxySession()
	{
	Server()->DropSession();
	if (!iReadMessage.IsNull())
		{
		iReadMessage.Complete(KErrDisconnected);
		if (iConsole) iConsole->ReadCancel();
		}
	if (iConsole) iConsole->Close();
	}

EXPORT_C void CConsoleProxySession::CreateL()
	{
	Server()->AddSession();
	}

EXPORT_C void CConsoleProxySession::ServiceL(const RMessage2& aMessage)
	{
	if (!iConsole && (aMessage.Function() != RConsoleProxy::ECreate))
		{
		User::Leave(KErrNotReady);
		}
	RBuf buf;
	CleanupClosePushL(buf);
	TBool complete = ETrue;
	switch (aMessage.Function())
		{
	case RConsoleProxy::ECreate:
		CreateL(aMessage);
		break;
	case RConsoleProxy::ERead:
		if (!iReadMessage.IsNull()) User::Leave(KErrAlreadyExists);
		iReadType = EReadBasic;
		iReadMessage = aMessage;
		iConsole->Read(*this);
		complete = EFalse;
		break;
	case RConsoleProxy::EReadKey:
		if (!iReadMessage.IsNull()) User::Leave(KErrAlreadyExists);
		iReadType = EReadKeys;
		iReadMessage = aMessage;
		iConsole->Read(*this);
		complete = EFalse;
		break;
	case RConsoleProxy::EReadCancel:
		if (!iReadMessage.IsNull())
			{
			iReadMessage.Complete(KErrCancel);
			iConsole->ReadCancel();
			}
		break;
	case RConsoleProxy::EWrite:
		buf.CreateL(aMessage.GetDesLengthL(0));
		aMessage.ReadL(0, buf);
		iConsole->Console()->Write(buf);
		break;
	case RConsoleProxy::EGetCursorPos:
		{
		TPckgBuf<TPoint> pos;
		pos() = iConsole->Console()->CursorPos();
		aMessage.WriteL(0, pos);
		break;
		}
	case RConsoleProxy::ESetCursorPosAbs:
		{
		TPoint pos(aMessage.Int0(), aMessage.Int1());
		iConsole->Console()->SetCursorPosAbs(pos);
		break;
		}
	case RConsoleProxy::ESetCursorPosRel:
		{
		TPoint pos(aMessage.Int0(), aMessage.Int1());
		iConsole->Console()->SetCursorPosRel(pos);
		break;
		}
	case RConsoleProxy::ESetCursorHeight:
		iConsole->Console()->SetCursorHeight(aMessage.Int0());
		break;
	case RConsoleProxy::ESetTitle:
		buf.CreateL(aMessage.GetDesLengthL(0));
		aMessage.ReadL(0, buf);
		iConsole->Console()->SetTitle(buf);
		break;
	case RConsoleProxy::EClearScreen:
		iConsole->Console()->ClearScreen();
		break;
	case RConsoleProxy::EClearToEndOfLine:
		iConsole->Console()->ClearToEndOfLine();
		break;
	case RConsoleProxy::EGetScreenSize:
		{
		TPckgBuf<TSize> size;
		if (aMessage.GetDesLengthL(0)!=size.Length()) User::Leave(KErrArgument);
		size() = iConsole->Console()->ScreenSize();
		aMessage.WriteL(0, size);
		break;
		}
	case RConsoleProxy::EGetKeyCode:
		{
		TPckg<TKeyCode> kc(iKeyCode);
		if (aMessage.GetDesLengthL(0)!=kc.Length()) User::Leave(KErrArgument);
		aMessage.WriteL(0, kc);
		break;
		}
	case RConsoleProxy::EGetKeyModifiers:
		{
		TPckg<TUint> mod(iKeyModifiers);
		if (aMessage.GetDesLengthL(0)!=mod.Length()) User::Leave(KErrArgument);
		aMessage.WriteL(0, mod);
		break;
		}
	case RConsoleProxy::ESetAttributes:
		{
		if (!iConsole) User::Leave(KErrNotReady);
		ConsoleAttributes::TAttributes attributes((TUint)aMessage.Int0(), (ConsoleAttributes::TColor)aMessage.Int1(), (ConsoleAttributes::TColor)aMessage.Int2());
		TInt err = ConsoleAttributes::Set(iConsole->Console(), attributes);
		aMessage.Complete(err);
		complete = EFalse;
		break;
		}
	case RConsoleProxy::EIsConstructed:
		{
		if (aMessage.GetDesLengthL(0)!=sizeof(TBool)) User::Leave(KErrArgument);
		if (!iConsole) User::Leave(KErrNotReady);
		if (!LazyConsole::IsLazy(iConsole->Console())) User::Leave(KErrExtensionNotSupported);
		TPckgBuf<TBool> constructed = LazyConsole::IsConstructed(iConsole->Console());
		aMessage.WriteL(0, constructed);
		complete = ETrue;
		break;
		}
	default:
		aMessage.Complete(KErrNotSupported);
		}
	CleanupStack::PopAndDestroy(&buf);
	if (complete)
		{
		aMessage.Complete(KErrNone);
		}
	}
	
EXPORT_C void CConsoleProxySession::ReadComplete(TInt aStatus)
	{
	iKeyCode = iConsole->Console()->KeyCode();
	iKeyModifiers = iConsole->Console()->KeyModifiers();
	if (!iReadMessage.IsNull())
		{
		TInt err = aStatus;
		if (iReadType == EReadKeys)
			{
			if (err == KErrNone)
				{
				err = iReadMessage.Write(0, TPckg<TKeyCode>(iKeyCode));
				}
			if (err == KErrNone)
				{
				err = iReadMessage.Write(1, TPckg<TUint>(iKeyModifiers));
				}	
			}
		
		iReadMessage.Complete(err);
		}
	}
	
EXPORT_C void CConsoleProxySession::DoCreateL(const TDesC& aTitle, const TSize& aSize)
	{
	__ASSERT_ALWAYS(!iConsole, Panic(EConsoleAlreadyCreated));
	MProxiedConsole* cons = InstantiateConsoleL();
	__ASSERT_ALWAYS(cons, Panic(ENoConsoleInstatiated));
	CleanupClosePushL(*cons);
	TName procName = RProcess().Name(); // econseik sets the process name to the console title...
	TInt err = cons->Console()->Create(aTitle, aSize);
	User::RenameProcess(procName.Left(procName.Locate('['))); // ...so restore it just in case
	User::LeaveIfError(err);
	
	ConsoleCreatedL(cons);
	
	iConsole = cons;
	CleanupStack::Pop(cons);
	}
	
EXPORT_C void CConsoleProxySession::ConsoleCreatedL(MProxiedConsole*)
	{
	}

void CConsoleProxySession::CreateL(const RMessage2& aMessage)
	{
	if (iConsole) User::Leave(KErrAlreadyExists);
	RBuf title;
	title.CreateL(aMessage.GetDesLengthL(0));
	CleanupClosePushL(title);
	aMessage.ReadL(0, title);
	
	TSize size(aMessage.Int1(), aMessage.Int2());
	
	DoCreateL(title, size);
	
	CleanupStack::PopAndDestroy(&title);
	}

EXPORT_C MProxiedConsole* CConsoleProxySession::InstantiateConsoleL()
	{
	return CConsoleWrapper::NewL(iConsoleCreate);
	}


//______________________________________________________________________________
//						MProxiedConsole
EXPORT_C MProxiedConsole* MProxiedConsole::DefaultL(CConsoleBase* aConsole)
	{
	return CConsoleWrapper::NewL(aConsole);
	}
//______________________________________________________________________________
//						CConsoleWrapper
CConsoleWrapper* CConsoleWrapper::NewL(TConsoleCreateFunction aConsoleCreate)
	{
	CConsoleWrapper* self = new(ELeave)CConsoleWrapper;
	CleanupStack::PushL(self);
	self->ConstructL(aConsoleCreate);
	CleanupStack::Pop(self);
	return self;
	}
	
CConsoleWrapper* CConsoleWrapper::NewL(CConsoleBase* aConsole)
	{
	CConsoleWrapper* self = new(ELeave)CConsoleWrapper;
	self->iConsole = aConsole;
	return self;	
	}
	
CConsoleWrapper::~CConsoleWrapper()
	{
	Cancel();
	delete iConsole;
	}

void CConsoleWrapper::Open()
	{
	++iRefCount;
	}

void CConsoleWrapper::Close()
	{
	--iRefCount;
	if (!iRefCount)
		{
		delete this;
		}
	}

CConsoleBase* CConsoleWrapper::Console()
	{
	return iConsole;
	}

void CConsoleWrapper::Read(CConsoleProxySession& aSession)
	{
	if (!IsActive())
		{
		iReader = &aSession;
		iConsole->Read(iStatus);
		SetActive();
		}
	}

void CConsoleWrapper::ReadCancel()
	{
	Cancel();
	iReader = NULL;
	}

void CConsoleWrapper::RunL()
	{
	iReader->ReadComplete(iStatus.Int());
	}

void CConsoleWrapper::DoCancel()
	{
	iConsole->ReadCancel();
	}

void CConsoleWrapper::ConstructL(TConsoleCreateFunction aConsoleCreate)
	{
	iConsole = aConsoleCreate();
	User::LeaveIfNull(iConsole);
	}

CConsoleWrapper::CConsoleWrapper()
	: CActive(CActive::EPriorityStandard)
	, iRefCount(1)
	{
	CActiveScheduler::Add(this);
	}


//______________________________________________________________________________
//						CShutdownTimer
CShutdownTimer* CShutdownTimer::NewL(CConsoleProxyServer& aServer)
	{
	CShutdownTimer* self = new(ELeave)CShutdownTimer(aServer);
	CleanupStack::PushL(self);
	self->ConstructL();
	CleanupStack::Pop(self);
	return self;
	}
	
CShutdownTimer::CShutdownTimer(CConsoleProxyServer& aServer)
	: CTimer(CActive::EPriorityLow), iServer(aServer)
	{
	CActiveScheduler::Add(this);
	}
	
void CShutdownTimer::Start()
	{
	After(KServerShutdownTimer);
	}

void CShutdownTimer::RunL()
	{
	iServer.ShutdownTimerExpired();
	}

