#include <signal.h>
#include <stdio.h>
#ifdef SYSV
#include <string.h>
#else
#include <strings.h>
#endif
#include <ctype.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <sys/time.h>

#include "config.h"
#include "sys_proto.h"
#include "string.h"
#include "netio.h"
#include "buf.h"
#include "netio_private.h"
#include "servers.h"

static const char *create_fail = "Either there is already a player with that name, or that name is illegal.\r\n";
static const char *connect_fail = "Either that player does not exist, or has a different password.\r\n";

static void	check_connect(Player *p, char *msg);
static void	stop_programming(Player *p);

void
tell(Playerid player, const char *msg)
{
    Player	*p;
    String	*str;

    for(p = players; p; p = p->next) {
	if (p->connected && p->id == player) {
	    str = string_cpy(msg);
	    str = string_cat(str, "\r\n");
	    buf_add(&p->output, str);
	    break;
	}
    }
}

void
boot(Playerid who)
{
    Player	*p;

    for(p = players; p; p = p->next) {
	if (p->connected && p->id == who) {
	    write(p->fd, DISCONNECT_MSG, strlen (DISCONNECT_MSG));
	    disconnect_player(p->id);
	    remove_player(p);
	    break;
	}
    }
}

void
player_command(Player *p, char *command)
{
    if (!p->connected) {
	check_connect(p, command);
    } else if (p->isprogramming) {
	if (!strcmp(command, ".")) {
	    stop_programming(p);
	} else {
	    fprintf(p->progfile, "%s\n", command);
	}
    } else {
	while(isspace(*command)) {
	    command++;
	}
	p->parsing = 1;
	parse(p->id, command);
    }
}

void
parse_done(Playerid player)
{
    Player	*p;

    for(p = players; p; p = p->next) {
	if(p->id == player) {
	    p->parsing = 0;
	}
    }
}

static Player *
find_connect(Playerid player, Player *not)
{
    Player	*p;

    for (p = players; p; p = p->next) {
	if (p != not && p->connected && p->id == player) {
	    return p;
	}
    }
    return 0;
}

static void
check_connect(Player *p, char *msg)
{
    char	*command, *user, *password;
    Playerid	 player;
    Player	*oldp;

    parse_connect(msg, &command, &user, &password);

    if (!strncmp(command, "co", 2)) {
	player = connect_player(user, password);
	if (player == NOTHING) {
	    write(p->fd, connect_fail, strlen(connect_fail));
	    writelog();
	    fprintf(stderr, "Player failed connect %s on descriptor %d\n",
		    user, p->fd);
	} else if ((oldp = find_connect(player, p))) {
	    writelog();
	    fprintf(stderr, "Player %s(#%d) transferred from fd %d to fd %d\n",
		    user, player, oldp->fd, p->fd);
	    p->connected = 1;
	    p->id = player;
	    write(oldp->fd, TRANSFER_MSG1, strlen(TRANSFER_MSG1));
	    remove_player(oldp);
	    write(p->fd, TRANSFER_MSG2, strlen(TRANSFER_MSG2));
	    write(p->fd, CONNECT_MSG, strlen(CONNECT_MSG));
	} else {
	    writelog();
	    fprintf(stderr, "Player connected %s(#%d) on descriptor %d\n",
		     user, player, p->fd);
	    p->connected = 1;
	    p->id = player;
	    write(p->fd, CONNECT_MSG, strlen(CONNECT_MSG));
	}
    } else if (!strncmp(command, "cr", 2) && !registration) {
	player = create_player (user, password);
	if (player == NOTHING) {
	    write(p->fd, create_fail, strlen(create_fail));
	    writelog();
	    fprintf(stderr, "Player failed create %s on descriptor %d\n",
			user, p->fd);
	} else {
	    writelog();
	    fprintf(stderr, "Player created:  %s(#%d) on descriptor %d\n",
		     user, player, p->fd);
	    p->connected = 1;
	    p->id = player;
	    write (p->fd, CONNECT_MSG, strlen (CONNECT_MSG));
	}
    } else {
	write(p->fd, welcome, strlen(welcome));
    }
}

void
parse_connect (char *msg, char **command, char **user, char **pass)
{
    while (*msg && isascii(*msg) && isspace (*msg))	/* skip whitespace */
	msg++;
    *command = msg;
    while (*msg && isascii(*msg) && !isspace (*msg))	/* skip command */
	msg++;
    if (*msg) {
	*msg++ = '\0';					/* terminate cmd */
    }
    while (*msg && isascii(*msg) && isspace (*msg))	/* skip whitespace */
	msg++;
    *user = msg;
    while (*msg && isascii(*msg) && !isspace (*msg))	/* skip user */
	msg++;
    if (*msg) {
	*msg++ = '\0';					/* terminate user */
    }
    while (*msg && isascii(*msg) && isspace (*msg))	/* skip whitespace */
	msg++;
    *pass = msg;
    while (*msg && isascii(*msg) && !isspace (*msg))	/* skip password */
	msg++;
    *msg = '\0';					/* terminate pass */
}

int
start_programming(Playerid player, GENPTR *progwhat)
{
    Player	*p;

    for (p = players; p; p = p->next) {
	if (p->id == player) {
	    break;
	}
    }
    if (!p) {
	return -1;
    }
    sprintf(p->progfilename, "%s/%s_%d", PROGDIR, serv_id2name(0), player);
    if (!(p->progfile = fopen(p->progfilename, "w"))) {
	writelog();
	fprintf(stderr, "Couldn't open programming temp file ");
	perror(p->progfilename);
	p->progfilename[0] = '\0';
	return -2;
    } else {
	p->isprogramming = 1;
	p->progwhat = (void *) progwhat;
	return 0;
    }
}

static Playerid	progr;			/* programmer */

static void
stop_programming(Player *p)
{
    fclose(p->progfile);
    p->isprogramming = 0;
    if (!(p->progfile = fopen(p->progfilename, "r"))) {
	writelog();
	perror(p->progfilename);
    } else {
	progr = p->id;
	do_compile(p->id, p->progfile, p->progwhat);
	fclose(p->progfile);
	unlink(p->progfilename);
    }
    tell(p->id, "Exiting programming mode.");
    p->progfilename[0] = '\0';
    p->progfile = 0;
}

#define MSEC(T) (T.tv_sec * 1000 + T.tv_usec / 1000)

struct timeval
msec2timeval(int msec)
{
    struct timeval	r;

    r.tv_sec = msec / 1000;
    r.tv_usec = (msec % 1000 ) * 1000;
    return r;
}

void
queue_player_commands(struct timeval cur_time, struct timeval *timeout)
{
    Player		*p;
    int			msec_since_last;
    static struct timeval	last = {0, 0};
    struct timeval	new;

    msec_since_last = MSEC(cur_time) - MSEC(last);
    last = cur_time;
    for (p = players; p; p = p->next) {
	p->quota += msec_since_last;
	if (p->quota > MAX_CMDS * MSEC_PER_CMD) {
	    p->quota = MAX_CMDS * MSEC_PER_CMD;
	}
	if (p->input.head && p->quota >= 0 && !p->parsing) {
	    player_command(p, p->input.head->str->str);
	    buf_delhead(&p->input);
	    if (!p->isprogramming) {
		p->quota -= MSEC_PER_CMD;
	    }
	}
	if (p->input.head && p->quota < 0) {
	      /* return when quota is positive */
	    new = msec2timeval(-p->quota);
	    *timeout = (timercmp(&new, timeout, <)) ? new : *timeout;
	}
    }
}