//
// C++ Implementation: server
//
// Description: 
//
//
// Author: Konrad Rosenbaum <konrad@silmor.de>, (C) 2006
//
// Copyright: See COPYING file that comes with this distribution
//
//

#include "server.h"

#include <QTcpServer>
#include <QTcpSocket>
#include <QMenuBar>
#include <QMenu>
#include <QLCDNumber>
#include <QMessageBox>

CServer::CServer(int prt,QWidget*p)
	:QMainWindow(p)
{
	setWindowTitle("Server on port "+QString::number(prt));
	//menu
	QMenu*m=menuBar()->addMenu("&Server");
	m->addAction("&Close Server",this,SLOT(close()));
	setAttribute(Qt::WA_DeleteOnClose);
	//lcd
	setCentralWidget(disp=new QLCDNumber(3));
	//create server socket
	serv=new QTcpServer(this);
	if(!serv->listen(QHostAddress::Any,prt)){
		//listen was unsuccessful, warn the user
		QMessageBox::warning(this,"Server Socket Error","Cannot establish Server Socket: "+serv->errorString());
		//queue a delete (must be queued because the parent tries to call show())
		deleteLater();
	}
	//make sure we are informed of new connections
	connect(serv,SIGNAL(newConnection()),this,SLOT(newcon()));
	nextid=0;
	//make sure we get signals from the client sockets
	connect(&disconmap,SIGNAL(mapped(int)),this,SLOT(discon(int)));
	connect(&readmap,SIGNAL(mapped(int)),this,SLOT(doread(int)));
}
CServer::~CServer()
{
	//close server
	serv->close();
	//close all clients
	QList<int>k=clients.keys();
	for(int i=0;i<k.size();i++){
		clients[k[i]].sock->close();
		clients[k[i]].sock->deleteLater();
	}
}

void CServer::discon(int cn)
{
	//sanity check
	if(!clients.contains(cn))return;
	//disconnect client from my slots
	Client c=clients[cn];
	c.sock->disconnect();
	//remove it from list
	clients.remove(cn);
	//delete it
	c.sock->deleteLater();
	//update lcd
	disp->display(clients.size());
}

void CServer::doread(int cn)
{
	//sanity check
	if(!clients.contains(cn))return;
	//read data
	clients[cn].buf+=QString::fromUtf8(clients[cn].sock->readAll());
	//process data
	while(processClient(cn));
}

void CServer::newcon()
{
	//check for all pending new connections
	while(serv->hasPendingConnections()){
		//create client structure
		Client c;
		//get next connection
		c.sock=serv->nextPendingConnection();
		//get a default nick
		c.nick=c.sock->peerAddress().toString()+":"+QString::number(c.sock->peerPort());
		//register it
		nextid++;
		clients.insert(nextid,c);
		disconmap.setMapping(c.sock,nextid);
		readmap.setMapping(c.sock,nextid);
		connect(c.sock,SIGNAL(disconnected()),&disconmap,SLOT(map()));
		connect(c.sock,SIGNAL(error(QAbstractSocket::SocketError)),&disconmap,SLOT(map()));
		connect(c.sock,SIGNAL(readyRead()),&readmap,SLOT(map()));
	}
	//update lcd
	disp->display(clients.size());
}

bool CServer::processClient(int cn)
{
	//is there a complete line?
	int pos=clients[cn].buf.indexOf('\n');
	if(pos<0)return false;
	//cut the line out
	QString ln=clients[cn].buf.left(pos).trimmed();
	clients[cn].buf=clients[cn].buf.mid(pos+1);
	//is this an empty line? yes: continue with next line
	if(ln.length()==0)return true;
	//check whether this is a command
	if(ln[0]=='/'){
		//process the command
		processCommands(ln.mid(1),cn);
	}else{
		//repeat the line to all
		sendData(clients[cn].nick+": "+ln);
	}
	//continue with next line
	return true;
}

void CServer::processCommands(QString ln,int cn)
{
	//split out parts
	QStringList cmd=ln.split(' ',QString::SkipEmptyParts);
	if(cmd.size()==0)return;
	//check command name
	if(cmd[0]=="nick"){
		if(cmd.size()>1){
			clients[cn].nick=cmd[1];
		}
		clients[cn].sock->write(QString("Your nick is "+clients[cn].nick+"\n").toUtf8());
	}else
	if(cmd[0]=="who"){
		QString ret="Currently logged in:\n";
		QList<int>k=clients.keys();
		for(int i=0;i<k.size();i++){
			Client c=clients[k[i]];
			ret+=" "+c.nick+" from "+
				c.sock->peerAddress().toString()+":"+
				QString::number(c.sock->peerPort())+"\n";
		}
		clients[cn].sock->write(ret.toUtf8());
	}else{
		clients[cn].sock->write(QString("Unknown command. Must be one of: nick, who.\n").toUtf8());
	}
}

void CServer::sendData(QString str)
{
	//make sure it is a line and convert to Unicode
	QByteArray dat=QString(str.trimmed()+"\n").toUtf8();
	//go through all clients
	QList<int>k=clients.keys();
	for(int i=0;i<k.size();i++){
		//send data
		clients[k[i]].sock->write(dat);
	}
}
