#include <atari.h>
#include <string.h>
#include "irc.h"
#include "addrs.h"
#include "screen.h"
#include "edbox.h"
#include "config.h"
#include "streq.h"
#include "timers.h"

/* A "command" is actually anything the user types, whether or
   not it starts with a / character. */

extern void num_to_numbuf(unsigned int n); /* printnum.s */

char *command, *arg1, *arg2, *arg3;
static char *target;

static void do_away(void);
static void do_bell(void);
static void do_click(void);
static void do_color(void);
static void do_ctcp(void);
static void do_info(void);
static void do_j(void);
static void do_j1(void);
static void do_list(void);
static void do_me(void);
static void do_msg(void);
static void do_ping(void);
static void do_query(void);
static void do_quit(void);
static void do_quote(void);
static void do_ver(void);
static void do_reset(void);
static void do_reboot(void);
static void do_optional_chan(void);

typedef struct {
	char *cmd;
	void (*func)(void);
	char req_arg;
} cmd_t;

/* future commands:
   OP DEOP VOICE DEVOICE KICK BAN KB IGNORE UNIGNORE MODE INVITE
 */
cmd_t command_defs[] = {
	{ "MSG", do_msg, 1 }, /* this must come first, do not move! */
	{ "AWAY", do_away, 1 },
	{ "ALERT", do_bell, 1 },
	{ "CLICK", do_click, 0 },
	{ "COLOR", do_color, 1 },
	{ "CTCP", do_ctcp, 1 },
	{ "INFO", do_info, 0 },
	{ "J", do_j, 0 },
	{ "J1", do_j1, 1 },
	{ "JOIN", do_j, 0 },
	{ "JOIN1", do_j1, 1 },
	{ "LIST", do_list, 1 },
	{ "M", do_msg, 1 },
	{ "ME", do_me, 1 },
	{ "NAMES", do_optional_chan, 0 },
	{ "PART", do_optional_chan, 0 },
	{ "PING", do_ping, 0 },
	{ "Q", do_query, 1 },
	{ "QUERY", do_query, 1 },
	{ "QUIT", do_quit, 0 },
	{ "QUOTE", do_quote, 1 },
	{ "TOPIC", do_optional_chan, 0 },
	{ "REBOOT", do_reboot, 0 },
	{ "RESET", do_reset, 0 },
	{ "VER", do_ver, 0 },
	{ "VERSION", do_ver, 0 },
	{ 0, 0 }
};

cmd_t *cmd_def;

static void do_reset(void) {
	extern unsigned char nreset(void);
	nreset();
}

static void cmd_chan_text(void) {
	scr_activate(scr_current);

	/* 0x02 = ^B = enable bold */
	scr_print_active("<\x02");
	scr_print_active(config.nick);
	scr_putc_active('\x02');

	scr_print_active("> ");
	scr_print_active(command);
	scr_eol_active();

	txbuf_set_str("PRIVMSG ");
	txbuf_append_str(target);
	txbuf_append_str(" :");
	txbuf_append_str(command);
	txbuf_send();
}


///
static void err_marker(void) {
	scr_print_current("\x02***\x02 ");
}

static void err_arg_req(void) {
	err_marker();
	scr_print_current(cmd_def->cmd);
	scr_print_current(": missing argument\n");
}

static void err_no_scr_target(void) {
	err_marker();
	scr_print_current("No channel/nick for screen\n");
}

static char have_commas(void) {
	if(strchr(arg1, ',')) {
		err_marker();
		scr_print_current("JOIN doesn't support commas\n");
		return 1;
	}
	return 0;
}

static void join_arg1(void) {
	txbuf_set_str2("JOIN ", arg1);
	txbuf_send();
}

static void mass_join(char scr) {
	if(have_commas())
		return;
	do {
		arg2 = nextarg(arg1);
		if(scr) {
			scr_create(arg1);
		}
		join_arg1();
		arg1 = arg2;
	} while(arg1);
}

static void do_j1(void) {
	mass_join(0);
}

static void do_j(void) {
	if(!arg1)
		arg1 = target; /* rejoin channel if window is 'dead' */

	if(!arg1) {
		err_no_scr_target();
		return;
	}

	mass_join(1);
}

static void do_quit(void) {
	reconnect_timeout = 0; /* do not reconnect on /quit */
	txbuf_set_str("QUIT");
	if(arg1) {
		txbuf_append_str2(" :", arg1);
	}
	txbuf_send();
}

static void pause(void) {
	OS.rtclok[2] = 0;
	while(OS.rtclok[2] != timers.hz)
		/* NOP */;
}

static void do_reboot(void) {
	arg1 = "Rebooting again...";
	do_quit();
	pause();
	do_reset();
	pause();
	asm("jmp $e477");
}

static void do_optional_chan(void) {
	if(arg1[0] == '#') {
		target = arg1;
		arg2 = nextarg(arg1);
	} else {
		arg2 = arg1;
	}

	if(!target) {
		err_no_scr_target();
		return;
	}

	txbuf_set_str(cmd_def->cmd);
	txbuf_append_spc();
	txbuf_append_str(target);
	if(arg2)
		txbuf_append_str2(" :", arg2);
	txbuf_send();
}

static void do_server_info(void) {
	txbuf_send_str("INFO");
}

/* arg1 = nick
   arg2 = CTCP type
   arg3 = optional data */
static void send_ctcp(void) {
	txbuf_set_str3("PRIVMSG ", arg1, " :\x01");
	txbuf_append_str(arg2);
	if(arg3)
		txbuf_append_str2(" ", arg3);
	txbuf_append_01();
	txbuf_send();
}

/* only the bottom 15 bits! */
unsigned int read_rtclok(void) {
	return ((OS.rtclok[1] << 8) | (OS.rtclok[2])) & 0x7fff;
}

static void rtclok_to_numbuf(void) {
	num_to_numbuf(read_rtclok());
}

void cmd_server_ping(void) {
	rtclok_to_numbuf();
	txbuf_set_str2("PING ", numbuf);
	txbuf_send();
}

static void do_ctcp_ping(void) {
	arg2 = "PING";
	rtclok_to_numbuf();
	arg3 = numbuf;
	send_ctcp();
}

void cmd_ctcp_ping(char *nick) {
	arg1 = nick;
	do_ctcp_ping();
}

static void do_no_arg_ctcp(char *type) {
	arg2 = type;
	arg3 = 0;
	send_ctcp();
}

static void do_ctcp_info(void) {
	do_no_arg_ctcp("CLIENTINFO");
}

static void do_ctcp_ver(void) {
	do_no_arg_ctcp("VERSION");
}

static void do_ctcp(void) {
	if(!arg1) {
		err_arg_req();
		return;
	}

	arg2 = nextarg(arg1);
	if(!arg2) {
		err_arg_req();
		return;
	}

	arg3 = nextarg(arg2);

	if(streq_i(arg2, "PING"))
		do_ctcp_ping();
	else
		send_ctcp();
}

static void do_me(void) {
	if(!target) {
		err_no_scr_target();
		return;
	}

	txbuf_set_str3("PRIVMSG ", target, " :" "\x01" "ACTION ");
	txbuf_append_str2(arg1, "\x01");
	txbuf_send();

	scr_print_current("\x02* ");
	scr_print_current(config.nick);
	scr_print_current("\x02 ");
	scr_print_current(arg1);
	scr_eol_current();
}

static void do_ping(void) {
	if(arg1)
		do_ctcp_ping();
	else
		cmd_server_ping();
}

static void do_list(void) {
	txbuf_set_str2("LIST ", arg1);
	txbuf_send();
}

static void do_info(void) {
	if(arg1)
		do_ctcp_info();
	else
		do_server_info();
}

static void do_quote(void) {
	txbuf_send_str(arg1);
}

static void do_ver(void) {
	if(arg1)
		do_ctcp_ver();
	else
		txbuf_send_str("VERSION");
}

static void do_color(void) {
	arg2 = nextarg(arg1);
	OS.color2 = a2uint(arg1);
	if(arg2)
		OS.color1 = a2uint(arg2);
}

static void do_query(void) {
	do {
		arg2 = nextarg(arg1);
		if(scr_create(arg1) == 0xff) {
			err_marker();
			scr_print_current("Query: all screens in use\n");
			return;
		}
		arg1 = arg2;
	} while(arg1);
}

static void do_msg(void) {
	arg2 = nextarg(arg1);
	if(arg2) {
		scr_print_current("->*");
		scr_print_current(arg1);
		scr_print_current("* ");
		scr_print_current(arg2);
		scr_eol_current();

		txbuf_set_str3("PRIVMSG ", arg1, " :");
		txbuf_append_str(arg2);
		txbuf_send();
	} else {
		err_arg_req();
	}
}

static void do_away(void) {
	OS.atract = 0x80;
	irc_away = 1;
	txbuf_set_str2("AWAY :", arg1);
	txbuf_send();
}

static void do_bell(void) {
	bell_type = *arg1 - '0';
}

static void do_click(void) {
	OS.noclik ^= 1;
}

static char cmd_local(void) {
	arg1 = nextarg(command);

	/*
	scr_print_current("command: ");
	scr_print_current(command);
	scr_eol_current();
	*/

	for(cmd_def = &command_defs[0]; cmd_def->cmd; cmd_def++) {
		if(streq_i(command, cmd_def->cmd)) {
			if(cmd_def->req_arg && !arg1) {
				err_arg_req();
			} else {
				(*(cmd_def->func))();
			}
			// scr_print_current("(did local command)\n");
			return 1;
		}
	}

	// scr_print_current("(not a local command)\n");
	return 0;
}

static void cmd_remote(void) {
	txbuf_set_str(command);
	if(arg1) {
		txbuf_append_spc();
		txbuf_append_str(arg1);
	}
	txbuf_send();
}
///



static void cmd_slash(void) {
	command++;
	if(!cmd_local())
		cmd_remote();
}

void cmd_execute(void) {
	if(!*edit_box) return;
	command = edit_box;

	if(scr_current > 1)
		target = scr_get_cur_name();
	else
		target = 0;

	if(command[0] == '/' && command[1] && command[1] != '/')
		cmd_slash();
	else if(target)
		cmd_chan_text();
	else if(scr_current == SCR_PRIV || scr_current == SCR_SERVER)
		cmd_send_pm(command);
	else
		err_no_scr_target();
}

void cmd_rejoin_chans(void) {
	char i;

	for(i = 2; i < MAX_SCREENS; i++) {
		if(screens[i].title[0] == '#') {
			arg1 = screens[i].title;
			join_arg1();
		}
	}

	if(!*(config.extra_channels))
		return;

	strncpy(edit_box, config.extra_channels, 128);
	arg1 = edit_box;
	mass_join(0);
	edbox_clear();
}

/* args contains the destination, space, the msg */
void cmd_send_pm(char *args) {
	cmd_def = command_defs; /* element 0 of command_defs must be MSG! */
	arg1 = args;
	do_msg();
}
