#include "stdafx.h"
#include "csmartsocket.h"
#include "gmud32.h"


#define new DEBUG_NEW

CSmartSocket::CSmartSocket()
{
	m_pParent=0;
	m_pRecieveBuf = new char[BUFFSIZE];
	m_pCombineBuf= new char[BUFFSIZE];

	m_pFragBuf = new char[BUFFSIZE];
	*m_pFragBuf=*m_pRecieveBuf=*m_pCombineBuf=0;
	m_bConnected=FALSE;
	m_bPaused=FALSE;
}

CSmartSocket::~CSmartSocket()
{
	delete m_pRecieveBuf;
	delete m_pCombineBuf;
	delete m_pFragBuf;
}

CSmartSocket::SetParentWnd(CWnd *pParent)
{
	m_pParent=pParent;
	return TRUE;
}

BOOL __cdecl CSmartSocket::Printf(LPSTR format, ...)
{
	ASSERT_VALID(this);
	
	if(!m_bConnected)
		return FALSE;
	// do the sprintf stuff
	LPSTR buffer = new char[BUFFSIZE];

	va_list marker;
	va_start(marker,format);
	vsprintf(buffer,format,marker);
	va_end(marker);

	// add it to our output buffer	
	m_asOutBuffer.Add(buffer);
	delete buffer;
	AsyncSelect(FD_READ|FD_WRITE|FD_CLOSE);
	return TRUE;
}

void CSmartSocket::OnConnect(int nErrorCode)
{
	if(!nErrorCode)
		m_bConnected=TRUE;
	m_pParent->SendMessage(WM_SOCKET_CONNECTED,nErrorCode);
	m_bPaused=FALSE;
}

void CSmartSocket::OnSend(int nErrorCode )
{
	if(nErrorCode)
		TRACE("\nError in send: (%d) - %s.",nErrorCode,SockerrToString(nErrorCode));

	if(m_asOutBuffer.GetSize())
	{
		if(Send(m_asOutBuffer[0],m_asOutBuffer[0].GetLength())==SOCKET_ERROR)
			TRACE("\nError in send function.");
		else
			m_asOutBuffer.RemoveAt(0);
	}
}

void CSmartSocket::OnClose(int nErrorCode)
{
	m_bConnected=FALSE;
	Close();
	m_pParent->PostMessage(WM_SOCKET_DISCONNECTED,nErrorCode);
	TRACE("\n\nOnClose Called.");
	Pause(FALSE);
}

BOOL CSmartSocket::HardClose()
{
	TRACE("\nSocket thinks it's connected? (%d)",m_bConnected);
	m_bConnected=FALSE;
    LINGER linger;
    if( m_hSocket == INVALID_SOCKET )
    {
        //  Ignore invalid sockets.
		TRACE("\nHard Close Aborted, not a valid socket");
        return FALSE;
    }
    linger.l_onoff  = TRUE;
    linger.l_linger = 0;
    SetSockOpt(SO_LINGER,(CHAR FAR *)&linger,sizeof(linger) );

    //  Close the socket.
	TRACE("\nHard Close Performed.");
    Close();
	m_hSocket = INVALID_SOCKET;
	m_pParent->SendMessage(WM_SOCKET_DISCONNECTED);
	return TRUE;
}

BOOL CSmartSocket::IsConnected()
{
	return m_bConnected;
}

// over-ridden functions
void CSmartSocket::OnReceive(int nErrorCode )
{
	if(m_bPaused)
		return;
	static int recursion = -1;
	recursion ++;
	if(recursion)
	{
		TRACE("\nRecursion: %d",recursion);
	}
	ASSERT_VALID(this);
	if(nErrorCode)
		TRACE("\nError in receive: (%d) - %s.",nErrorCode,SockerrToString(nErrorCode));

	LPSTR pBuf = new char [BUFFSIZE];
	LPSTR pAdjBuf = pBuf;				// adjusted buffer for frag merging.
	if(*m_pFragBuf)	// special routine to avoid sending out incomplete ansi sequences.
	{
		strcpy(pBuf,m_pFragBuf);	// first copy the frag
		*m_pFragBuf=0;
		pAdjBuf+=strlen(pBuf);
	}
	int retcode=1;
	retcode = Receive(pAdjBuf,BUFFSIZE-50);
	if(retcode>0 && retcode !=SOCKET_ERROR)
	{
		pAdjBuf[retcode]=0;	// null terminate it.
		if(GetApp()->m_bAnsi)	// special routine to avoid sending out incomplete ansi sequences.
		{
			// check the combined string for a trailing fragment
			LPSTR pCheck=pAdjBuf+(retcode-1);
			// pCheck should be pointing at the last char...
			while(pCheck>pBuf)
			{

				if(isdigit(*pCheck) && pCheck>pBuf)
					pCheck--;
				if(isdigit(*pCheck)&& pCheck>pBuf)
					pCheck--;
				if(*pCheck==';'&& pCheck>pBuf)
					pCheck--;
				else if(*pCheck=='['&& pCheck>pBuf)
					pCheck--;
				else
					break;
			}
			if(*pCheck==27) // we have a fragment
			{
				strcpy(m_pFragBuf,pCheck);	// copy it for storage
				*pCheck=0;					// truncate the string
			}
			// okay we're now ready to send the ansi-complete string.
			AfxCheckMemory();
			m_pParent->PostMessage(WM_SOCKET_STRING_RECIEVED,0,(long)pBuf);
		}
		else
			m_pParent->PostMessage(WM_SOCKET_STRING_RECIEVED,0,(long)pBuf);
	}
	else
	{
		delete pBuf;
	}

	recursion--;
}

CSmartSocket::Pause( BOOL bPause)
{
	if(bPause)
	{
		m_bPaused=TRUE;
	}
	else
	{
		m_bPaused=FALSE;
		OnReceive(0);
	}

	return bPause;
}

LPSTR CSmartSocket::SockerrToString( UINT serr )
{
    switch( serr )
    {
	    case WSAENAMETOOLONG :
	        return "Name too long";

	    case WSANOTINITIALISED :
	        return "Not initialized";

	    case WSASYSNOTREADY :
	        return "System not ready";

	    case WSAVERNOTSUPPORTED :
	        return "Version is not supported";

	    case WSAESHUTDOWN :
	        return "Can't send after socket shutdown";

	    case WSAEINTR :
	        return "Interrupted system call";

	    case WSAHOST_NOT_FOUND :
	        return "Host not found";

	    case WSATRY_AGAIN :
	        return "Try again";

	    case WSANO_RECOVERY :
	        return "Non-recoverable error";

	    case WSANO_DATA :
	        return "Hostname lookup failure";

	    case WSAEBADF :
	        return "Bad file number";

	    case WSAEWOULDBLOCK :
	        return "Operation would block";

	    case WSAEINPROGRESS :
	        return "Operation now in progress";

	    case WSAEALREADY :
	        return "Operation already in progress";

	    case WSAEFAULT :
	        return "Bad address";

	    case WSAEDESTADDRREQ :
	        return "Destination address required";

	    case WSAEMSGSIZE :
	        return "Message too long";

	    case WSAEPFNOSUPPORT :
	        return "Protocol family not supported";

	    case WSAENOTEMPTY :
	        return "Directory not empty";

	    case WSAEPROCLIM :
	        return "EPROCLIM returned";

	    case WSAEUSERS :
	        return "EUSERS returned";

	    case WSAEDQUOT :
	        return "Disk quota exceeded";

	    case WSAESTALE :
	        return "ESTALE returned";

	    case WSAEINVAL :
	        return "Invalid argument";

	    case WSAEMFILE :
	        return "Too many open files";

	    case WSAEACCES :
	        return "Access denied";

	    case WSAELOOP :
	        return "Too many levels of symbolic links";

	    case WSAEREMOTE :
	        return "The object is remote";

	    case WSAENOTSOCK :
	        return "Socket operation on non-socket";

	    case WSAEADDRNOTAVAIL :
	        return "Can't assign requested address";

	    case WSAEADDRINUSE :
	        return "Address already in use";

	    case WSAEAFNOSUPPORT :
	        return "Address family not supported by protocol family";

	    case WSAESOCKTNOSUPPORT :
	        return "Socket type not supported";

	    case WSAEPROTONOSUPPORT :
	        return "Protocol not supported";

	    case WSAENOBUFS :
	        return "No buffer space is supported";

	    case WSAETIMEDOUT :
	        return "Connection timed out";

	    case WSAEISCONN :
	        return "Socket is already connected";

	    case WSAENOTCONN :
	        return "Socket is not connected";

	    case WSAENOPROTOOPT :
	        return "Bad protocol option";

	    case WSAECONNRESET :
	        return "Connection reset by peer";

	    case WSAECONNABORTED :
	        return "Software caused connection abort";

	    case WSAENETDOWN :
	        return "Network is down";

	    case WSAENETRESET :
	        return "Network was reset";

	    case WSAECONNREFUSED :
	        return "Connection refused";

	    case WSAEHOSTDOWN :
	        return "Host is down";

	    case WSAEHOSTUNREACH :
	        return "Host is unreachable";

	    case WSAEPROTOTYPE :
	        return "Protocol is wrong type for socket";

	    case WSAEOPNOTSUPP :
	        return "Operation not supported on socket";

	    case WSAENETUNREACH :
	        return "ICMP network unreachable";

	    case WSAETOOMANYREFS :
	        return "Too many references";

	    default :
	        return "Unknown";
    }
}