/*!
 * \ file XSock.cpp bal bla
 *
 */

#include "xsock.h"

/*!
 * \def VERBOSE Active ou pas le mode verbeux
 */
#define VERBOSE 0

// constantes utilisées dans XSock.cpp et dans XSockUDP.cpp
#define NBPORTSPUB      64512
#define PNUMPORTPUB     1025
#define GETPORTPUB()    PNUMPORTPUB+(rand()%NBPORTSPUB)

using namespace std;
using namespace XSockExcept;

namespace XSock {

	XSock::XSock (xsock_role role,protocol proto){
		/* initialise la structure d'addresse  */
		/* on commence par vider la structure pour éviter les déchets...  */
		memset(&this->_server, 0, sizeof(this->_server));
		memset(&this->_client, 0, sizeof(this->_client));
		this->_client.sin_family=AF_INET;
		this->_server.sin_family=AF_INET;

                /* on initialise le systèmes de nombres aléatoires */
                srand(time(NULL));
		/* on initialise avec des addresses locales au cas où... */
		this->_client.sin_addr.s_addr=htonl(INADDR_LOOPBACK);
		this->_server.sin_addr.s_addr=htonl(INADDR_LOOPBACK);

		/* et les ports au hasard */
		this->_client.sin_port=htons(0);
		this->_server.sin_port=htons(0);

		/* calcule la taille de la table des descripteurs */
		this->_sockSetDSize = getdtablesize();

		this->_role=role;
		this->_proto=proto;
		
		this->_client_length = sizeof(this->_client);
		this->_server_length = sizeof(this->_server);
		
		
	}

	/*! 
	 * Indique le port à utiliser sur le serveur.
	 */
	void XSock::port(u_short port){
		switch(this->_role){
			case CLIENT:
				this->_server.sin_port=htons(port);
				break;
			case SERVER:
				this->_server.sin_port=htons(port);
				break;
			default:
				throw eUnknownXSockRole();
				break;
		}
	}


	/*!
	 *  Fixe la taille de la queue d'attente de connexion
	 */

	void XSock::backlog(int size){
		this->_backlog=size;
	}


	/*!
	 * Indique l'addresse IP du serveur.
	 */

	void XSock::ip(int ip){
		switch (this->_role){
			case CLIENT: 
				this->_server.sin_addr.s_addr=htonl(ip);
				break;
			case SERVER:				  
				this->_server.sin_addr.s_addr=htonl(ip);
				break;
			default: 
				throw eUnknownXSockRole();
				break;
		}
	}

	void XSock::dns(string dns){
		struct hostent *he;
		if ((he = gethostbyname(dns.c_str())) == NULL) {
			throw eUnableToResolveName();
		}

		memcpy(&(this->_server.sin_addr.s_addr), he->h_addr_list[0], he->h_length);

		switch (this->_role){
			case CLIENT: 
				break;
			case SERVER:				  
				break;
			default: 
				throw eUnknownXSockRole();
				break;
		}

	}
	/*! 
	 * Connecte XSock au réseau.
	 */

	void XSock::dump_info(){
		if (VERBOSE>=1){
			printf("XSock server : %d\n",this->_server.sin_addr.s_addr);
			printf("XSock client : %d\n",this->_client.sin_addr.s_addr);
			printf("XSock server port : %d\n",this->_server.sin_port);
			printf("XSock client port : %d\n",this->_client.sin_port);
		}
	}

	char * XSock::getClientAddress(){
		return inet_ntoa(this->_client.sin_addr);
	}
	
	char * XSock::getServerAddress(){
		return inet_ntoa(this->_server.sin_addr);
	}


	void XSock::launch(){
		this->dump_info();	
		switch (this->_role){
			case CLIENT: 
				switch (this->_proto){
					case TCP:
						this->launch_tcp_client();
						break;
					case UDP:
						this->launch_udp_client();
						break;
					case UDP_RELIABLE:
						this->launch_udp_reliable_client();
						break;
					default: 
						throw eUnknownProtocol();
						break;
				}
				break;
			case SERVER: 
				switch (this->_proto){
					case TCP:
						this->launch_tcp_server();
						break;
					case UDP:
						this->launch_udp_server();
						break;
					case UDP_RELIABLE:
						this->launch_udp_reliable_server();
						break;
					default: 
						throw eUnknownProtocol();
						break;
				}
				break;
			default:
				throw eUnknownXSockRole();
				break;
		}
	}

	/*! 
	 * Demande a XSock de recevoir des informations.
	 * \param bufferData buffer de données.
	 * \param size taille du buffer.
	 */

	ssize_t XSock::recv(void *bufferData, ssize_t size){
		switch(this->_proto){
			case TCP:
				return this->recv_tcp(bufferData, size);
				break;
			case UDP:
				return this->recv_udp(bufferData, size);
				break;
			case UDP_RELIABLE:
				return this->recv_udp_reliable(bufferData, size);
				break;
			default: 
				throw eUnknownProtocol();
				break;
		}
	}

	ssize_t XSock::send(const void *bufferData, ssize_t size){
		switch(this->_proto){
			case TCP:
				return this->send_tcp(bufferData, size);
				break;
			case UDP:
				return this->send_udp(bufferData, size);
				break;
			case UDP_RELIABLE:
				return this->send_udp_reliable(bufferData, size);
				break;
			default: 
				throw eUnknownProtocol();
				break;
		}
	}


	XSock XSock::accept(){
		XSock result(CLIENT,this->_proto);
		switch (this->_role){
			case CLIENT: 
				// TODO: râler, car le client ne recoit pas de 
				// connexions...
				break;
			case SERVER: 
				switch (this->_proto){
					case TCP:
						result=this->accept_tcp();
						break;
					case UDP:
						result=this->accept_udp();
						break;
					case UDP_RELIABLE:
						result=this->accept_udp_reliable();
						break;
					default: 
						throw eUnknownProtocol();
						break;
				}
				break;
			default:
				throw eUnknownXSockRole();
				break;
		}
		return result;
	}

	void XSock::close(){
				switch (this->_proto){
					case TCP:
						this->close_tcp();
						break;
					case UDP:
						this->close_udp();
						break;
					case UDP_RELIABLE:
						this->close_udp_reliable();
						break;
					default: 
						throw eUnknownProtocol();
						break;

				}
		//TODO: a faire...
	}
} // end namespace