// NetObj.cpp : CArchive-able abstract objects
//

#include "stdafx.h"
#include "NetObj.h"
#include "misc.h"
#include "msg.h"

#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif

///////////////////////////////////////////////////////////////////////
// CNetObject

CCriticalSection CNetObject::m_del;
CCriticalSection Receiver::m_del;
CCriticalSection Sender::m_del;

IMPLEMENT_SERIAL(CNetObject, CObject, 1)

//virtual
void CNetObject::Serialize(CArchive& ar)
{	
	CObject::Serialize(ar);
}

//virtual, override in derived class
BOOL CNetObject::OnSend()
{	
	return TRUE;  //ok, send this object
}

//virtual, override in derived class
void CNetObject::OnReceive(NetTransport* transport)
{	
	m_pTransport = transport;
}

CString CNetObject::GetTransportId() 
{ 
	return m_pTransport->GetTransportId(); 
}

///////////////////////////////////////////////////////////////////////
// CNetArchive

void CNetArchive::Clear()
{
	if (IsStoring())
	{
		if (m_nMapCount > 1)
		{
			m_pStoreMap->RemoveAll();
			m_pStoreMap->SetAt(NULL,(void*)(DWORD)0);
			m_nMapCount = 1;
		}
	}
	else
	{
		while (m_nMapCount > 1)
		{
			m_nMapCount--;
			m_pLoadArray->RemoveAt(m_nMapCount);
		}
	}
}

///////////////////////////////////////////////////////////////////////
// NetTransport

NetTransport::NetTransport()
{
	m_pArchiveIn  = NULL;
	m_pArchiveOut = NULL;
	m_bOldVersion = FALSE;
	m_bTerminated = FALSE;
}

//virtual
NetTransport::~NetTransport()
{
	Close(TRUE/*FALSE*/);
}

BOOL NetTransport::Open(CFile* pFile)
{
	ASSERT(pFile);
	
	try
	{
		m_pArchiveIn  = new CNetArchive(pFile, CArchive::load);
		m_pArchiveOut = new CNetArchive(pFile, CArchive::store);
		return TRUE;
	}
	catch (CMemoryException* e)
	{
		e->Delete();
		Clear();
		OnError(NET_ERRMEMORY);
	}
	catch (CArchiveException* e)
	{
		e->Delete();
		Clear();
	}
	catch(CFileException* e)
	{
		e->Delete();
		Clear();
	}
	return FALSE;
}

class Lock
{
	CCriticalSection* psec;
public:
	 Lock(CCriticalSection& sec) { /*TRACE0("[");*/ (psec=&sec)->Lock(); }
	~Lock() { /*TRACE0("]");*/ psec->Unlock(); }
};

void NetTransport::Clear()
{
//	Lock lock1(m_archin_sec);
//	Lock lock2(m_archout_sec);

	m_archin_sec.Lock();

	if (m_pArchiveIn)
	{
		delete m_pArchiveIn;
		m_pArchiveIn = NULL;
	}

	m_archin_sec.Unlock();

	m_archout_sec.Lock();

	if (m_pArchiveOut)
	{
		delete m_pArchiveOut;
		m_pArchiveOut = NULL;
	}

	m_archout_sec.Unlock();
}

void NetTransport::Term()
{
	if (m_pArchiveOut)
		m_pArchiveOut->Abort();

	if (m_pArchiveIn)
		m_pArchiveIn->Abort();

	m_bTerminated = TRUE;
}

void NetTransport::Close(BOOL term)
{
/*	if (term)
	{
		m_bTerminated = TRUE;

		TRACE0("term\n");
//		Term();
		return;
	}
*/
	TRACE0("NetTransport: close\n");

//	m_archin_sec.Lock();
//	m_archout_sec.Lock();

	TRACE0("NetTransport: ...\n");

	m_bTerminated |= term;

	m_archout_sec.Lock();

	if (m_pArchiveOut)
	{
		m_pArchiveOut->Abort();
		delete m_pArchiveOut;
		m_pArchiveOut = NULL;
	}

	m_archout_sec.Unlock();

	m_archin_sec.Lock();

	if (m_pArchiveIn)
	{
		m_pArchiveIn->Abort();
		delete m_pArchiveIn;
		m_pArchiveIn = NULL;
	}

	m_archin_sec.Unlock();

	TRACE0("NetTransport: done\n");

//	m_archout_sec.Unlock();
//	m_archin_sec.Unlock();
}

BOOL NetTransport::Send(CNetObject* pNObj)
{
//	Lock lock2(m_close_sec);

	ASSERT(pNObj);
	
	try
	{
		Lock lock(m_archout_sec);

		if (m_pArchiveOut && pNObj->OnSend())
		{
			if (m_bOldVersion)
				pNObj->Serialize(*m_pArchiveOut);
			else
				*m_pArchiveOut << pNObj;

			m_pArchiveOut->Flush();

			if (!m_bOldVersion)
				m_pArchiveOut->Clear();

			return TRUE;
		}
	}
	catch(CArchiveException* e)
	{
		OnArchiveError(e->m_cause);
		e->Delete();
	}
	catch(CFileException* e)
	{
		OnFileError(e->m_cause);
		e->Delete();

//		m_close_sec.Lock();
/*
		TRACE0("\nDisconnect on Send\n");

		if (m_receiver && !m_bTerminated)
			m_receiver->OnDisconnect();
*/
//		m_close_sec.Unlock();
	}
	return FALSE;
}

BOOL NetTransport::Receive()
{
	try
	{
		Lock lock(m_archin_sec);

		CNetObject* pNObj = NULL;

		if (m_pArchiveIn && !m_bTerminated)
		{
			do
			{
				if (m_bOldVersion)
				{
					pNObj = new Event;
					pNObj->Serialize(*m_pArchiveIn);
				}
				else
					*m_pArchiveIn >> pNObj;

				pNObj->OnReceive(this);
				BOOL disconnected = m_receiver ? m_receiver->IsDisconnect(pNObj) : TRUE;

				m_archin_sec.Unlock();
				if (m_receiver)
					m_receiver->OnReceive(pNObj);
				m_archin_sec.Lock();

				pNObj->release();
/*
				if (pNObj->m_bAutoDelete)
					delete pNObj;
				else
					pNObj->m_bAutoDelete = TRUE;
*/
				if (!m_pArchiveIn || disconnected || m_bTerminated)
					return FALSE;

				if (!m_bOldVersion)
					m_pArchiveIn->Clear();

			} while(!m_pArchiveIn->IsBufferEmpty());

			return TRUE;
		}
	}
	catch(CArchiveException* e)
	{
		OnArchiveError(e->m_cause);
		e->Delete();

		if (m_receiver && !m_bTerminated)
			m_receiver->OnDisconnect();
	}
	catch(CFileException* e)
	{
		OnFileError(e->m_cause);
		e->Delete();


		TRACE0("\nDisconnect on Receive\n");

//		m_close_sec.Lock();

		if (m_receiver && !m_bTerminated)
			m_receiver->OnDisconnect();

//		m_close_sec.Unlock();
	}
	catch(CMemoryException* e)
	{
		OnError(NET_ERRMEMORY);
		e->Delete();
	}
	return FALSE;
}

void NetTransport::OnArchiveError(int nCause)
{
	switch(nCause)
	{
		case CArchiveException::badIndex:
		case CArchiveException::badClass:
		case CArchiveException::badSchema:
			OnError(NET_ERROBJECT);
			break;

		case CArchiveException::endOfFile:
		default:
			OnError(NET_ERRARCHIVE);
			break;
	}
}

void NetTransport::OnFileError(int nCause)
{
	switch(nCause)
	{
		case CFileException::endOfFile:
			OnError(NET_ERRDROP);
			break;

		case CFileException::invalidFile:
			OnError(NET_ERRTRANSPORT);
			break;
		default:
			OnError(NET_ERRFILE);
			break;
	}
}

//virtual
void NetTransport::OnError(int nType)
{
	//cleanup
	Close(FALSE);

#ifdef _DEBUG
	TRACE0("NetTransport::");
	switch(nType)
	{
		case NET_ERRNONE:
			TRACE0("NET_ERRNONE");
			break;
		case NET_ERRMEMORY:
			TRACE0("NET_ERRMEMORY");
			break;
		case NET_ERRDROP:
			TRACE0("NET_ERRDROP");
			break;
		case NET_ERRTRANSPORT:
			TRACE0("NET_ERRTRANSPORT");
			break;
		case NET_ERRFILE:
			TRACE0("NET_ERRFILE");
			break;
		case NET_ERRARCHIVE:
			TRACE0("NET_ERRARCHIVE");
			break;
		case NET_ERROBJECT:
			TRACE0("NET_ERROBJECT");
			break;
		default:
			TRACE0("unknown");
	}
#endif
}

///////////////////////////////////////////////////////////////////////
// CNetFile
//
// intercept calls to CFile functions that would blow 
//  up if the file handle doesn't point to a disk file
//

IMPLEMENT_DYNAMIC(CNetFile, CFile)

CNetFile::CNetFile()
{
}

//virtual 
CNetFile::~CNetFile()
{
	Close();
}

///////////////////////////////////////////////////////////////////////
//supported CFile APIs

//virtual 
void CNetFile::Close()
{
}

//virtual
UINT CNetFile::Read(void* lpBuf, UINT nCount)
{
	ASSERT(FALSE);
	return 0;
}

//virtual 
void CNetFile::Write(const void* lpBuf, UINT nCount)
{
	ASSERT(FALSE);
}

///////////////////////////////////////////////////////////////////////
//unsupported CFile APIs

BOOL CNetFile::Open(
	LPCTSTR /*lpszFileName*/, UINT /*nOpenFlags*/, CFileException* /*pError*/)
{
	AfxThrowNotSupportedException();
	return FALSE;
}

CFile* CNetFile::Duplicate() const
{
	AfxThrowNotSupportedException();
	return NULL;
}

DWORD CNetFile::GetPosition() const
{
	AfxThrowNotSupportedException();
	return 0;
}

LONG CNetFile::Seek(LONG lOff, UINT nFrom)
{
	if (lOff != 0L || nFrom != current)
		TRACE0("Warning - Attempt made to seek on a CNetFile\n");
	return 0;
}

void CNetFile::SetLength(DWORD /*dwNewLen*/)
{
	AfxThrowNotSupportedException();
}

DWORD CNetFile::GetLength() const
{
	AfxThrowNotSupportedException();
	return 0;
}

void CNetFile::LockRange(DWORD /*dwPos*/, DWORD /*dwCount*/)
{
	AfxThrowNotSupportedException();
}

void CNetFile::UnlockRange(DWORD /*dwPos*/, DWORD /*dwCount*/)
{
	AfxThrowNotSupportedException();
}

void CNetFile::Flush()
{
}

void CNetFile::Abort()
{
	AfxThrowNotSupportedException();
}

///////////////////////////////////////////////////////////////////////

#ifdef _DEBUG
void CNetFile::AssertValid() const
{
	CFile::AssertValid();
}

void CNetFile::Dump(CDumpContext& dc) const
{
	CFile::Dump(dc);
}
#endif

//EOF
