
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <limits.h>
#include <arpa/telnet.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/wait.h>
#include <sys/time.h>
#include <sys/resource.h>
#include <netinet/in.h>
#include <unistd.h>
#include <netdb.h>
#include <string.h>
#include <fcntl.h>
#include <signal.h>
#include <limits.h>

#include "config.h"
#include	"utils.h"
#include	"structs.h"
#include "functions.h"
#include "command.h"
#include "db.h"
#include "weather.h"


/* GLOBAL Variables */
extern struct cook_data *cook_list;
extern struct regen_data *timed_regen;
extern char *greetings;

/* STRUCTURES */
struct descriptor_data *desc_list, *next_desc;
struct char_data *channel[MAX_CHANNELS+1];
struct timeval boot_time,gametime,timespent,now;
static struct timeval last_time,timeout,null_time;

char	loadobjects = LOAD_OBJECTS;
char	loadmobs = LOAD_MOBS;
char	initworld = INIT_WORLD;

unsigned int	seed=0;

long  max_num=0;
long  countdown=0;
char	_shutdown = SHUTDOWN_RUNNING;

char	comm[MAX_INP_LEN];
char	buf[SMALL_BUFSIZE];
char	buf1[SMALL_BUFSIZE];
char	buf2[SMALL_BUFSIZE];
char	buf3[SMALL_BUFSIZE];

int	port=DEFAULT_PORT;
int	max_users=MAX_USERS;
int	mother,maxdesc;
int	min_login=MIN_LOGIN;

int	sock_connected=0;
int	sock_playing=0;
int	sock_logins=0;
int	last_desc=0;
//////////////////CRTH
char	TICKS = 0;
char	DAY_NIGHT[40+1];
char	Weather[160+1];
char	WeatherRep[160+1];
char	SEASON = 0;
int	DAYS = 0;
int	MONTHS = 1;
long	YEARS = 1;

char	DEBUG_CHAR = 0;
char	DEBUG_MOB = 0;
char	DEBUG_WORLD = 0;
int	Tmp = 0;
int	Wnd = 0;
int	Cld = 0;
int	Tpe = 0;
//////////////////CRTH
/*****************************************************************/
main(int argc, char **argv)
{
	int i=1,size,c;
	int x;

	//cprintf(buf,"~B - ~CH~DE~EL~FL~GO~~!~G",GREEN);
	//printf("%s\n",buf);
	//exit(0);

	while(argc > i) {
		if(*argv[i] == '-') {
			x = FALSE;
		} else if(*argv[i] == '+') {
			x = TRUE;
		} else {
			printf("Invalid parameter: %s\n",--argv[i]);
			exit(1);
		}
		switch(*++argv[i]) {
		case 'p':
			if(argc <= i) {
				printf("No port number supplied with option '-p'\n");
				exit(1);
			} else if((port=atoi(argv[++i]))<1024) {
				printf("Invalid port number!\n");
				exit(1);
			}
			break;
		////////CRTH
		case 'd': if(argv[i+1] == NULL)
						 break;
					 switch(*argv[++i])
					 {	case '0':	DEBUG_WORLD = 1;
										DEBUG_MOB = 1;
										DEBUG_CHAR = 1;
										break;
						case '1':	DEBUG_MOB = 1;
										break;
						case '2':	DEBUG_CHAR = 1;
					 }
							
				break;
		////////CRTH
		case 'r':
			if(argc <= i) {
				printf("No level restriction supplied with option '-r'\n");
				exit(1);
			} else if(!_numeric(&min_login,argv[++i])) {
				printf("Invalid level restriction\n");
				exit(1);
			}
			break;
		case 'm':
			if(argc <= i) {
				printf("Maximum number of users not specified with option '-m'\n");
				exit(1);
			} else if(!_numeric(&max_users,argv[++i])) {
				printf("Invalid number of maximum users\n");
				exit(1);
			}
			break;
		case 'x':
			loadmobs = x;
			printf("Mobiles will %s be loaded...\n", loadmobs ? "*NOT*" : "");
			break;
		case 'y':
			loadobjects = x;
			printf("Objects will %s be loaded...\n", loadobjects ? "*NOT*" : "");
			break;
		case 'z':
			initworld = x;
			printf("World will %s be initialised...\n", initworld ? "*NOT*" : "");
			break;
		default:
			printf("Invalid arguement: %s\n",--argv[--i]);
			exit(1);
		}
		i++;
	}

	printf("Using port: %d\n",port);
	mother=maxdesc=init_socket(port);

	/* Set the seed (for the random number generator) */
	gettimeofday(&boot_time,(struct timezone *)0);
	seed = (unsigned int)boot_time.tv_usec;
	srandom(seed);

	/* Load the world into memory and start it ticking */
	boot_world();

	if(DEBUG_WORLD)
		printf("*** FULL DEBUG ON\n");
	else
	{
		if(DEBUG_MOB)
			printf("*** MOBILE DEBUG ON\n");			
		if(DEBUG_CHAR)
			printf("*** CHARACTER DEBUG ON\n");
	}

	// All seasonal and date data should be saved on shutdown
	// and reloaded at this point.
	LoadSeasonDate();
	sprintf(DAY_NIGHT,"%s",TODAY[0]);
	WeatherChange();
	
	/* Go and control the game flow */
	game_loop();

	exit(0);
}

void	game_loop(void)
{
	fd_set input_set, output_set, exc_set, ready;
	struct descriptor_data *point, *next_point;
	int    pulse=0;

	null_time.tv_sec=null_time.tv_usec=0;

	signal(SIGCHLD,(void *)catch_zombie);

	/* The main game loop */
	do {

		FD_ZERO(&input_set);
		FD_ZERO(&output_set);
		FD_ZERO(&exc_set);
		FD_SET(mother,&input_set);
		for(point=desc_list;point;point=next_point) {
			next_point=point->next_in_list;
			FD_SET(point->port_desc, &input_set);
			FD_SET(point->port_desc, &output_set);
			FD_SET(point->port_desc, &exc_set);
		}

		if(select(maxdesc+1,&input_set,&output_set,&exc_set,&null_time)<0) {
			perror("Select poll");
			exit(1);
		}

		if(select(0,(fd_set *)0, (fd_set *)0, (fd_set *)0, &timeout)<0) {
			perror("Select poll");
			exit(1);
		}

		sigsetmask(0);
		/* Any new connections? */
		if(FD_ISSET(mother, &input_set))
			if(new_descriptor()<0)
				perror("New descriptor");

      for(point=desc_list;point;point=next_point) {
         next_point=point->next_in_list;
         if(FD_ISSET(point->port_desc,&exc_set)) {
            FD_CLR(point->port_desc,&input_set);
            FD_CLR(point->port_desc,&output_set);
               close_socket(point);
         }
      }

      /* Check for input */
      for(point=desc_list;point;point=next_point) {
         next_point=point->next_in_list;
         if(FD_ISSET(point->port_desc, &input_set)) {
            if(process_input(point)<0)
               close_socket(point);
			}
      }

      /* process commands */
      for(point=desc_list;point;point=next_point) {
         next_point=point->next_in_list;
         if((--(point->wait) <=0) && get_input(point, comm))
					interpreter(point, comm);
      }

		for(point=desc_list;point;point=next_desc) {
     		next_desc=point->next_in_list;
      	if(FD_ISSET(point->port_desc, &output_set) && *(point->output_buf)) {
				if(process_output(point)<0)
					close_socket(point);
				else
					point->prompt=1;
			}
		}

      for (point=desc_list;point;point=next_point) {
         next_point = point->next_in_list;
         if (point->connected == CON_CLOSE)
            close_socket(point);
      }

      for(point=desc_list;point;point=next_point) {
         next_point=point->next_in_list;
         if(point->prompt && (point->connected > CON_LINKLESS)) {
            if(point->character->LEVEL == IMP) {
               my_write(point->port_desc,"\n\r# ");
            } else {
					sprintf(buf,"\n\r<\033[31m%d\033[37m:\033[35m%d\033[37m:\033[32m%d\033[37m>\033[0m ",point->character->HIT,point->character->MANA,point->character->MOVE);
               my_write(point->port_desc,buf);
				}
            point->prompt=0;
         }
      }

		/* Need to do fighting, regeneration, zone refresh,
			mob movement & violencce, weather, time... */

		gettimeofday(&now,(struct timezone *)0);
	      	timespent.tv_sec = now.tv_sec - last_time.tv_sec;
      		timespent.tv_usec = now.tv_usec - last_time.tv_usec;
		gametime.tv_sec = now.tv_sec - boot_time.tv_sec;
		gametime.tv_usec = now.tv_sec - boot_time.tv_usec;

		if(last_time.tv_sec != now.tv_sec) {
			if(_shutdown == SHUTDOWN_COUNTING) {
				if(!countdown) {
					_shutdown = 0;
				} else if(!(--countdown)) {
					send_2_all(0,"\n\r\033[1mSYSTEM: Shutdown now!\033[0m\n\r");
					save_all();
					SaveSeasonDate();
				} else if(!(countdown % 60)) {
					sprintf(buf,"\033[1mSYSTEM: Shutdown in %d minute(s)\033[0m\n\r",(countdown/60));
					send_2_all(0, buf);
				}
			} 

			/* Decrement the life of the objects */
			if(cook_list)
				--cook_list->counter;
			/* Remove all items of time zero or less */
			while((cook_list) && cook_list->counter < 1)
				rem_cooked_item();

			/* Decrement the regen list */
			if(timed_regen)
				--timed_regen->counter;
			/* Remove all items to be regenerated this turn */
			while((timed_regen) && timed_regen->counter < 1)
				regenerate(timed_regen);

			if(!(gametime.tv_sec % ACT_INTELLIGENT))
				do_mob_flags();
			if(!(gametime.tv_sec % DO_VIOLENCE))
				do_violence();
			if(!(gametime.tv_sec % DO_MOVEMENT))
				move_mobs();
			if(!(gametime.tv_sec % CHECK_HEALTH))
				check_health();
//////////CRTH
			if(!(gametime.tv_sec % ROOM_EVENTS))
				check_room_events();

			if(!(gametime.tv_sec % CHECK_ACTORS))
				check_actors();

			if(!(gametime.tv_sec % TIME_OF_DAY))
				amend_TODAY();			

			if(!(gametime.tv_sec % WEATHER_UPD))				
			        send_weather();		
			
			if(!(gametime.tv_sec % COMFORTLEVEL))
				SaveSeasonDate();
///////////CRTH
		}

		/* Put this at the end */
      last_time = now;

	} while(_shutdown);
}
/**************************************
*		Socket routines: M.K.Judge.     *
**************************************/
int	init_socket(int port)
{
	struct sockaddr_in server;
	int sock;
	char *opt;

	/* Name the socket */
	server.sin_family=AF_INET;
	server.sin_addr.s_addr=INADDR_ANY;
	server.sin_port=htons(port);

	/* Create the socket */
	if((sock=socket(AF_INET, SOCK_STREAM, 0))<0) {
		perror("Opening stream socket");
		exit(1);
	}

	/* Set socket re-useable */
	if(setsockopt(sock,SOL_SOCKET,SO_REUSEADDR,(char *)&opt,sizeof(opt))<0) {
		perror("setsockopt REUSEADDR");
		exit(1);
	}

	/* Bind the socket */
	if(bind(sock,(struct sockaddr *) &server, sizeof(server))) {
		perror("Binding socket");
		exit(1);
	}
	listen(sock,3);
	return(sock);
}

void	catch_zombie(void)
{
	union wait wstatus;

	while(wait3(&wstatus,WNOHANG,NULL)>0);
}

int	new_descriptor(void)
{
	int desc,size,c;
	struct descriptor_data *newd, *point, *next_point;
	struct sockaddr_in sock;
	struct hostent *from;
	
	if(DEBUG_WORLD)
		printf("Connection attempt.\n");

	if(!(desc=new_connection()))
		return(-1);

	sock_logins++;
	sock_connected++;
	if(desc > maxdesc)
		maxdesc=desc;

	CREATE(newd, struct descriptor_data, 1);
	memset(newd, 0, sizeof(struct descriptor_data));
	CREATE(newd->character, struct char_data, 1);
	memset(newd->character, 0, sizeof(struct char_data));

	size=sizeof(sock);
	if(getpeername(desc, (struct sockaddr *) &sock, &size)<0) {
		perror("getpeername");
		*newd->character->host='\0';
	} else {
		c=sock.sin_addr.s_addr;
		sprintf(newd->character->host,"%d.%d.%d.%d", (c &0x000000FF),
		(c &0x0000FF00) >> 8, (c &0x00FF0000) >> 16, (c &0xFF000000) >> 24);
	}

	/* Check if banned */

	/* Init descriptor */
	CREATE(newd->output_buf,char,SMALL_BUFSIZE);
	newd->bufspace = SMALL_BUFSIZE-1;
	newd->connected = CON_NME;
	newd->port_desc = desc;
	newd->wait = -1;
	*newd->input_buf = '\0';
	*newd->last_input = '\0';
	
	/* Prepend to list */
	newd->next_in_list = desc_list;
	desc_list=newd;

	newd->character->desc=newd;
	newd->character->in_room = 0;
	/******************************************************
		Give the caller a welcome!
	******************************************************/
	send_2_output(greetings, newd);
	send_2_output("By what name are you known, traveller?: ", newd);
	return(0);
}


int	new_connection(void)
{
	struct sockaddr_in server;
	int sock,i;

	i=sizeof(server);
	getsockname(mother,(struct sockaddr *)(&server),&i);

	if((sock=accept(mother,(struct sockaddr *)(&server),&i))<0) {
		perror("accept");
		return(0);
	}

	if(fcntl(sock, F_SETFL, FNDELAY) == -1) {
		perror("Fatal error executing nonblock");
		exit(1);
	}
	return(sock);
}


void	close_socket(struct descriptor_data *d)
{
	/* Close and clear the socket */
	if(d->port_desc) {
		close(d->port_desc);
		--sock_connected;
	
		if(d->port_desc == maxdesc)
			--maxdesc;

		/* Flag as no socket attached */
		d->port_desc = 0;
	}

	/* Check for a linkless character */
	if(d->character && (d->connected == CON_PLYNG)) {
		/* Let the people know what's happening */
		sprintf(buf,"has lost %s link",sex[d->character->SEX].b);
		send_2_room(d->character,IGNR_INVIS,buf);
		/* Save it's current state (just in case) */
		save_char(d->character);
		/* Make the character untouchable */
		change_status(d->character, CON_LINKLESS);
	} else if(d->character->POSITION == POS_DEAD) {
		if(DEBUG_CHAR)
			printf("%s is DEAD!.\n",d->character->name);
		kill_char(d->character,"s' spirit has departed this world...");	
	} else {
		if(DEBUG_CHAR)
			printf("%s has disconnected.\n",d->character->name);
		kill_char(d->character,"has left the game");
	}
}

/******************************************
*				I/O routines.						*
*******************************************/
int	my_write(int desc, char *txt)
{
	write(desc, txt, strlen(txt));
	return;
}

int send_2_output(char *txt, struct descriptor_data *desc)
{
	char	*tmp;
	int size = strlen(txt);

   if((!desc) || !desc->port_desc)
      return(0);

	if(desc->bufspace >= size) {
		strcpy(desc->output_buf+desc->bufptr,txt);
		desc->bufspace -= size;
		desc->bufptr += size;
   } else if(!desc->buf_size) {
		CREATE(tmp,char,LARGE_BUFSIZE);
		strcpy(tmp,desc->output_buf);
		free(desc->output_buf);
		desc->output_buf=tmp;
		desc->bufspace = (LARGE_BUFSIZE - desc->bufptr);
		desc->buf_size++;
      strcpy(desc->output_buf+desc->bufptr,txt);
      desc->bufspace -= size;
      desc->bufptr += size;
	}
}

int process_input(struct descriptor_data *t)
{
   int rval,c;

	memset(buf, 0, sizeof(buf));
   if((rval=read(t->port_desc,buf,MAX_INP_LEN-2))<0) {
      perror("Reading Input");
      return(-1);
   } else if(rval <= 0) {
      perror("Read Error");
      return(-1);
   }

   if(*buf <= 0)
      return(0);

	for(c=0;*(t->input_buf+c) != '\0';c++);
	if(*buf == '!') {
		rval=strlen(t->last_input);
		if((rval+c)>=MAX_INP_LEN)
			return(0);
		strcpy((t->input_buf+c),t->last_input);
	} else if((rval+c)>=MAX_INP_LEN)
		return(0);
	else
		strcpy((t->input_buf+c),buf);
	return(0);
}

/* Seperate a command from the input buffer */
int get_input(struct descriptor_data *d, char *dest)
{
	int s,c;

	/* Copy what the command across (if there) */
	for(s=0;*(d->input_buf+s) != '\n' && *(d->input_buf+s);s++)
		*(dest+s) = *(d->input_buf+s);
	*(dest+s) = '\0';

	/* Check if we have a command */
	if(*(d->input_buf+s) != '\n')
		return(0);

	/* Remove any trailing blanks */
	for(;(*(d->input_buf+s) < 33 || *(d->input_buf+s) > 'z') && *(d->input_buf+s);s++);

	/* Delete the line from the input buffer */
	for(c=0;*(d->input_buf+s) != '\0';c++,s++)
		*(d->input_buf+c) = *(d->input_buf+s);
	*(d->input_buf+c) = '\0';

	/* Save the last input */
	strcpy(d->last_input,dest);
	return(1);
}

int process_output(struct descriptor_data *t)
{
	/* If playing the game: Move down a line first */
	if(t->connected == CON_PLYNG)
		if(!write(t->port_desc,"\n\r",2))
			return(-1);

	/* Send the text to be sent */
 	if(!write(t->port_desc,t->output_buf,t->bufptr))
		return(-1);

	/* Clear the buffers */
   if(t->buf_size) {
		free(t->output_buf);
		CREATE(t->output_buf,char,SMALL_BUFSIZE);
		t->buf_size=0;
   }
   t->bufspace=SMALL_BUFSIZE-1;
   t->bufptr=0;
   *(t->output_buf)='\0';

   return(1);
}


