#include <stdafx.h>
#include "CService.h"

centre::centre(CENTREDLG p){
	parent = p;
	idlist = new std::list<int>;
	idlist-> clear();
	idToInfo = new std::map<int, info*>;
	idToInfo-> clear();
	usrToPsw = new std::map<CString, CString>;
	usrToPsw-> clear();
	introduce();
}
centre::~centre(){
	delete idlist;
	delete idToInfo;
	delete usrToPsw;
	detach();
}
int centre::BeginService(int port = 326){
	m_Net.SetHandler(this);
	return m_Net.Run(port);
}
void centre::EndService(){
	closeAll();
	m_Net.Stop();
}
void centre::Kick(int){
}
void centre::introduce(){
}
void centre::addpair(CString, CString){
}
void centre::detach(){
}
void centre::onBoard(int id){
	NETOBJ *tObj;
	int pt;
	std::list<int>::iterator i = idlist->begin();
	while(i != (idlist->end())){
		int j = *i; 
		if(j != id && (*idToInfo)[j] != NULL && ((*idToInfo)[j]-> login)){
			tObj = new NETOBJ;
			tObj-> m_header.m_type = ONBOARD;
			tObj-> m_header.m_length = 4 + 4 + 256 + (*idToInfo)[id]-> addr.GetLength() + 1;
			tObj-> m_ID = *i;
			pt = ((*idToInfo)[id])-> port;
			tObj-> m_pBuf = new char[tObj-> m_header.m_length];
			tObj-> m_pBuf[0] = ((char*)&id)[0];
			tObj-> m_pBuf[1] = ((char*)&id)[1];
			tObj-> m_pBuf[2] = ((char*)&id)[2];
			tObj-> m_pBuf[3] = ((char*)&id)[3];
			tObj-> m_pBuf[4] = ((char*)&pt)[0];
			tObj-> m_pBuf[5] = ((char*)&pt)[1];
			tObj-> m_pBuf[6] = ((char*)&pt)[2];
			tObj-> m_pBuf[7] = ((char*)&pt)[3];
			strcpy(&(tObj-> m_pBuf[4 + 4]), (*idToInfo)[id]-> name);
			strcpy(&(tObj-> m_pBuf[4 + 4 + 256]), (*idToInfo)[id]-> addr.GetString());
			m_Net.Send(tObj);
		}
		i++;
	}
}
void centre::offBoard(int id){
	NETOBJ *tObj;
	std::list<int>::iterator i = idlist->begin();
	while(i != (idlist->end())){
		if(*i != id && (*idToInfo)[*i]-> login){
			tObj = new NETOBJ;
			m_Net.Send(new NETOBJ(*i, OFFBOARD, id));
		}
		i++;
	}
}
void centre::closeAll(){ 
	std::list<int>::iterator i = idlist->begin();
	while(i != idlist->end()){
		int j = *i;
		delete (*idToInfo)[j];
		m_Net.Send(new NETOBJ(j, CENQUIT));
		m_Net.DisConnect(j);
		i++;
	}
	idlist-> clear();
	idToInfo-> clear();
}
int centre::OnAccept(int id){
	idlist-> push_back(id);
	(*idToInfo)[id] = new info;
	m_Net.GetAddr(id, &(*idToInfo)[id]-> addr, &(*idToInfo)[id]-> port);
	parent-> sAccept(id, (*idToInfo)[id]-> addr, (*idToInfo)[id]-> port);
	return 0;
}
void centre::OnRecv(NETOBJ *obj){
	if((*idToInfo)[obj-> m_ID] == NULL){
		return;
	}
	if(obj-> m_header.m_type == LOGIN){	// Reg Integrated
		char namec[256];
		char passc[32];
		strcpy(namec, obj-> m_pBuf);
		strcpy(passc, &(obj-> m_pBuf[256]));
		CString name(namec);
		CString pass(passc);
		if((*usrToPsw)[name] != CString()  && ((*usrToPsw)[name]) != pass){
			m_Net.Send(new NETOBJ(obj-> m_ID, REJECT));	// incorrect password
			parent-> sReject(name);
		}
		else{	// login
			if((*usrToPsw)[name] == CString()){	// register
				(*usrToPsw)[name] = pass;
				addpair(name, pass);
				m_Net.Send(new NETOBJ(obj-> m_ID, REGDONE));
				parent-> sReg(name);
			}
			(*idToInfo)[obj-> m_ID]-> login = true;
			strcpy((*idToInfo)[obj-> m_ID]-> name, (char *)name.GetString());
			onBoard(obj-> m_ID);
			m_Net.Send(new NETOBJ(obj-> m_ID, LOGINED));
			parent-> sLogin(name, obj-> m_ID, (*idToInfo)[obj-> m_ID]-> addr, (*idToInfo)[obj-> m_ID]-> port);
		}
	}
	else if(obj-> m_header.m_type == QUERYALL){
		int temp;
		int pt;
		NETOBJ* tObj;
		std::list<int>::iterator i = idlist->begin();
		while(i != idlist-> end()){
			temp = *i;
			if((*idToInfo)[temp]-> login == true){
				tObj = new NETOBJ;
				tObj-> m_header.m_type = QUERYREP;
				tObj-> m_header.m_length = 4 + 4 + 256 + (*idToInfo)[temp]-> addr.GetLength() + 1;
				tObj-> m_ID = obj-> m_ID;
				pt = (*idToInfo)[temp]-> port;
				tObj-> m_pBuf = new char[tObj-> m_header.m_length];
				tObj-> m_pBuf[0] = ((char*)&temp)[0];
				tObj-> m_pBuf[1] = ((char*)&temp)[1];
				tObj-> m_pBuf[2] = ((char*)&temp)[2];
				tObj-> m_pBuf[3] = ((char*)&temp)[3];
				tObj-> m_pBuf[4] = ((char*)&pt)[0];
				tObj-> m_pBuf[5] = ((char*)&pt)[1];
				tObj-> m_pBuf[6] = ((char*)&pt)[2];
				tObj-> m_pBuf[7] = ((char*)&pt)[3];
				strcpy(&(tObj-> m_pBuf[4 + 4]), (*idToInfo)[temp]-> name);
				strcpy(&(tObj-> m_pBuf[4 + 4 + 256]), (*idToInfo)[temp]-> addr.GetString());
				m_Net.Send(tObj);
			}
			i++;
		}
	}
	else if(obj-> m_header.m_type == QUERYID){
		(*idToInfo)[obj-> m_ID]-> port = *((int *)obj-> m_pBuf);
		m_Net.Send(new NETOBJ(obj-> m_ID, IDREPLY, obj-> m_ID));
		parent-> sSetPort(obj-> m_ID, (*idToInfo)[obj-> m_ID]-> port);
	}
	else if(obj-> m_header.m_type == QUIT){
		OnClose(obj-> m_ID);
	}
	else if(obj-> m_header.m_type == SHUTDOWN){
		OnBeClose(obj-> m_ID);
	}
}
void centre::OnClose(int id){
	if((*idToInfo)[id] == NULL) return;
	delete (*idToInfo)[id];
	(*idToInfo)[id] = NULL;
	idToInfo-> erase(id);
	idlist-> remove(id);
	offBoard(id);
	parent-> sClose(id);
}
void centre::OnBeClose(int id){
	if((*idToInfo)[id] == NULL) return;
	(*idToInfo)[id] = NULL;
	idToInfo-> erase(id);
	idlist-> remove(id);
	offBoard(id);
	parent-> sClose(id);
}

user::user(USERDLG p){
	parent = p;
	served = false;
	myID = -1;
	rooms.clear();
	conlist.clear();
	idlist.clear();
	idToCon.clear();
	conToId.clear();
	idToName.clear();
	conToIp.clear();
	idToIp.clear();
	idToPort.clear();
}
int user::BeginService(int port = 525){
	m_Net.SetHandler(this);
	myPt = port;
	return m_Net.Run(port);
};
void user::EndService(){
	if(served){
		m_Net.Send(new NETOBJ(cenCon, SHUTDOWN));
		m_Net.DisConnect(cenCon);
	}
	served = false;
	closeAll();
	parent-> OnTerminateServe();
	m_Net.Stop();
};
room* user::newRoom(ROOMDLG d){
	rooms.push_front(new room(this, d));
	return rooms.front();
}
int user::BeginRoom(room *r, int port){
	return(r-> BeginService(port));
}
int user::OnAccept(int con){
	CString *addr = new CString;
	int port;
	conlist.push_front(con);
	m_Net.GetAddr(con, addr, &port);
	conToIp[con] = addr;
	return 0;
};
void user::OnRecv(NETOBJ *obj){
//	if(conToIp[obj-> m_ID] == NULL){
//		return;
//	}
	if(obj-> m_header.m_type == REJECT){
		parent-> OnLoginReject();
	}
	else if(obj-> m_header.m_type == REGDONE){
		parent-> OnRegDone();
	}
	else if(obj-> m_header.m_type == LOGINED){
		parent-> OnLogin();
	}
	else if(obj-> m_header.m_type == IDREPLY){
		myID = *(int *)obj-> m_pBuf;
		if(idToCon[myID] != NULL){
			delete idToCon[myID];
			idToCon[myID] = NULL;
		}
		parent-> OnUpdateID(myID);
		QueryAll();
	}
	else if(obj-> m_header.m_type == QUERYREP
		|| obj-> m_header.m_type == ONBOARD){
		int id;
		int port;
		char *name = new char[256]; 
		CString *ip;
		id = *((int *)obj-> m_pBuf);
		port = *((int *)&obj-> m_pBuf[4]);
		strcpy(name, &(obj-> m_pBuf[8]));
		ip = new CString(&(obj-> m_pBuf[264]));
		idlist.push_front(id);
		idToName[id] = name;
		idToCon[id] = NULL;
		idToIp[id] = ip;
		idToPort[id] = port;
		parent-> OnUserOn(id, name, ip, port);
	}
	else if(obj-> m_header.m_type == OFFBOARD){
		int id = *((int *)obj-> m_pBuf);
//		delete idToName[id];
//		delete idToIp[id];
		disconnect(id);
//		idlist.remove(id);
//		idToName.erase(id);
//		idToIp.erase(id);
		parent-> OnUserOff(id);
	}
	else if(obj-> m_header.m_type == SETID){
		int id = *((int *)obj-> m_pBuf);
		int con = obj-> m_ID;
		if(strcmp(*conToIp[con], *idToIp[id])){
			conToId[con] = id;
			idToCon[id] = new int(con);
		}
		else{
			m_Net.Send(new NETOBJ(con, IDREJECT));
		}
	}
	else if(obj-> m_header.m_type == IDREJECT){
		parent-> OnIDProb();
	}
	else if(obj-> m_header.m_type == MESG){
		int id = conToId[obj-> m_ID];
		CString *s = new CString(obj-> m_pBuf);
		parent-> OnMsgIn(id, s);
	}
	else if(obj-> m_header.m_type == CENQUIT){
		served = false;
		parent-> OnCenQuit();
	}
	else if(obj-> m_header.m_type == CONNECT){
		int id = *((int *)obj-> m_pBuf);
		int con = obj-> m_ID;
		idToCon[id] = new int(con);
		conToId[con] = id;
	}
	else if(obj-> m_header.m_type == DISCON){
		int con = obj-> m_ID;
		int id = conToId[con];
		delete idToCon[id];
		idToCon[id] = NULL;
		conToId.erase(con);
	}
};
void user::OnClose(int con){
	if(conToIp[con] == NULL) return;
	if(cenCon == con){
		served = false;
		parent-> OnTerminateServe();
	}
	else parent-> OnUserOff(conToId[con]);
	int id = conToId[con];
	delete conToIp[con];
	conToIp[con] = NULL;
	conToIp.erase(con);
	conToId.erase(con);
	idToCon.erase(id);
	conlist.remove(con);
};
int user::RequestService(CString ip, int port = 326){
	if(served == true) TerminateService();
	cenCon = (m_Net.Connect(new CString(ip), port));
	if(cenCon != -1){
		served = true;
	}
	return (cenCon);
}
void user::TerminateService(){
	if(served){
		m_Net.Send(new NETOBJ(cenCon, QUIT));
		m_Net.DisConnect(cenCon);
	}
	served = false;
	parent-> OnTerminateServe();
}
void user::Login(char* name, char* pass){
	if(served){
		QueryID();
		NETOBJ *tObj = new NETOBJ;
		tObj-> m_header.m_type = LOGIN;
		tObj-> m_header.m_length = 256 + 32;
		tObj-> m_ID = cenCon;
		tObj-> m_pBuf = new char[256 + 32];
		strcpy(tObj-> m_pBuf, name);
		strcpy(&(tObj-> m_pBuf[256]), pass);
		m_Net.Send(tObj);
	}
}
void user::QueryAll(){
	m_Net.Send(new NETOBJ(cenCon, QUERYALL));
}
void user::QueryID(){
	m_Net.Send(new NETOBJ(cenCon, QUERYID, myPt));
}
int user::connect(int id, int port = 525, bool recon = true){
//	if(myID == -1) return -2;
	if(idToCon[id] != NULL){
		if(!recon) return *idToCon[id];
		else{
			conlist.remove(*idToCon[id]);
			m_Net.DisConnect(*idToCon[id]);
		}
	}
	int temp = m_Net.Connect(idToIp[id], port);
	conlist.push_front(temp);
	idToCon[id] = new int(temp);
	m_Net.Send(new NETOBJ(temp, CONNECT, myID));
	conToIp[temp] = new CString(*idToIp[id]);	
	conToId[temp] = id;
	parent-> OnConnect(id, idToIp[id], port);
	return temp;
}
void user::disconnect(int id){
	int *con = idToCon[id];
	if(con != NULL){
		m_Net.Send(new NETOBJ(int(*con), DISCON));
		m_Net.DisConnect(*con);
		delete conToIp[*con];
		conToId.erase(*con);
		conToIp.erase(*con);
		conlist.remove(*con);
		delete idToCon[id];
		idToCon[id] = NULL;
	}
}
void user::message(int id, CString *s){
	if(idToCon[id] == NULL) connect(id, idToPort[id]);
	int con = *idToCon[id];
	if(con != -1) m_Net.Send(new NETOBJ(con, MESG, s));
}
void user::identity(int id){
	m_Net.Send(new NETOBJ(*idToCon[id], SETID, myID));
}
void user::closeAll(){
	std::list<int>::iterator i = conlist.begin();
	while(i != conlist.end()){
		int con = *i;
		int id = conToId[con];
		m_Net.Send(new NETOBJ(int(con), DISCON));
		m_Net.DisConnect(con);
		delete conToIp[con];
		conToId.erase(con);
		conToIp.erase(con);
		delete idToCon[id];
		idToCon[id] = NULL;
		i++;
	}
	conlist.clear();
}