/* ARISA - Admin Interface - Global Menu
 * Copyright (C) 2003 Carl Ritson
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
 */

/** Global Menu Commands **/

MENU_CMD(cmd_quit) {
	return RET_QIT;
}

MENU_CMD(cmd_exit) {
	if(ins->prev == NULL)
		return CHAIN_CMD(cmd_quit);
	else
		return RET_UP;
}

MENU_CMD(cmd_log) {
	if(strcmp(args[1],"on") == 0)
		log_subscribe(ac->msgqueue);
	else if(strcmp(args[1],"off") == 0)
		log_unsubscribe(ac->msgqueue);
	else {
		LOCK(ac->user);
		LOG("<%s>: %s",ac->user->username,args[1]);
		UNLOCK(ac->user);
	}
	return 0;
}

MENU_CMD(cmd_prompt) {
	admin_output_prompt(ac);
	return 0;
}

MENU_CMD(cmd_gotoroot) {
	return RET_RTN; 
}

MENU_CMD(cmd_date) {
	char buffer[32];
	admin_msg(ac,"%s",timestr(xtime(),buffer,sizeof(buffer)));
	return 0;
}

MENU_CMD(cmd_colour) {
	if(strcasecmp(args[1],"none") == 0)
		ac->colour = COLOUR_NONE;
	else if(strcasecmp(args[1],"irc") == 0)
		ac->colour = COLOUR_IRC;
	else if(strcasecmp(args[1],"int") == 0)
		ac->colour = COLOUR_INT;
	else {
		admin_msg(ac,"Invalid colour type: %s",args[1]);
		RET_VAL(RET_INV_ARG);
	}
	return 0;
}

MENU_CMD(cmd_pljp) {
	int i,found;
	LOCKR(&(global->party_line.lock));
	for(i = 0, found = 0; i < global->party_line.no_subs && !found; ++i) {
		if(global->party_line.subs[i] == ac->msgqueue)
			found = 1;
	}
	UNLOCKR(&(global->party_line.lock));
	if(found)
		admin_party_line_unsubscribe(ac);
	else
		admin_party_line_subscribe(ac);
	return 0;
}

MENU_CMD(cmd_plmsg) {
	char buffer[510];
	LOCK(ac->user);
	snprintf(buffer,sizeof(buffer),"<%s> %s",ac->user->username,args[1]);
	UNLOCK(ac->user);
	admin_party_line_msg(buffer,ac->msgqueue);
	return 0;
}

MENU_CMD(cmd_msg) {
	char buffer[510];
	user_t *u;
	int i,j;
	
	u = user_find(args[1]);
	if(u != NULL) {
		LOCK(ac->user);
		snprintf(buffer,sizeof(buffer),"%sPM%s <%s> %s",
				COLOUR_BOLD,COLOUR_BOLD,
				ac->user->username,args[2]);
		UNLOCK(ac->user);
		LOCK(global);
		for(i = 0, j = 0; i < global->no_admins; ++i) {
			LOCK(global->admins[i]);
			if(global->admins[i]->admin.user == u) {
				pqueue_push_back(&global->admins[i]->msgqueue,xstrdup(buffer));
				j++;
			}
			UNLOCK(global->admins[i]);
		}
		UNLOCK(global);
		if(j == 0) {
			admin_msg(ac,"User %s, not logged on.",args[1]);
			RET_VAL(RET_EXE_ERR);
		}
	} else {
		admin_msg(ac,"Invalid User: %s",args[1]);
		RET_VAL(RET_INV_ARG);
	}

	return 0;
}


static const char *send_state_text(state_t s); // pre-declaration
MENU_CMD(cmd_whoison) {
	char username[64],buffer[32];
	int i,j;
	user_t *u;
	
	LOCK(global);
	for(i = 0; i < global->no_admins; ++i) {
		LOCK(global->admins[i]);
		if(global->admins[i]->state == STATE_COMPLETE) {
			UNLOCK(global->admins[i]);
			continue;
		}
		
		if(global->admins[i]->admin.user != NULL) {
			/* This lock bounce is okay since we have the 
			 * global lock.
			 */
			u = global->admins[i]->admin.user;
			UNLOCK(global->admins[i]);
			LOCK(u);
			xstrncpy(username,u->username,sizeof(username));
			UNLOCK(u);
			LOCK(global->admins[i]);
		} else
			strcpy(username,"-");
		
		LOCKR(&(global->party_line.lock));
		for(j = 0; j < global->party_line.no_subs && j >= 0; ++j) {
			if(global->party_line.subs[j] == 
					&(global->admins[i]->msgqueue))
				j = -2;
		}
		UNLOCKR(&(global->party_line.lock));
		
		admin_cmsg(ac,"%s (%s), State: %s, Since: %s%s%s",
			username,
			global->admins[i]->host == NULL ? "Via Socket" :
			 global->admins[i]->host,
			send_state_text(global->admins[i]->state),
			timestr(global->admins[i]->connected,buffer,
							sizeof(buffer)),
			j >= 0 ? "" : ", ",
			j >= 0 ? "" : "%9PL%9");
		
		UNLOCK(global->admins[i]);
	}
	UNLOCK(global);
	
	return 0;
}

MENU_CMD(cmd_netmsg) {
	network_t *n;
	char buffer[512];
	
	n = irc_network_find(args[1]);
	if(n != NULL) {
		colourise(COLOUR_IRC,buffer,sizeof(buffer),args[3]);
		irc_send_msg(n,args[2],buffer);
	} else {
		admin_msg(ac,"Invalid Network: %s",args[1]);
		RET_VAL(RET_INV_ARG);
	}
	return 0;
}


static int qsort_cmdcmp(const void *a, const void *b); // pre-declaration
MENU_CMD(cmd_help) {
	unsigned long privs;
	int i,len,no_cmds = 0;
	menu_t *cmd,**cmds = NULL;
	char *cmd_str;
	
	if(no_args == 1)
		cmd_str = "help";
	else 
		cmd_str = args[1];
	
	LOCK(ac->user);
	privs = ac->user->uprivs;
	UNLOCK(ac->user);
	
	for(i = 0; cmd_str[i] != '\0'; ++i) {
		if(cmd_str[i] == '?')
			break;
	}

	len = i;
	if(cmd_str[len] == '?') {
		cmds = find_cmds(ins,cmd_str,len,privs,&no_cmds,0,0);
		if(no_cmds > 0) {
			if(no_cmds > 1)
				qsort(cmds,no_cmds,sizeof(char *),qsort_cmdcmp);
			for(i = 0; i < no_cmds; ++i) 
				admin_msg(ac,"%s",cmds[i]->name);
		}
	} else {
		cmds = find_cmds(ins,cmd_str,len,privs,&no_cmds,0,1);
		cmd = NULL;
		if(no_cmds == 1) 
			cmd = cmds[0];
		else if(no_cmds > 1) {
			for(i = 0; i < no_cmds && cmd == NULL; ++i) {
				if(strcmp(cmds[i]->name,cmd_str) == 0)
					cmd = cmds[i];
			}
		}	
		
		if(cmd != NULL) {
			if(cmd->help != NULL) {
				admin_cmsg(ac,"Help for %s (%s).",
						cmd_str,cmd->name);
				admin_cmsg(ac,"");
				for(i = 0; i < cmd->no_help; ++i)
					admin_cmsg(ac,cmd->help[i],cmd->name);
			} else {
				admin_msg(ac,"No help available for %s (%s).",
						cmd_str,cmd->name);
			}
		} else {
			admin_msg(ac,"Command %s not found.",cmd_str);
		}
	}
	
	if(cmds != NULL) {
		for(i = 0; i < no_cmds; ++i)
			menu_deref(cmds[i]);
		xfree(cmds);
	}
	
	return 0;
}		

MENU_CMD(cmd_verbose) {
	if(strcasecmp(args[1],"on") == 0)
		ac->verbose = 1;
	else
		ac->verbose = 0;
	return 0;
}

static double d_avgspeed(uint32_t *ags) {
	double ret = 0.0;
	int i;
	for(i = 0; i < AVGSPEED; ++i)
		ret += (double) ags[i];
	return ret / (double)AVGSPEED;
}

static void gstat_line(acontext_t *ac) {
	char shared_buf[8], ul_buf[8], dl_buf[8];
	double ul_rate = 0.0, dl_rate = 0.0;
	off_t shared = 0, ul = 0, dl = 0;
	int no_chats = 0, no_sends = 0, no_queues = 0, no_uploads = 0;
	int no_admins = 0, no_packs = 0, no_sent = 0;
	int max_chats = 0, max_sends = 0, max_queues = 0, max_uploads = 0;
	int i,j;

	LOCK(global);
	no_admins = global->no_admins;
	for(i = 0; i < global->no_interfaces; ++i) {
		interface_t *intf = global->interfaces[i];
		LOCK(intf);
		if(intf->type == INTERFACE_SEND) {
			ul += intf->stat_mib * (1024.0 * 1024.0) + 
				intf->stat_carry;
			ul_rate += d_avgspeed(intf->avgspeed);
			for(j = 0; j < intf->send.no_pools; ++j) {
				pool_t *p = intf->send.pools[j];
				LOCK(p);
				no_sends += p->no_csends;
				max_sends += p->sends;
				UNLOCK(p);
			}
		} else if(intf->type == INTERFACE_RECV) {
			dl += intf->stat_mib * (1024.0 * 1024.0) +
				intf->stat_carry;
			dl_rate += d_avgspeed(intf->avgspeed);
			no_uploads += intf->recv.no_uploads;
			max_uploads += intf->recv.max_no_uploads;
		} else if(intf->type == INTERFACE_CHAT) {
			ul += intf->stat_mib * (1024.0 * 1024.0) +
				intf->stat_carry;
			ul_rate += d_avgspeed(intf->avgspeed);
			no_chats += intf->chat.no_chats;
			max_chats += intf->chat.max_no;
		}
		UNLOCK(intf);
	}
	for(i = 0; i < global->no_queues; ++i) {
		queue_t *q = global->queues[i];
		LOCK(q);
		no_queues += q->no_qsends;
		max_queues += q->length;
		UNLOCK(q);
	}
	for(i = 0; i < global->no_packlists; ++i) {
		packlist_t *p = global->packlists[i];
		LOCK(p);
		no_packs += p->no_packs;
		for(j = 0; j < p->no_packs; ++j) {
			RLOCK(p->packs[j]);
			shared += p->packs[j]->size;
			no_sent += p->packs[j]->no_gets;
			RUNLOCK(p->packs[j]);
		}
		UNLOCK(p);
	}
	UNLOCK(global);

	admin_msg(ac,"GStat: U:%.1fKiB/s D:%.1fKiB/s S:%d/%d Q:%d/%d R:%d/%d C:%d/%d A:%d P:%d/%d Sh:%s Ul:%s Dl:%s",
		ul_rate / 1024.0,dl_rate / 1024.0,
		no_sends,max_sends,no_queues,max_queues,
		no_uploads,max_uploads,no_chats,max_chats,
		no_admins,
		no_sent,no_packs,three_digit_size(shared_buf,shared),
		three_digit_size(ul_buf,ul),
		three_digit_size(dl_buf,dl));
}

MENU_CHECK(gstat_line_checker) {
	gstat_line(ac);
	return 0;
}

MENU_CMD(cmd_stats) {
	if(no_args < 2) {
		gstat_line(ac);
	} else {
		while(ins->prev != NULL)
			ins = ins->prev;
		if(strcasecmp(args[1],"on") == 0) {
			if(ins->check == NULL) {
				ins->check = gstat_line_checker;
				admin_msg(ac,"Global stat line: on.");
			} else if(ins->check == gstat_line_checker) {
				admin_msg(ac,"Global stat line: on.");
			} else {
				admin_msg(ac,"Global stat line can not be set.");
			}
		} else if(strcasecmp(args[1],"off") == 0) {
			if(ins->check == gstat_line_checker) {
				ins->check = NULL;
				admin_msg(ac,"Global stat line: off.");
			} else {
				admin_msg(ac,"Global stat line is not on.");
			}
		} else {
			admin_msg(ac,"%s [on|off]",args[0]);
		}
	}
	return 0;
}

/** Global Menu Structure **/
MENU_HELPER(cmd_net_channels_helper); // pre-declaration
static const cmd_t global_menu[] = {
	{"/",		cmd_gotoroot,	0,0,0,1,	PRIVS_EMPTY,NULL},
	{"toroot",	cmd_gotoroot,	0,0,0,1,	PRIVS_EMPTY,NULL},
	{"'",		cmd_plmsg,	1,-2,0,1,
			PRIV_TO_MASK(PRIV_PARTY_LINE),	NULL},
	{"plmsg",	cmd_plmsg,	1,-2,0,1,
			PRIV_TO_MASK(PRIV_PARTY_LINE),	NULL},
	{";",		cmd_pljp,	0,0,0,1,
			PRIV_TO_MASK(PRIV_PARTY_LINE),	NULL},
	{"pljp",	cmd_pljp,	0,0,0,1,
			PRIV_TO_MASK(PRIV_PARTY_LINE),	NULL},
	{":",		cmd_msg,	2,-2,0,1,
			PRIV_TO_MASK(PRIV_PARTY_LINE),	NULL},
	{"pmsg",	cmd_msg,	2,-2,0,1,
			PRIV_TO_MASK(PRIV_PARTY_LINE),	NULL},
	{".",		cmd_prompt,	0,0,0,1,	PRIVS_EMPTY,NULL},
	{"prompt",	cmd_prompt,	0,0,0,1,	PRIVS_EMPTY,NULL},
	{"..",		cmd_exit,	0,0,0,1,	PRIVS_EMPTY,NULL},
	{"|",		cmd_whoison,	0,0,0,1,
			PRIV_TO_MASK(PRIV_PARTY_LINE),	NULL},
	{"whoison",	cmd_whoison,	0,0,0,1,
			PRIV_TO_MASK(PRIV_PARTY_LINE),	NULL},
	{"=",		cmd_netmsg,	3,-2,0,1,
			PRIV_TO_MASK(PRIV_MESSAGE),	cmd_net_channels_helper},
	{"netmsg",	cmd_netmsg,	3,-2,0,1,
			PRIV_TO_MASK(PRIV_MESSAGE),	cmd_net_channels_helper},
	{"colour",	cmd_colour,	1,1,0,1,	PRIVS_EMPTY,NULL},
	{"date",	cmd_date,	0,0,0,1,	PRIVS_EMPTY,NULL},
	{"exit",	cmd_exit,	0,0,0,1,	PRIVS_EMPTY,NULL},
	{"log",		cmd_log,	1,-2,0,1,
			PRIV_TO_MASK(PRIV_LOG),		NULL},
	{"help",	cmd_help,	0,1,0,0,	PRIVS_EMPTY,NULL},
	{"quit",	cmd_quit,	0,0,1,1,	PRIVS_EMPTY,NULL},
	{"stats",	cmd_stats,	0,1,0,0,	PRIVS_EMPTY,NULL},
	{"verbose",	cmd_verbose,	1,1,0,1,	PRIVS_EMPTY,NULL},
	{NULL}
};
