// wsp_session.cpp
// 
// Copyright (c) 2002 - 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 "wsp_session.h"
#include "wsp_factory.h"
#include "wsp_request.h"
#include "wsp_panic.h"
#include "wsp_log.h"


//
// Constants.
//

_LIT(KWspThreadName, "WinSockPrtThread");
_LIT(KWspThreadNameWhilstWaitingForDeath, "WinSockPrtThread_WaitingForDeath_%Ld");
_LIT(KWspThreadStartSemaphoreName, "WinSockPrtThreadStartSemaphore");
const TInt KWspThreadStackSize = KDefaultStackSize;
const TInt KWspThreadMinHeapSize = 0x4000;		// 16 kb
const TInt KWspThreadMaxHeapSize = 0x40000;		// 256 kb


//
// RWin32Session.
//

TInt RWin32Session::Open()
	{
	return CreateThread();
	}

void RWin32Session::Close()
	{
	if (iThread.Handle())
		{
		iWin32Message.Set(CWin32Session::EClose);
		iRequest.MakeRequest(iWin32Message);
		iThread.Close();
		}
	}

TInt RWin32Session::MakeRequest(TWin32Message& aMessage)
	{
	return iRequest.MakeRequest(aMessage);
	}

TInt RWin32Session::CreateThread()
	{
	WSP_LOG(WspLog::Write(_L("RWin32Session::CreateThread")));
#ifdef _DEBUG
	TFindThread findThread(KWspThreadName);
	TFullName name;
	__ASSERT_DEBUG(findThread.Next(name)==KErrNotFound, Panic(EWinSockPrtThreadAlreadyExists));
#endif
	
	RSemaphore threadStartSemaphore;
	TInt err = threadStartSemaphore.CreateGlobal(KWspThreadStartSemaphoreName, 0, EOwnerThread);
	if (err)
		{
		return err;
		}

	err = iThread.Create(KWspThreadName, WinSockPrtThreadStart, KWspThreadStackSize, KWspThreadMinHeapSize, KWspThreadMaxHeapSize, &iRequest, EOwnerThread);
	if (err == KErrNone)
		{
		err = iRequest.DuplicateParentThreadHandle(iThread);
		if (err)
			{
			iThread.Close();
			threadStartSemaphore.Close();
			return err;
			}
		//iThread.SetPriority(EPriorityAbsoluteVeryLow); // Hack to prevent the thread from blocking all threads lower than the default priority. Should really be using a raw Win32 thread rather than a Symbian one.
		iThread.Resume();
		threadStartSemaphore.Wait();
		threadStartSemaphore.Close();
		}
	else
		{
		threadStartSemaphore.Close();
		}
	return err;
	}


//
// Thread start functions.
//

GLDEF_C TInt WinSockPrtThreadStart(TAny* aPtr)
	{
	__UHEAP_MARK;
	CTrapCleanup* trapCleanup = CTrapCleanup::New();
	if (trapCleanup == NULL)
		{
		return KErrNoMemory;
		}
	TRAPD(err, InitialiseAndRunThreadL(aPtr));
	WSP_LOG(WspLog::Close());
	delete trapCleanup;
	__UHEAP_MARKEND;
	return err;
	}

GLDEF_C void InitialiseAndRunThreadL(TAny* aPtr)
	{
	WSP_LOG(User::LeaveIfError(WspLog::Open()));
	CWin32Scheduler* win32Scheduler = CWin32Scheduler::NewL();
	CleanupStack::PushL(win32Scheduler);
	TWin32Request* request = static_cast<TWin32Request*>(aPtr);
	CWin32Factory* win32Factory = CWin32Factory::NewL(*request, *win32Scheduler);
	CleanupStack::PushL(win32Factory);

	RSemaphore threadStartSemaphore;
	User::LeaveIfError(threadStartSemaphore.OpenGlobal(KWspThreadStartSemaphoreName, EOwnerThread));
	threadStartSemaphore.Signal();
	threadStartSemaphore.Close();
	
	win32Scheduler->Start();

	CleanupStack::PopAndDestroy(2, win32Scheduler);
	}


//
// CWin32Session.
//

void CWin32Session::AddSubSessionL(CWin32SubSession* aSubSession)
	{
	TInt subSessionHandle = reinterpret_cast<TInt>(aSubSession);
	TPckg<TInt> subSessionHandlePckg(subSessionHandle);
	iRequest.Message().WriteBuffer().Copy(subSessionHandlePckg);
	CleanupStack::PushL(aSubSession);
	User::LeaveIfError(iSubSessions.Append(aSubSession));
	CleanupStack::Pop(aSubSession);
	}

CWin32Session::~CWin32Session()
	{
	__ASSERT_DEBUG(iSubSessions.Count() == 0, Panic(EWinSockPrtSubSessionListNotEmpty));
	iSubSessions.Close();
	}

CWin32Session::CWin32Session(TWin32Request& aRequest, CWin32Scheduler& aScheduler)
	: CWin32ActiveObject(aScheduler), iRequest(aRequest)
	{
	}

void CWin32Session::ConstructL()
	{
	CWin32ActiveObject::ConstructL();
	iRequest.SetEventHandle(iEvent);
	}

void CWin32Session::Run()
	{
	TInt err = KErrNone;

	CWin32SubSession* subSession = reinterpret_cast<CWin32SubSession*>(iRequest.SubSession());
	TWin32Message& message = iRequest.Message();
	if (subSession == NULL)
		{
		switch (message.OppCode())
			{
			case EClose:
				{
				Close();
				break;
				}
			case ECloseSubSession:
				{
				CloseSubSession();
				break;
				}
			default:
				{
				// Pass on to sub-class.
				TRAP(err, ServiceL(message));
				}
			}
		}
	else
		{
		TRAP(err, subSession->ServiceL(message));
		}

	iRequest.Requested(err);
	}

void CWin32Session::Close()
	{
	// This thread could take a while to die, so give it a different name so that subsequent client attempt to create another one don't fail with KErrInUse.
	TTime time;
	time.UniversalTime();
	const TInt64& int64 = time.Int64();
	TBuf<64> newThreadName;
	newThreadName.Format(KWspThreadNameWhilstWaitingForDeath(), int64);
	User::RenameThread(newThreadName); // Throw away error code.
	iScheduler.Stop();
	}

void CWin32Session::CloseSubSession()
	{
	TInt handle;
	TPckg<TInt> handlePckg(handle);
	handlePckg.Copy(iRequest.Message().ReadBuffer());
	CWin32SubSession* subSessionToClose = reinterpret_cast<CWin32SubSession*>(handle);
	TInt pos = iSubSessions.Find(subSessionToClose);
	__ASSERT_DEBUG(pos >= 0, Panic(EWinSockPrtUnableToFindSubSession));
	delete subSessionToClose;
	iSubSessions.Remove(pos);
	}
