/*
....[@@@..[@@@..............[@.................. MUD++ is a written from
....[@..[@..[@..[@..[@..[@@@@@....[@......[@.... scratch multi-user swords and
....[@..[@..[@..[@..[@..[@..[@..[@@@@@..[@@@@@.. sorcery game written in C++.
....[@......[@..[@..[@..[@..[@....[@......[@.... This server is an ongoing
....[@......[@..[@@@@@..[@@@@@.................. development project.  All 
................................................ contributions are welcome. 
....Copyright(C).1995.Melvin.Smith.............. Enjoy. 
------------------------------------------------------------------------------
Melvin Smith (aka Fusion)         msmith@falcon.mercer.peachnet.edu 
MUD++ development mailing list    mudpp-list@spice.com
------------------------------------------------------------------------------
server.cc
*/

#include "server.h"

void sig_handler( int )
{
	// Generate core
	raise( SIGSEGV );
}


Server::~Server()
{
	master->close();
}


Socket * Server::getSock()
{
	return master;
}

// This is for booting "into" a previous servers process
// space and inheriting the open master descriptor. Skip
// all the master struct creation and just assign the
// descriptor to sock and clean up a few other things.
// This really needs some serious error handling. Also need
// to save the sock struct info in the reboot.tab file
// and recreate it from that.
// --Fusion

int Server::boot( int oldport, int olddesc )
{
	signal( SIGPIPE, SIG_IGN );
	signal( SIGBUS, sig_handler );

	master = new Socket( "", oldport, olddesc ); 
	
	printf( "SERVER: Rebooted succesfully - ready for connections.\n" );
	printf( "SERVER: Port [%d]\n", master->getPort() );
	
	topdesc = olddesc;
	return 0;
}


int Server::boot( int tport )
{
	signal( SIGPIPE, SIG_IGN );
	signal( SIGBUS, sig_handler );

	master = new Socket( tport );

	if( master->error() )
	{
		perror( "SERVER: Socket::boot()" );
		exit(0);
	}

	master->reuseAddr();
	master->noDelay();

	if( master->listen() )
	{
		perror( "SERVER: Socket::listen()" );
		exit(0);
	}

	masterdesc = topdesc = master->getDesc();
	masterport = master->getPort();

	printf( "SERVER: Booted succesfully - ready for connections.\n" );
	printf( "SERVER: Port [%d]\n", masterport );

	return 0;
}



int Server::poll()
{
	memcpy( &R_SET, &MASTER_SET, bitwidth );
	memcpy( &W_SET, &MASTER_SET, bitwidth );
	memcpy( &E_SET, &MASTER_SET, bitwidth );

	FD_SET( masterdesc, &R_SET );

//	if( select( topdesc+1, &R_SET, &W_SET, &E_SET, &null_time) == -1 )
	if( select( topdesc+1, &R_SET, &W_SET, &E_SET, 0 ) == -1 )
	{
		printf("SERVER: select() error in Poll() - exiting.\n");
		exit( errno );
	}

	return FD_ISSET( masterdesc, &R_SET );
}

// I high resolution sleep function
// Best to inline this to remove the overhead of the function call

void Server::sleep( long millisecs )
{
	static struct timeval sleep_time;

	sleep_time.tv_sec  = 0;
	sleep_time.tv_usec = millisecs; 
	if( select( 0, NULL, NULL, NULL, &sleep_time ) == -1 )
	{
		printf("SERVER: Error select() in Sleep()\n");
		exit( errno );
	}
}


// Do we want the numerical ip or try to get the name from the
// nameserver? If you have a slow nameserver dont use this.
void Server::useNameServer()
{
	nameserver = 1;
}


int Server::newConnection()
{
	FD_ZERO( &R_SET );
	FD_SET( masterdesc, &R_SET );
	if( select( masterdesc + 1, &R_SET, 0, 0, &null_time ) == -1 )
	{
		printf("SERVER: select() error in NewConnection() - exiting.\n");
		exit( errno );
	}

	return FD_ISSET( masterdesc, &R_SET );
}



Socket * Server::accept()
{
	int desc;

	Socket *newSock = master->accept();

	if( !newSock )
	{
		if( errno == EAGAIN || errno == EWOULDBLOCK )
			return 0;

		perror( "Server::accept()" );
		exit(0);
		return 0;
	}

	newSock->noDelay();
	newSock->getPeerName();
	newSock->resolveIP();
	desc = newSock->getDesc();

	if( desc > topdesc )
		topdesc = desc;

	FD_SET( desc, &MASTER_SET );
	descripts++;

	return newSock;
}



void Server::addSock( Socket * sock )
{
	if( FD_ISSET( sock->getDesc(), &MASTER_SET ) )
	{
		printf( "SERVER: Error. Attempt to add existing descriptor.\n" );
		exit(0);
	}

	FD_SET( sock->getDesc(), &MASTER_SET );
	
	if( sock->getDesc() > topdesc )
		topdesc = sock->getDesc();

	descripts++;
}



void Server::remove( Socket * sock )
{
	// I exit on the following two errors because if this happens
	// you need to find out why.

	int desc = sock->getDesc();

	if( desc < 0 || desc >= maxdesc ) 
	{
		printf("SERVER: Close() - desc [%d] out of bounds.\n", desc);
		exit( 0 );
	}

	if( ! FD_ISSET( desc, &MASTER_SET ) )
	{
		printf("SERVER: Invalid descriptor\n");
		exit( 0 );
	}

	FD_CLR( desc, &MASTER_SET );

	if( !--descripts )
	{
		topdesc = masterdesc;
		return;
	}

	if( desc == topdesc )
	{
		for( desc = topdesc-1; !FD_ISSET( desc, &MASTER_SET ); desc-- )
			;
		topdesc = desc;
	}
}



int Server::canRead( Socket * sock )
{
	return FD_ISSET( sock->getDesc(), &R_SET );
}


int Server::canWrite( Socket * sock )
{
	return FD_ISSET( sock->getDesc(), &W_SET );
}


int Server::error( Socket * sock )
{
	int desc = sock->getDesc();

	if( FD_ISSET( desc, &E_SET ) )
	{
		FD_CLR( desc, &R_SET );
		FD_CLR( desc, &W_SET );
		return FD_ISSET( desc, &E_SET );
	}

	return 0;
}