/*
	Schrodinger's Box Haven  code version -0.1 alpha
        Copyright (C) 1994, Gordon Chan

	You can do whatever ya want with this code as long as you leave this
        copyright & disclaimer intact.  You can hack it up all you want, etc...
        No guarantees.

	Send comments, flames, bug reports, etc ... to
		au233@freenet.carleton.ca  <---- (preferred address for now)
		gjc@achilles.net
		gchan@ccs.carleton.ca
*/


/* Here's the copyrights & disclaimers from the Opium Den and unixCB codes */

/* Opium Den */
/*
  val's haven code v -0.85

        Copyright (C) 1994 Joel Ward

        Warranty:
                Don't blame me for anything.

        You can do whatever the fuck you want with this code as long as you
don't remove this copyright/disclaimer notice.
*/

/* unixCB */
/*
  
  cbd.c
  
  "cbd.c" is the main code for the CB simulator.
  
  Copyright (c) 1992, Gary Grossman.  All rights reserved.
  Send comments and questions to: garyg@soda.berkeley.edu
  
  */

#include<stdio.h>
#include<string.h>
#include<unistd.h>
#include<arpa/telnet.h>
#include<sys/time.h>

#include "linked_lists.h"
#include "main.h"
#include "control_sequences.h"
#include "misc_commands.h"
#include "chat.h"


static struct channel default_channel = {"blah", 0, NULL, NULL};

struct user *firstuser = (struct user *) 0;
struct channel *firstchannel = (struct channel *) 0;

int add_user ( desc, site, port )
	int desc, port;
	char *site;
{
	struct user *user, *u;
	struct timeval temptime;
	struct timezone tempzone;

	if ( ( user = (struct user *) malloc ( sizeof (struct user) ) ) == (struct user *) 0 ) {
#ifdef DEBUG
	 	write_err ("can't allocate user\r\n");
#endif
		write_user (desc, ">> Your player record could not be allocated.  Try again later.\n");
		close (desc);
		return -1;
	}


/* allocate space for queues */
	if ( !(user->input = (struct queue *) create_queue(user->input) ) ||
	     !(user->output = (struct queue *) create_queue(user->output) ) ) {
		write_user (desc, ">> Your player record could not be allocated.  Try again later.\r\n");
		close (desc);
		return -1;
	} else
		write_err ("queues created\r\n");

	if ( !(user->input->begin = (char *) create_text_queue(user->input->queue, INPUTMAX) ) ||
	     !(user->output->begin = (char *) create_text_queue(user->output->queue, OUTPUTMAX) ) ) {
		write_user (desc, ">> Your player record could not be allocated.  Try again later.\r\n");
		close (desc);
		return -1;
	} else
		write_err ("text queues created\r\n");


/* stick default channel onto the linked list of channels if it's not there */
	if (!firstchannel) {
		if ( (user->channel = (struct channel *) malloc 
		  (sizeof(struct channel)) ) == (struct channel *) 0) {
			write_user (desc, ">> Your player record could not be allocated.  Try again later.\r\n");
			close (desc);
			return -1;
		}
		initilize_channel (user->channel, default_channel.name);
		firstchannel = user->channel;	

	} else if ( (user->channel = (struct channel *) find_channel 
	  (default_channel.name)) == (struct channel *) 0) {
		if ( (user->channel = (struct channel *) malloc 
		  (sizeof(struct channel)) ) == (struct channel *) 0) {
			write_user (desc, ">> Your player record could not be allocated.  Try again later.\r\n");
			close (desc);
			return -1;
		}	
		add_channel (user->channel, default_channel.name);
	}


	FD_SET(desc, &master);
	usercount++;

	send_ts (desc, WILL, TELOPT_ECHO);
	send_ts (desc, WILL, TELOPT_SGA);

	user->desc = desc;
	strcpy (user->site, site);
	user->site [SITEMAX-1] = '\0';
	user->port = port;
	user->ts_mode = TS_NONE;

	initialize_user (user);

	/* stick user onto the end of the user linked list */
	if (!firstuser)
		firstuser = user;
	else {
		for (u = firstuser; u->next; u = u->next);
		u->next = user; 
	}	

#ifdef LOGIN_SCREEN
	set_login (user);
#else
	toggle_user (user, Flogged_in);
	greeting (user);
#endif
}
	

void initialize_user (user)
  struct user *user;
{
  struct timeval temptime;
  struct timezone tempzone;

  (void) gettimeofday (&temptime, &tempzone);
  user->logintime = user->idletime = temptime.tv_sec;

  user->channel->count++;
  user->private_to = (struct user *) 0;
  strcpy (user->name, "?");
  strcpy (user->description, "boring dude");
  user->next = NULL;
  user->sec_level = 1;	
  user->flags = 0;
  user->linecharmax = INPUTMAX;
  user->number_of_lines = 0;

  user->newline = '\\';
  user->colour = (rand() % 6) + 31;
  user->line_width = 80;

  user->gags = (unsigned char) 0;
  user->reverse_gags = (unsigned char) 0;
	
  strcpy(user->message_format, "%<_*_%l,\\p\\ %n/%d_*_%> %m"); 
  strcpy(user->action_format, "%<_*_%l\\,p\\_*_%> %n/%d %m"); 
}


void delete_user (user, str, silent, exit_str)
	struct user *user;
	char *str, *exit_str;
	int silent;
{
	struct user *u;

	FD_CLR(user->desc, &master);
	usercount--;

	if (user == firstuser) 
		firstuser = user->next;
	else {
		for (u = firstuser; u->next && user != u->next; u=u->next);
		u->next = user->next;
	}
		
	/* free up space taken by queues */
	
	destroy_text_queue (user->input->begin);
	destroy_queue (user->input);
	destroy_text_queue (user->output->begin);
	destroy_queue (user->output);
	write_err ("input and output queues NUKED\r\n");

#ifdef DEBUG
	sprintf (err, "user on line %d left\r\n", user->desc);
	write_err (err);
#endif
	if (is_user (user, Fcomm_line) || is_user (user, Ftyping) )
		write_user (user->desc, "\r\n");
	write_user (user->desc, str);
	if (!silent)
		handle_message_output (exit_str);

	/* remove user from channel */
	if (--(user->channel->count)) {
		if (user->channel->lock == user) {
			user->channel->lock = (struct user *) 0;
			for (u = firstuser; u; u = u->next)
				if (u->channel == user->channel)
					process_output (u, ">> Channel has been unlocked.\r\n");
		}
	} else 
		delete_channel (user->channel);

	/* un-gag and un-reverse-gag this line */
	for (u = firstuser; u; u = u->next) {
		if (u->gags)
			clrbit (u->gags, user->desc-1);
		if (u->reverse_gags)
			clrbit (u->reverse_gags, user->desc-1);
	}

	if (user->gags)
		free (user->gags);
	if (user->reverse_gags)
		free (user->reverse_gags);
	
	shutdown (user->desc, 2);
	close (user->desc);
	free (user);
}


/* find user in linked list of users */
/* it returns null if user or line# is not in linked list */

struct user *find_user (arg)
	char *arg;
{
	struct user *u;

	for (u = firstuser; u; u = u->next) {
		if (!strncmp(arg, u->name, NAMEMAX-1) || (atoi(arg) == u->desc) )
			return u;
	}
	return (struct user *) 0;
}		


/* queue handling routines */

struct queue *create_queue (q) 
	struct queue *q;
{
	if ( (q = (struct queue *) malloc (sizeof(struct queue)) ) == (struct queue *) 0)
		return (struct queue *) 0; 
	return q;
}


char *create_text_queue (q, size)
	char *q;
	int size;
{
	if ( (q= (char *) malloc (size*sizeof(char)) ) == (char *) 0) 	
		return (char *) 0;
	return q;
}


void destroy_queue (q)
	struct queue *q;
{
	free (q);
}

void destroy_text_queue (q)
	 char *q;
{
	free (q);
}


void clear_queue (q)
	struct queue *q;
{
	q->length = 0;
	q->queue = q->begin;
	*q->queue = '\0'; 
}	

