// tcpcsy.cpp
// 
// Copyright (c) 2008 - 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 <c32comm.h>
#include "panic.h"
#include "tcpcsy.h"
#include "tcpcsylog.h"


//
// CTcpPort.
//

TInt CTcpPort::LinkOffset()
	{
	return _FOFF(CTcpPort, iLink);
	}

CTcpPort* CTcpPort::NewLC(CTcpPortFactory& aFactory, CConfig::TMode aMode, TUint aProtocolFamily, TUint aProtocol, const TInetAddr& aAddress, RSocketServ& aSocketServ)
	{
	CTcpPort* self = new(ELeave) CTcpPort(aFactory);
	CleanupClosePushL(*self);
	self->ConstructL(aMode, aProtocolFamily, aProtocol, aAddress, aSocketServ);
	return self;
	}

CTcpPort::CTcpPort(CTcpPortFactory& aFactory)
	: iFactory(aFactory)
	{
	LOG("Created new CTcpPort object 0x%08x", this);
	}

void CTcpPort::ConstructL(CConfig::TMode aMode, TUint aProtocolFamily, TUint aProtocol, const TInetAddr& aAddress, RSocketServ& aSocketServ)
	{
	iInetAddr = aAddress;

	if (aMode == CConfig::EActive)
		{
		iConnector = CConnector::NewL(aSocketServ, iSocket, aProtocolFamily, aProtocol, iInetAddr, *this);
		}
	else
		{
		iListener = CListener::NewL(aSocketServ, iSocket, aProtocolFamily, aProtocol, iInetAddr, *this);
		}

	iReader = CReader::NewL(iSocket, *this);
	iWriter = CWriter::NewL(iSocket, *this);
	}

CTcpPort::~CTcpPort()
	{
	delete iWriter;
	delete iReader;
	delete iConnector;
	if (iSocketConnected)
		{
		TRequestStatus status;
		iSocket.Shutdown(RSocket::ENormal, status);
		User::WaitForRequest(status);
		iSocket.Close();
		}
	iLink.Deque();
	}

void CTcpPort::StartRead(const TAny*, TInt aLength)
	{
	LOG("CTcpPort::StartRead this=0x%08x, aLength=%d", this, aLength);

	if (iError)
		{
		ReadError(iError);
		}
	else if (aLength == 0)
		{
		ReadCompleted(KErrNone);
		}
	else
		{
		if (aLength < 0)
			{
			iReader->StartRead(-aLength, CReader::EOneOrMore);
			}
		else
			{
			iReader->StartRead(aLength, CReader::EFull);
			}
		}
	}

void CTcpPort::ReadCancel()
	{
	LOG("CTcpPort::ReadCancel this=0x%08x", this);
	iReader->Abort();
	ReadError(KErrCancel);
	}

TInt CTcpPort::QueryReceiveBuffer(TInt& aLength) const
	{
	iReader->GetBufferLength(aLength);
	return KErrNone;
	}

void CTcpPort::ResetBuffers(TUint)
	{
	// Doesn't really have much meaning for the TCPCSY as reads and writes are mapped directly onto RSocket calls.
	}

void CTcpPort::StartWrite(const TAny* aClientBuffer, TInt aLength)
	{
	LOG("CTcpPort::StartWrite this=0x%08x, aClientBuff=0x%08x, aLength=%d", this, aClientBuffer, aLength);
	
	TInt err = KErrNone;

	if (iError)
		{
		err = iError;
		}
	else if (aLength > 0)
		{
		if (iWriteBuf.MaxLength() < aLength)
			{
			err = iWriteBuf.ReAlloc(aLength);
			}

		if (err == KErrNone)
			{
			iWriteBuf.Zero();
			err = IPCRead(aClientBuffer, iWriteBuf, 0);
			if (err == KErrNone)
				{
				iWriter->StartWrite(iWriteBuf);
				}
			}
		}

	if (err || (aLength == 0))
		{
		WriteComplete(err);
		}
	}

void CTcpPort::WriteCancel()
	{
	LOG("CTcpPort::WriteCancel this=0x%08x", this);
	iWriter->Abort();
	WriteComplete(KErrCancel);
	}

void CTcpPort::Break(TInt)
	{
	}

void CTcpPort::BreakCancel()
	{
	}

TInt CTcpPort::GetConfig(TDes8&) const
	{
	return KErrNone;
	}

TInt CTcpPort::SetConfig(const TDesC8&)
	{
	return KErrNone;
	}

TInt CTcpPort::GetCaps(TDes8&)
	{
	return KErrNone;
	}

TInt CTcpPort::SetServerConfig(const TDesC8&)
	{
	return KErrNone;
	}

TInt CTcpPort::GetServerConfig(TDes8&)
	{
	return KErrNone;
	}

TInt CTcpPort::GetSignals(TUint&)
	{
	return KErrNone;
	}

TInt CTcpPort::SetSignalsToMark(TUint)
	{
	return KErrNone;
	}

TInt CTcpPort::SetSignalsToSpace(TUint)
	{
	return KErrNone;
	}

TInt CTcpPort::GetReceiveBufferLength(TInt&) const
	{
	return KErrNone;
	}

TInt CTcpPort::SetReceiveBufferLength(TInt)
	{
	return KErrNone;
	}

void CTcpPort::Destruct()
	{
	delete this;
	}

void CTcpPort::NotifySignalChange(TUint)
	{
	}

void CTcpPort::NotifySignalChangeCancel()
	{
	}


void CTcpPort::NotifyConfigChange()
	{
	}

void CTcpPort::NotifyConfigChangeCancel()
	{
	}

void CTcpPort::NotifyFlowControlChange()
	{
	}

void CTcpPort::NotifyFlowControlChangeCancel()
	{
	}

void CTcpPort::NotifyBreak()
	{
	}

void CTcpPort::NotifyBreakCancel()
	{
	}

void CTcpPort::NotifyDataAvailable()
	{
	}

void CTcpPort::NotifyDataAvailableCancel()
	{
	}

void CTcpPort::NotifyOutputEmpty()
	{
	}

void CTcpPort::NotifyOutputEmptyCancel()
	{
	}

TInt CTcpPort::GetFlowControlStatus(TFlowControl&)
	{
	return KErrNotSupported;
	}

TInt CTcpPort::GetRole(TCommRole& aRole)
	{
	aRole = iRole;
	return KErrNone;
	}

TInt CTcpPort::SetRole(TCommRole aRole)
	{
	iRole = aRole;
	return KErrNone;
	}

void CTcpPort::FreeMemory()
	{
	}

void CTcpPort::ConnectionComplete(TInt aError)
	{
	LOG("CTcpPort::ConnectionComplete this=0x%08x, aError=%d", this, aError);
	if (aError == KErrNone)
		{
		iSocketConnected = ETrue;
		iReader->SocketConnected();
		iWriter->SocketConnected();
		}
	else
		{
		iError = aError;
		}
	}

void CTcpPort::ListenComplete(TInt aError)
	{
	LOG("CTcpPort::ListenComplete this=0x%08x, aError=%d", this, aError);
	if (aError == KErrNone)
		{
		iReader->SocketConnected();
		iWriter->SocketConnected();
		}
	else
		{
		iError = aError;
		}
	}

void CTcpPort::ReadComplete(const TDesC8& aData)
	{
	LOG("CTcpPort::ReadComplete this=0x%08x", this);
	TInt err = IPCWrite(NULL, aData, 0);
	ReadCompleted(KErrNone);
	}

void CTcpPort::ReadError(TInt aError)
	{
	LOG("CTcpPort::ReadError this=0x%08x, aError=%d", this, aError);
	ReadCompleted(aError);
	}

void CTcpPort::WriteComplete(TInt aError)
	{
	LOG("CTcpPort::WriteComplete this=0x%08x, aError=%d", this, aError);
	WriteCompleted(aError);
	}



//
// CTcpPortFactory.
//

CTcpPortFactory* CTcpPortFactory::NewL()
	{
	CTcpPortFactory* self = new(ELeave)CTcpPortFactory;
	CleanupClosePushL(*self);
	self->ConstructL();
	CleanupStack::Pop(self);
	return self;
	}

CPort* CTcpPortFactory::NewPortL(const TUint aUnit)
	{
	TInetAddr inetAddr;
	iConfig->GetAddressL(aUnit, inetAddr);
	CTcpPort* port = CTcpPort::NewLC(*this, iConfig->ModeL(aUnit), iConfig->ProtocolFamilyL(aUnit), iConfig->ProtocolL(aUnit), inetAddr, iSocketServ);
	TBuf<10> name;
	name.Num(aUnit);
	port->SetNameL(&name);
	CleanupStack::Pop(port);
	iPorts.AddLast(*port);
	return port;
	}

void CTcpPortFactory::Info(TSerialInfo& aSerialInfo)
/**
 * This method fills information into the passed structure.  It is required for factory objects.
 *
 * @param	aSerialInfo		-	a reference to the structure to fill in.
 *
 * @return	None
 */
	{
	aSerialInfo.iDescription = KCsyName;
	aSerialInfo.iName = KCsyName;
	iConfig->UnitRange(aSerialInfo.iLowUnit, aSerialInfo.iHighUnit);
	}

CTcpPortFactory::CTcpPortFactory()
	: iPorts(CTcpPort::LinkOffset())
	{
	}

void CTcpPortFactory::ConstructL()
	{
#ifdef __FLOG_ACTIVE	
	
#ifdef LOG_CSY_EVENTS
#pragma message("General logging enabled")
	(void) iEventLogger.Connect();
	iEventLogger.SetLogTags(KDebugSubSystem(), KDebugCategoryEvents());
#endif // LOG_CSY_EVENTS

#ifdef LOG_CSY_TX
#pragma message("Tx logging enabled")
	(void) iTxLogger.Connect();
	iTxLogger.SetLogTags(KDebugSubSystem(), KDebugCategoryTx());
#endif // LOG_CSY_TX

#ifdef LOG_CSY_RX
#pragma message("Rx logging enabled")
	(void) iRxLogger.Connect();
	iRxLogger.SetLogTags(KDebugSubSystem(), KDebugCategoryRx());
#endif // LOG_CSY_RX

#endif // __FLOG_ACTIVE

	SetNameL(&(KCsyName()));
	iConfig = CConfig::NewL(*this);
	User::LeaveIfError(iSocketServ.Connect());
	}
	
CTcpPortFactory::~CTcpPortFactory()
	{
	delete iConfig;
	iSocketServ.Close();

#ifdef __FLOG_ACTIVE

#ifdef LOG_CSY_EVENTS
	iEventLogger.Close();
#endif // LOG_CSY_EVENTS

#ifdef LOG_CSY_TX
	iTxLogger.Close();
#endif // LOG_CSY_TX

#ifdef LOG_CSY_RX
	iRxLogger.Close();
#endif // LOG_CSY_RX

#endif // __FLOG_ACTIVE
	}
	
TSecurityPolicy CTcpPortFactory::PortPlatSecCapability(TUint /*aPort*/) const
	{
	return TSecurityPolicy(ECapabilityNetworkServices);
	}

extern "C"
	{
	IMPORT_C CSerial * LibEntry(void);
	}


EXPORT_C CSerial* LibEntry(void)
	{
	return CTcpPortFactory::NewL();
	}

void Panic(TTcpCsyPanicReason aReason)
	{
	_LIT(KCategory, "tcpcsy");
	User::Panic(KCategory(), aReason);
	}
