// log.cpp
// 
// Copyright (c) 2006 - 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 "log.h"
#ifdef IOSRV_LOGGING

#include "server.h"
#include "clientserver.h"

#ifndef IOSRV_LOGGING_USES_CLOGGER
_LIT(KLogFileName, "c:\\logs\\iosrv.txt");
_LIT8(KNewLine, "\r\n");
#endif

#if !defined(__WINS__) || defined (EKA2)
CIoLog* gLog = NULL;
#endif

CIoLog* CIoLog::NewL(RFs& aFs)
	{
	CIoLog* self = new(ELeave) CIoLog(aFs);
	CleanupStack::PushL(self);
	self->ConstructL();
	CleanupStack::Pop(self);
	return self;
	}

CIoLog::~CIoLog()
	{
#ifdef IOSRV_LOGGING_USES_CLOGGER
	iClogger.Close();
#else
	iFile.Close();
#endif
#if defined(__WINS__) && !defined(EKA2)
	Dll::SetTls(NULL);
#endif
	}


#define CASE_RETURN_LIT(XXX) case XXX: { _LIT(_KLit, #XXX); return &_KLit; }
#define DEFAULT_RETURN_LIT(XXX) default: { _LIT(_KLit, XXX); return &_KLit; }
	
const TDesC* CIoLog::StringifyError(TInt aError)
	{
	switch (aError)
		{
		CASE_RETURN_LIT(KErrNone);
		CASE_RETURN_LIT(KErrNotFound);
		CASE_RETURN_LIT(KErrGeneral);
		CASE_RETURN_LIT(KErrCancel);
		CASE_RETURN_LIT(KErrNoMemory);
		CASE_RETURN_LIT(KErrNotSupported);
		CASE_RETURN_LIT(KErrArgument);
		CASE_RETURN_LIT(KErrBadHandle);
		CASE_RETURN_LIT(KErrOverflow);
		CASE_RETURN_LIT(KErrUnderflow);
		CASE_RETURN_LIT(KErrAlreadyExists);
		CASE_RETURN_LIT(KErrPathNotFound);
		CASE_RETURN_LIT(KErrDied);
		CASE_RETURN_LIT(KErrInUse);
		CASE_RETURN_LIT(KErrServerTerminated);
		CASE_RETURN_LIT(KErrServerBusy);
		CASE_RETURN_LIT(KErrCompletion);
		CASE_RETURN_LIT(KErrNotReady);
		CASE_RETURN_LIT(KErrUnknown);
		CASE_RETURN_LIT(KErrCorrupt);
		CASE_RETURN_LIT(KErrAccessDenied);
		CASE_RETURN_LIT(KErrLocked);
		CASE_RETURN_LIT(KErrWrite);
		CASE_RETURN_LIT(KErrDisMounted);
		CASE_RETURN_LIT(KErrEof);
		CASE_RETURN_LIT(KErrDiskFull);
		CASE_RETURN_LIT(KErrBadDriver);
		CASE_RETURN_LIT(KErrBadName);
		CASE_RETURN_LIT(KErrCommsLineFail);
		CASE_RETURN_LIT(KErrCommsFrame);
		CASE_RETURN_LIT(KErrCommsOverrun);
		CASE_RETURN_LIT(KErrCommsParity);
		CASE_RETURN_LIT(KErrTimedOut);
		CASE_RETURN_LIT(KErrCouldNotConnect);
		CASE_RETURN_LIT(KErrCouldNotDisconnect);
		CASE_RETURN_LIT(KErrDisconnected);
		CASE_RETURN_LIT(KErrBadLibraryEntryPoint);
		CASE_RETURN_LIT(KErrBadDescriptor);
		CASE_RETURN_LIT(KErrAbort);
		CASE_RETURN_LIT(KErrTooBig);
		CASE_RETURN_LIT(KErrDivideByZero);
		CASE_RETURN_LIT(KErrBadPower);
		CASE_RETURN_LIT(KErrDirFull);
		CASE_RETURN_LIT(KErrHardwareNotAvailable);
#ifdef EKA2
		CASE_RETURN_LIT(KErrPermissionDenied);
#endif
//		CASE_RETURN_LIT(KErrExtensionNotSupported);
//		CASE_RETURN_LIT(KErrCommsBreak);
		DEFAULT_RETURN_LIT("*** ERROR UNKNOWN ***");
		}
	}
	
const TDesC* CIoLog::StringifyOpcode(TInt aOpCode)
	{
	switch (aOpCode)
		{
		CASE_RETURN_LIT(EIoHandleClose);
		CASE_RETURN_LIT(EIoHandleSetOwner);
		CASE_RETURN_LIT(EIoHandleSetUnderlyingConsole);
		CASE_RETURN_LIT(EIoHandleAttachedToConsole);
		CASE_RETURN_LIT(EIoCreateReader);
		CASE_RETURN_LIT(EIoOpenReaderByThreadId);
		CASE_RETURN_LIT(EIoOpenReaderByExplicitThreadId);
		CASE_RETURN_LIT(EIoDuplicateReader);
		CASE_RETURN_LIT(EIoCreateWriter);
		CASE_RETURN_LIT(EIoOpenWriterByThreadId);
		CASE_RETURN_LIT(EIoOpenWriterByExplicitThreadId);
		CASE_RETURN_LIT(EIoDuplicateWriter);
		CASE_RETURN_LIT(EIoSetReadWriteMode);
		CASE_RETURN_LIT(EIoSetReadMode);
		CASE_RETURN_LIT(EIoSetReaderToForeground);
		CASE_RETURN_LIT(EIoRead);
		CASE_RETURN_LIT(EIoSetLineSeparator);
		CASE_RETURN_LIT(EIoWrite);
		CASE_RETURN_LIT(EIoReadCancel);
		CASE_RETURN_LIT(EIoWriteCancel);
		CASE_RETURN_LIT(EIoIsForegroundReader);
		CASE_RETURN_LIT(EIoConsoleWaitForKey);
		CASE_RETURN_LIT(EIoConsoleWaitForKeyCancel);
		CASE_RETURN_LIT(EIoConsoleCaptureKey);
		CASE_RETURN_LIT(EIoConsoleCancelCaptureKey);
		CASE_RETURN_LIT(EIoConsoleCaptureAllKeys);
		CASE_RETURN_LIT(EIoConsoleCancelCaptureAllKeys);
		CASE_RETURN_LIT(EIoConsoleCursorPos);
		CASE_RETURN_LIT(EIoConsoleSetCursorPosAbs);
		CASE_RETURN_LIT(EIoConsoleSetCursorPosRel);
		CASE_RETURN_LIT(EIoConsoleSetCursorHeight);
		CASE_RETURN_LIT(EIoConsoleSetTitle);
		CASE_RETURN_LIT(EIoConsoleClearScreen);
		CASE_RETURN_LIT(EIoConsoleClearToEndOfLine);
		CASE_RETURN_LIT(EIoConsoleScreenSize);
		CASE_RETURN_LIT(EIoConsoleSetAttributes);
		CASE_RETURN_LIT(EIoEndPointAttachReader);
		CASE_RETURN_LIT(EIoEndPointAttachWriter);
		CASE_RETURN_LIT(EIoEndPointSetForegroundReadHandle);
		CASE_RETURN_LIT(EIoCreatePipe);
		CASE_RETURN_LIT(EIoCreateConsole);
		CASE_RETURN_LIT(EIoOpenConsole);
		CASE_RETURN_LIT(EIoConsoleImplementation);
		CASE_RETURN_LIT(EIoCreateFile);
		CASE_RETURN_LIT(EIoCreateNull);
		CASE_RETURN_LIT(EIoSetObjectName);
		CASE_RETURN_LIT(EIoCreatePersistentConsole);
		CASE_RETURN_LIT(EIoOpenPersistentConsoleByName);
		CASE_RETURN_LIT(EIoPersistentConsoleAttachReadEndPoint);
		CASE_RETURN_LIT(EIoPersistentConsoleAttachWriteEndPoint);
		CASE_RETURN_LIT(EIoPersistentConsoleDetachReadEndPoint);
		CASE_RETURN_LIT(EIoPersistentConsoleDetachWriteEndPoint);
		CASE_RETURN_LIT(EIoPersistentConsoleNotifyReadDetach);
		CASE_RETURN_LIT(EIoPersistentConsoleNotifyWriteDetach);
		CASE_RETURN_LIT(EIoPersistentConsoleCancelNotifyReadDetach);
		CASE_RETURN_LIT(EIoPersistentConsoleCancelNotifyWriteDetach);
		CASE_RETURN_LIT(EIoHandleIsType);
		CASE_RETURN_LIT(EIoHandleGetName);
		CASE_RETURN_LIT(EIoHandleEquals);
		CASE_RETURN_LIT(EIoReadHandleNotifyChange);
		CASE_RETURN_LIT(EIoReadHandleCancelNotifyChange);
		CASE_RETURN_LIT(EIoDuplicateReaderHandleFromThread);
		CASE_RETURN_LIT(EIoDuplicateWriterHandleFromThread);
		DEFAULT_RETURN_LIT("*** OPCODE UNKNOWN ***");
		}
	}

class TOverflowTruncate : public TDes16Overflow
	{
public:
	virtual void Overflow(TDes16&) {}
	};

class RMessageAccess : public RMsg
	{
public:
	TInt Handle() const { return iHandle; }
	};

void CIoLog::StartServiceLC(const RMsg& aMessage)
	{
	CIoLog& self = Self();
	ASSERT(!self.iInServiceL);
	self.iScratchBuf.SetLength(0);
	self.iScratchBuf = ClientNameL(aMessage);
	CleanupStack::PushL(TCleanupItem(EndService, &self));
	TOverflowTruncate overflow;
	self.iScratchBuf.AppendFormat(_L(" requested %S (opcode: %d, message handle: %d)"), &overflow, StringifyOpcode(aMessage.Function()), aMessage.Function(), static_cast<const RMessageAccess&>(aMessage).Handle());
	self.Write(self.iScratchBuf);
	self.iInServiceL = ETrue;
	}

void CIoLog::EndService(TAny* aSelf)
	{
	CIoLog& self = *(static_cast<CIoLog*>(aSelf));
	ASSERT(self.iInServiceL);
	self.Write(KNullDesC);
	self.iInServiceL = EFalse;
	}

void CIoLog::LogCompletion(const RMsg& aMessage, TInt aError)
	{
	TFullName threadName(_L("unknown"));
	TRAP_IGNORE(threadName = ClientNameL(aMessage));
	CIoLog& self = Self();
	self.iScratchBuf.SetLength(0);
	TOverflowTruncate overflow;
	self.iScratchBuf.AppendFormat(_L("Completing %S's request (opcode: %S, message handle: %d) with %S(%d)"), &overflow, &threadName, StringifyOpcode(aMessage.Function()), static_cast<const RMessageAccess&>(aMessage).Handle(), StringifyError(aError), aError);
	self.Write(self.iScratchBuf);
	}

void CIoLog::Write(const TDesC& aData)
	{
	CIoLog& self = Self();
#ifdef IOSRV_LOGGING_USES_CLOGGER
	if (aData.Length() == 0) return; // The trick of writing knulldesc to get a newline is not really appropriate with clogger
	self.iClogger.Log(aData);
#else
	if (self.iFile.SubSessionHandle())
		{
		self.iNarrowBuf.Copy(aData.Left(self.iNarrowBuf.MaxLength() - KNewLine().Length() - 1));
		if (self.iInServiceL)
			{
			self.iNarrowBuf.Insert(0, _L8("\t"));
			}
		self.iNarrowBuf.Append(KNewLine);
		self.iFile.Write(self.iNarrowBuf);
		self.iFile.Flush();
		}
#endif
	}

void CIoLog::Printf(TRefByValue<const TDesC> aFmt, ...)
	{
	TOverflowTruncate overflow;
	VA_LIST list;
	VA_START(list, aFmt);
	CIoLog& self = Self();
	self.iScratchBuf.SetLength(0);
	self.iScratchBuf.AppendFormatList(aFmt, list, &overflow);
	Write(self.iScratchBuf);
	}

CIoLog::CIoLog(RFs& aFs)
	: iFs(aFs)
	{
	}

void CIoLog::ConstructL()
	{
#ifdef IOSRV_LOGGING_USES_CLOGGER
	_LIT(KIoSrvLog, "iosrv");
	User::LeaveIfError(iClogger.Connect(KIoSrvLog));
#else
//	iFs.MkDirAll(KLogFileName);
	/*User::LeaveIfError*/(iFile.Replace(iFs, KLogFileName, EFileWrite));
#endif
#if defined(__WINS__) && !defined(EKA2)
	Dll::SetTls(this);
#else
	gLog = this;
#endif
	Write(KNullDesC);
	Write(_L("New log created"));
	}

CIoLog& CIoLog::Self()
	{
#if defined(__WINS__) && !defined(EKA2)
	return *(static_cast<CIoLog*>(Dll::Tls()));
#else
	return *gLog;
#endif
	}

#endif
