#include <stdafx.h>
#include "CNet.h"
#include "blocksock.h"


//following are threads


UINT RecvThProc(LPVOID pParam){
	RECVTHREADPARAM* pRecvInfo = (RECVTHREADPARAM*) pParam;
	WSAEVENT EventArray[1];
	EventArray[0] = pRecvInfo->hEvent;
	CBlockingSocket bSocket(pRecvInfo->hSocket);
	CNet* myNet = (CNet*)(pRecvInfo->pNet);
	int myID = pRecvInfo->ID;
	delete pRecvInfo;
	WSANETWORKEVENTS NetworkEvents;
	WSAResetEvent(EventArray[0]);
	CSingleLock Slock(&myNet->m_csRecvCallback);
	
	while(1){
		WSAWaitForMultipleEvents(1, EventArray, FALSE, WSA_INFINITE, FALSE);
		WSAEnumNetworkEvents(bSocket.GetSocket(), EventArray[1], &NetworkEvents);
		
			WSAResetEvent(EventArray[0]);
			char header_buf[sizeof(NETOBJ_HEADER)];

			if(bSocket.Read(header_buf, sizeof(NETOBJ_HEADER)) == SOCKET_ERROR){
				myNet->ErrorDisConnect(myID);
				return 0;
			}

			NETOBJ_HEADER* header = (NETOBJ_HEADER*)header_buf;

			if(header->m_type == -1){
				myNet->ErrorDisConnect(myID);
				return 0;
			}

			if(header->m_type == -2)
				continue;
		
			char* pBuf = new char[header->m_length];

			if(bSocket.Read(pBuf, header->m_length) == SOCKET_ERROR){
				myNet->ErrorDisConnect(myID);
				return 0;
			}
		
			NETOBJ* pnetobj = new NETOBJ;
			pnetobj->m_header = *header;
			pnetobj->m_ID = myID;
			pnetobj->m_pBuf = pBuf;

			Slock.Lock();
			myNet->m_pHandler->OnRecv(pnetobj);
			Slock.Unlock();

			delete pnetobj;
		
		
	}
	return 0;
}

UINT RefreshThProc(LPVOID pParam){
	CNet* myNet = (CNet*) pParam;
	while(1){
		myNet->Refresh();
		Sleep(3000);
	}
	return 0;
}

UINT LisThProc(LPVOID pNet){
	CNet *myNet  = (CNet *) pNet;
	CSingleLock Slock(&myNet->m_csSocketMap);
	while(1){
		myNet->g_listen.Listen();

		CBlockingSocket recvedSocket;
		CSockAddr recvedAddr;
		myNet->g_listen.Accept(recvedSocket, recvedAddr);


		SOCKET_INFO socket_info;
		
		//建立event關聯
		socket_info.m_hSocket = recvedSocket.GetSocket();
		socket_info.m_hEvent = WSACreateEvent();
		WSAEventSelect(socket_info.m_hSocket, socket_info.m_hEvent, FD_READ);

		int id = myNet->GetAnFreeID();

		//填寫傳給RecvThread的參數
		RECVTHREADPARAM *pParam = new RECVTHREADPARAM;
		pParam->hSocket = socket_info.m_hSocket;
		pParam->hEvent = socket_info.m_hEvent;
		pParam->ID = id;
		pParam->pNet = myNet;

		Slock.Lock();
        myNet->m_SocketMap[id] = socket_info;
		myNet->m_pHandler->OnAccept(id);	
		myNet->m_SocketMap[id].m_RecvThread =  AfxBeginThread(RecvThProc, (LPVOID)pParam); //begin recv thread
		Slock.Unlock();
		
		
		
	}
	return 0;
}



//following are the implement of CNet

CNet::CNet(){
	if(InitWinSock() == -1)
		::MessageBox(0, "Can't initialize winsock! - CNet::InitWinSock()", 0, 0);


	m_Start = false;

	m_tListen = NULL;
	m_IDcounter = 0;
}

int CNet::InitWinSock(){
	WSADATA wsaData;
	int iResult = WSAStartup( MAKEWORD(2,2), &wsaData );
	if(iResult != NO_ERROR)
		return -1;
	return 0;
}

int CNet::Connect(CString* pAddr, int port){

	CBlockingSocket conn_socket;
	conn_socket.Create();
	CSockAddr sock_addr((LPCTSTR)*pAddr, port);

	if( conn_socket.Connect(sock_addr) == SOCKET_ERROR)
		return -1;

	SOCKET_INFO socket_info;
	ZeroMemory(&socket_info, sizeof(SOCKET_INFO));

	//建立event關聯

	socket_info.m_hSocket = conn_socket.GetSocket();
	socket_info.m_hEvent = WSACreateEvent();
	WSAEventSelect(socket_info.m_hSocket, socket_info.m_hEvent, FD_READ|FD_CLOSE);

	int id = GetAnFreeID();

	//fill the param, will be deleted by RecvThread
	RECVTHREADPARAM *pParam = new RECVTHREADPARAM;
	pParam->hSocket = socket_info.m_hSocket;
	pParam->hEvent = socket_info.m_hEvent;
	pParam->ID = id;
	pParam->pNet = this;
	
	CSingleLock Slock(&m_csSocketMap);
	Slock.Lock();
	m_SocketMap[id] = socket_info;
	m_SocketMap[id].m_RecvThread =  AfxBeginThread(RecvThProc, (LPVOID)pParam); //begin recv thread
	Slock.Unlock();
	/////////////////adding Mutex///////////////////////
	
	return id;
	//need to handle error, hmmm... I'm thinking;
}


int CNet::Send(NETOBJ* pobj){
	CSingleLock Slock(&m_csSocketMap);

	if(m_SocketMap.count(pobj->m_ID) == 1){

		Slock.Lock();
		CBlockingSocket sendSocket(m_SocketMap[pobj->m_ID].m_hSocket);
		Slock.Unlock();
		//sending header
		if(sendSocket.Send((char*)&(pobj->m_header), sizeof(pobj->m_header)) == SOCKET_ERROR){
			ErrorDisConnect(pobj->m_ID);
			delete pobj;
			return -1;
		}

		//sending buf
		if(sendSocket.Send(pobj->m_pBuf, pobj->m_header.m_length) == SOCKET_ERROR){
			ErrorDisConnect(pobj->m_ID);
			delete pobj;
			return -1;
		}

		delete pobj;
		return 0;
	}
	else
		return -1;
}

int CNet::Send(NETOBJ & netobj){
	if(m_SocketMap.count(netobj.m_ID) == 1){
		CSingleLock Slock(&m_csSocketMap);
		Slock.Lock();
		CBlockingSocket sendSocket(m_SocketMap[netobj.m_ID].m_hSocket);
		Slock.Unlock();
		//sending header
		if(sendSocket.Send((char*)&netobj.m_header, sizeof(netobj.m_header)) == SOCKET_ERROR){
			ErrorDisConnect(netobj.m_ID);
			return -1;
		}

		//sending buf
		if(sendSocket.Send(netobj.m_pBuf, netobj.m_header.m_length) == SOCKET_ERROR){
			ErrorDisConnect(netobj.m_ID);
			return -1;
		}

		return 0;
	}
	else
		return -1;
}

int CNet::GetAnFreeID(){
	//if(m_SocketMap.size() == MAX_CONNECTION){
	//}
	CSingleLock Slock(&m_csSocketMap);
	m_IDcounter = (m_IDcounter + 1)%MAX_CONNECTION;
	Slock.Lock();
	while (m_SocketMap.count(m_IDcounter)){
		m_IDcounter = (m_IDcounter + 1)%MAX_CONNECTION;
	}
	Slock.Unlock();
	return m_IDcounter;
}

int CNet::Run(int port){
	if(m_Start)
		return -1;
	else{
		CSockAddr sa(INADDR_ANY, port);
		if(g_listen.Create() == -1)
			return -1;
		g_listen.Bind(sa);

		if(m_pHandler == NULL)
			return -1;
		m_Start = true;
		m_tRefresh = AfxBeginThread(RefreshThProc, (LPVOID)this);
		m_tListen = AfxBeginThread(LisThProc, (LPVOID)this);
		

		return 0;
	}
}

int CNet::DisConnect(int ID){

	//delete ID from map
	CSingleLock Slock(&m_csSocketMap);
	Slock.Lock();
	SOCKETMAP::iterator pos = m_SocketMap.find(ID);
	SOCKET_INFO socket_info = pos->second;
	m_SocketMap.erase(pos);
	Slock.Unlock();

	TerminateThread(socket_info.m_RecvThread->m_hThread, 0);
	WSACloseEvent(socket_info.m_hEvent);

	static NETOBJ_HEADER header;
	header.m_type = -1;
	header.m_length = 0;

	CBlockingSocket bSocket(socket_info.m_hSocket);
	bSocket.Send((char*) &header, sizeof(header));
	closesocket(socket_info.m_hSocket);
	return 0;
}

int CNet::ErrorDisConnect(int id){
	CSingleLock Slock(&m_csSocketMap);
	
	//get SOCKET_INFO
	Slock.Lock();
	if(m_SocketMap.count(id) <= 0)
		return -1;
	SOCKETMAP::iterator pos = m_SocketMap.find(id);
	SOCKET_INFO socket_info = pos->second;
	m_SocketMap.erase(pos);
	Slock.Unlock();

	TerminateThread(socket_info.m_RecvThread->m_hThread, 0);
	m_pHandler->OnClose(id);
	WSACloseEvent(socket_info.m_hEvent);
	closesocket(socket_info.m_hSocket);
	
	return 0;
}
	

void CNet::SetHandler(CNetHandler *phandler){
	m_pHandler = phandler;
}

int CNet::GetAddr(int id, CString* pAddr, int * pPort){
	CSingleLock Slock(&m_csSocketMap);

	//get SOCKET_INFO
	Slock.Lock();
	SOCKET_INFO socket_info = m_SocketMap[id];
	Slock.Unlock();

	CBlockingSocket bSocket(socket_info.m_hSocket);
	CSockAddr sockaddr;
	if( SOCKET_ERROR == bSocket.GetPeerAddr(sockaddr)){
		ErrorDisConnect(id);
		return -1;
	}
	*pAddr = sockaddr.DottedDecimal();
	*pPort  = sockaddr.Port();
	return 0;
}



void CNet::Stop(){
	if(m_Start){

		TerminateThread(m_tListen->m_hThread, 0);
		TerminateThread(m_tRefresh->m_hThread, 0);
		m_Start = false;

		g_listen.Close();

		static NETOBJ_HEADER aheader;
		aheader.m_type = -1;
		aheader.m_length = 0;

		SOCKETMAP::iterator pos;
		for(pos = m_SocketMap.begin(); pos != m_SocketMap.end(); ++pos){
			SOCKET_INFO socket_info = pos->second;
			TerminateThread(socket_info.m_RecvThread->m_hThread, 0);
			WSACloseEvent(socket_info.m_hEvent);

			CBlockingSocket bSocket(socket_info.m_hSocket);
			bSocket.Write((char*) &aheader, sizeof(aheader));
			closesocket(socket_info.m_hSocket);
		}

		m_SocketMap.clear();
	}

}

void CNet::Refresh(){
	CSingleLock Slock(&m_csSocketMap);

	static NETOBJ_HEADER header;
	header.m_length = 0;
	header.m_type = -2;

	Slock.Lock();
	SOCKETMAP::iterator pos;
	for(pos = m_SocketMap.begin(); pos != m_SocketMap.end(); ++pos){
		CBlockingSocket bSocket(pos->second.m_hSocket);
		if(bSocket.Write((char*)&header, sizeof(header)) == SOCKET_ERROR){
			ErrorDisConnect(pos->first);
		}
	}
	Slock.Unlock();
}

CNet::~CNet(){
	Stop();
}