/* ARISA - Admin Interface - Interface 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
 */

static int is_int_new(interface_t *i) {
	int j;
	LOCK(global);
	for(j = 0; j < global->no_interfaces; ++j) {
		if(global->interfaces[j] == i) {
			UNLOCK(global);
			return 0;
		}
	}
	UNLOCK(global);
	return 1;
}

static const char *send_state_text(state_t s) {
	switch(s) {
		case STATE_UNKNOWN:	return "%rUNKNOWN%n";
		case STATE_QUEUED:	return "%cQUEUED%n";
		case STATE_RESOLVING:	return "%cRESOLVING%n";
		case STATE_CONNECTING:	return "%rCONNECTING%n";
		case STATE_NEGOTIATING:	return "%cNEGOTIATING%n";
		case STATE_LISTENING:	return "%cLISTENING%n";
		case STATE_ACTIVE:	return "%gACTIVE%n";
		case STATE_COMPLETING:	return "%GCOMPLETING%n";
		case STATE_COMPLETE:	return "%GCOMPLETE%n";
		case STATE_SLOW:	return "%ySLOW%n";
		case STATE_TIMEOUT:	return "%yTIMEOUT%n";
		case STATE_DELETED:	return "%rDELETED%n";
		case STATE_PURGING:	return "%rPURGING%n";
		case STATE_PURGEREQUEUE: return "%rREQUEUE%n";
		case STATE_ERROR:	return "%rERROR%n";
		case STATE_RETRY:	return "%yRETRY%n";
		case STATE_RESUME:	return "%cRESUME%n";
		case STATE_RESUMED:	return "%cRESUMED%n";
		default:
					return "UNKNOWN";
	}
}

/** Interface Menu Commands **/

MENU_HELPER(cmd_interfaces_helper) {
	int i,j;
	LOCK(global);
	if(no_args == 2) {
		for(j = 0; args[1][j] != '\0'; ++j) {
			if(args[1][j] == '?')
				break;
		}
		for(i = 0; i < global->no_interfaces; ++i) {
			LOCK(global->interfaces[i]);
			if(global->interfaces[i]->deleted == 0 &&
				(j == 0 ||
				 strncmp(global->interfaces[i]->name,args[1],j) == 0))
				admin_msg(ac,"%s",global->interfaces[i]->name);
			UNLOCK(global->interfaces[i]);
		}
	} else if(no_args > 2) {
		admin_msg(ac,"SEND");
		admin_msg(ac,"RECV");
		admin_msg(ac,"CHAT");
	}	
	UNLOCK(global);
}

MENU_CMD(cmd_interface_makedefault) {
	interface_t *intf = (interface_t *)ins->data;
	interfacetype_t type;
	int i;
	LOCK(intf);
	type = intf->type;
	if(intf->def != 0) {
		UNLOCK(intf);
		admin_msg(ac,"Already Default Set as Default %s Interface",
				interfacetype_to_str(type));
		return 0;
	} else if(intf->deleted != 0) {
		UNLOCK(intf);
		return 0;
	}
	UNLOCK(intf);
	LOCK(global);
	for(i = 0; i < global->no_interfaces; ++i) {
		if(global->interfaces[i] != intf) {
			LOCK(global->interfaces[i]);
			if(global->interfaces[i]->type == type &&
				global->interfaces[i]->def != 0 &&
				global->interfaces[i]->deleted != 0)
				global->interfaces[i]->def = 0;
			UNLOCK(global->interfaces[i]);
		}
	}
	LOCK(intf);
	intf->def = 1;
	UNLOCK(intf);
	UNLOCK(global);
	admin_msg(ac,"Set as Default %s Interface",interfacetype_to_str(type));
	return 0;
}

MENU_CMD(cmd_interface_rename) {
	interface_t *intf = (interface_t *)ins->data;
	char buffer[96];

	if(interface_rename(intf,args[1]) == 0) {
		LOCK(intf);
		snprintf(buffer,sizeof(buffer),"interface: %s (%s)",
				intf->name,interfacetype_to_str(intf->type));
		UNLOCK(intf);
		if(ins->prompt != NULL && ins->free_prompt != 0)
			xfree(ins->prompt);
		ins->prompt = xstrdup(buffer);
		ins->free_prompt = 1;
	
		admin_msg(ac,"Renamed Interface to %s",args[1]);
		admin_output_prompt(ac);
	} else
		admin_msg(ac,"Failed Renamed to %s",args[1]);

	return 0;
}

MENU_CMD(cmd_interface_settings) {
	interface_t *intf = (interface_t *)ins->data;

	if(no_args == 1) {
		char buffer[256];
		pqueue_t settings;
		pqueue_init(&settings,0);
		interface_read_settings(intf,&settings);
		
		LOCK(intf);
		snprintf(buffer,sizeof(buffer),
				"-- Interface Settings for %s (%s) --",
				intf->name,interfacetype_to_str(intf->type));
		UNLOCK(intf);
		output_settings(ac,buffer,NULL,&settings);
		
	} else if(no_args >= 3) {
		if(interface_apply_setting(intf,args[1],args[2]) == -1)
			admin_msg(ac,"Invalid setting: %s",args[1]);
	} else
		admin_msg(ac,"%s [<setting> <value>]",args[0]);
	
	return 0;
}

MENU_CMD(cmd_interface_save) {
	interface_t *intf = (interface_t *)ins->data;
	int ret;
	if(is_int_new(intf)) {
		ret = interface_add(intf);
		if(ret != -1) {
			admin_msg(ac,"Successfully Saved.");
			ins->free = NULL;
		} else
			admin_msg(ac,"Save Failed.");
	} else
		admin_msg(ac,"Interface is already saved");
	return 0;
}

MENU_CMD(cmd_interface_delete) {
	interface_t *intf = (interface_t *)ins->data;
	int done = 1;
	if(!is_int_new(intf)) {
		interface_del(intf);
		LOCK(intf);
		if(intf->deleted != 0)
			admin_msg(ac,"Interface %s Deleted",intf->name);
		else {
			done = 0;
			admin_msg(ac,"Interface %s Deletion not possible",intf->name);
		}
		UNLOCK(intf);
	}
	if(done)
		return RET_UP;
	else
		return 0;
}

MENU_CMD(cmd_interface_info) {
	interface_t *intf = (interface_t *)ins->data;
	char tmp[16];
	float f;
	int i,j,k;
	
	LOCK(intf);
	admin_msg(ac,"-- Interface %s --",intf->name);
	admin_msg(ac,"Type: %s (%s)",interfacetype_to_str(intf->type),
		intf->def ? "Default" : "Not Default");
	admin_msg(ac,"State: %s",
		thread_is_running(&(intf->thread)) ? "Running" : "Offline");
	
	for(i = 0, f = 0.0; i < AVGSPEED; ++i)
		f += (float)intf->avgspeed[i];
	f /= ((float)AVGSPEED) * 1024.0;
	if(intf->bandwidth != 0)
		admin_msg(ac,"Current Rate: %.2f KiB/sec (Limit: %ld, Samples: %d)",
				f,intf->bandwidth,AVGSPEED);
	else
		admin_msg(ac,"Current Rate: %.2f KiB/sec (Samples: %d)",
				f,AVGSPEED);
	
	admin_msg(ac,"Record Rate: %.2f KiB/sec",
			((float)intf->record_rate)/1024.0);
	admin_msg(ac,"Total Transferred: %s",
			format_bytes(tmp,sizeof(tmp),
				((unsigned long long)intf->stat_mib)*1024*1024
				+ (unsigned long long)intf->stat_carry,1));
	
	if(intf->type == INTERFACE_SEND) {
		for(i = 0,j = 0,k = 0; i < intf->send.no_pools; ++i) {
			LOCK(intf->send.pools[i]);
			if(intf->send.pools[i]->deleted == 0) {
				j++;
				k += intf->send.pools[i]->no_csends;
			}
			UNLOCK(intf->send.pools[i]);
		}
		admin_msg(ac,"Number of Pools: %d",j);
		admin_msg(ac,"Current Sends: %d",k);
	} else if(intf->type == INTERFACE_RECV) {
		admin_msg(ac,"Current Uploads: %d",intf->recv.no_uploads);
	} else if(intf->type == INTERFACE_CHAT) {
		admin_msg(ac,"Current Chats: %d",intf->chat.no_chats);
	}
	UNLOCK(intf);
	
	return 0;
}

static void cmd_interface_display_upload(acontext_t *ac, send_t *u, time_t now) {
	char buffer2[16],buffer3[16];
	float f;
	int i;

	LOCK(u);
	RLOCK(u->pack);
	for(i = 0, f = 0.0; i < AVGSPEED; ++i)
		f += (float)u->avgspeed[i];
	f /= ((float)AVGSPEED) * 1024.0;
	
	if(u->network != NULL)
		LOCK(u->network); // NOTE: Special case
	admin_cmsg(ac,"%%r-%%n From: [%%9%s:%s%%n] (%s)",
			u->network != NULL ? u->network->name : "Unknown",
			u->nick != NULL ? u->nick : "Unknown",u->host);
	if(u->network != NULL)
		UNLOCK(u->network);
	admin_cmsg(ac,"  Rate:  %%9%.2f KiB/sec%%n, State %s, Elp:%s Rem:%s (%ds)",
			f,send_state_text(u->state),
			u->connected == 0 ? "-" :
			 etimestr((now - u->connected),buffer2,sizeof(buffer2)),
			f > 0.0 && u->pos != u->pack->size ?
			 etimestr(
				 (u->pack->size - u->pos) /
				 (off_t)((f*1024.0) >= 1.0 ? (f*1024.0) : 1.0),
				 buffer3,sizeof(buffer3)) 
			 : "-",
			 u->last_contact != 0 ? now - u->last_contact : 0);
	admin_cmsg(ac,"  File:  %%9%s%%n (%ld/%ldKiB)",
			u->pack->file,
			(long)u->pos/1024,(long)u->pack->size/1024);
	RUNLOCK(u->pack);
	UNLOCK(u);
}

MENU_CMD(cmd_interface_access) {
	interface_t *i = (interface_t *)ins->data;
	char title[128];
	access_t *acc;
	
	LOCK(i);
	if(i->type != INTERFACE_RECV) {
		UNLOCK(i);
		admin_msg(ac,"Only available on %s type interfaces.",
				interfacetype_to_str(INTERFACE_RECV));
		return RET_INV_CMD;
	}
	snprintf(title,sizeof(title),"Interface %s",i->name);
	if(i->recv.access == NULL && no_args > 1) {
		i->recv.access = alloc_access();
		LOCK(i->recv.access);
		i->recv.access->flags = ACCESS_DENY;
		UNLOCK(i->recv.access);
	}
	acc = i->recv.access;
	UNLOCK(i);
	
	return cmd_access_list(ac,acc,title,args,no_args);
}

MENU_CMD(cmd_interface_uploads) {
	interface_t *intf = (interface_t *)ins->data;
	int i;

	LOCK(intf);
	if(intf->type != INTERFACE_RECV) {
		UNLOCK(intf);
		admin_msg(ac,"Only available on %s type interfaces.",
				interfacetype_to_str(INTERFACE_RECV));
		return -1;
	} else
		UNLOCK(intf);
	
	if(no_args == 1) {
		time_t now = xtime();
		LOCK(intf);
		admin_msg(ac,"-- Uploads on Interface %s --",intf->name);
		for(i = 0; i < intf->recv.no_uploads; ++i)
			cmd_interface_display_upload(ac,
					intf->recv.uploads[i],now);
		if(i == 0)
			admin_msg(ac,"None");
		UNLOCK(intf);
	} else if(tolower(args[1][0]) == 'c' && no_args >= 3) {
		if(no_args >= 4) {
			i = interface_upload_close(intf,args[2],NULL,
					args[3],NULL,NULL,NULL);
			i += interface_upload_close(intf,args[2],NULL,
					NULL,args[3],NULL,NULL);
		} else
			i = interface_upload_close(intf,args[2],NULL,NULL,
					NULL,NULL,NULL);
		admin_msg(ac,"Closed %d Matching Uploads",i);
	} else
		admin_msg(ac,"%s [close <hostmask> [<nick>|<file>]]",args[0]);
	
	return 0;
}

static void cmd_interface_display_chat(acontext_t *ac, chat_t *c, time_t now) {
	char buffer1[32];
	
	LOCK(c);
	if(c->network != NULL)
		LOCK(c->network);
	admin_cmsg(ac,"%%r-%%n To: [%%9%s:%s%%n] Type: %s, State: %s",
			c->network != NULL ? c->network->name : "Unknown",
			c->nick != NULL ? c->nick : "Unknown",
			c->type == CHAT_LIST ? "LIST" : "ADMIN",
			send_state_text(c->state));
	if(c->network != NULL)
		UNLOCK(c->network);
	admin_msg(ac,"  Started: %s, Last Contact: %d Seconds",
			timestr(c->started,buffer1,sizeof(buffer1)),
			c->last_contact != 0 ? now - c->last_contact : 0);
	UNLOCK(c);
}

MENU_CMD(cmd_interface_chats) {
	interface_t *intf = (interface_t *)ins->data;
	int i;

	LOCK(intf);
	if(intf->type != INTERFACE_CHAT) {
		UNLOCK(intf);
		admin_msg(ac,"Only available on %s type interfaces.",
				interfacetype_to_str(INTERFACE_CHAT));
		return -1;
	} else
		UNLOCK(intf);
	
	if(no_args == 1) {
		time_t now = xtime();
		LOCK(intf);
		admin_msg(ac,"-- Chats on Interface %s --",intf->name);
		for(i = 0; i < intf->chat.no_chats; ++i)
			cmd_interface_display_chat(ac,intf->chat.chats[i],now);
		if(i == 0)
			admin_msg(ac,"None");
		UNLOCK(intf); 
	} else if(tolower(args[1][0]) == 'c' && no_args >= 3) {
		i = interface_chat_close(intf,args[2],NULL,
				no_args >= 4 ? args[3] : NULL,NULL,NULL);
		admin_msg(ac,"Closed %d Matching Chats",i);
	} else
		admin_msg(ac,"%s [close <hostmask> [<nick>]]",args[0]);
	
	return 0;
}

/** Interface Menu Structures **/

static const cmd_t interface_menu[] = {
	{"access",	cmd_interface_access,	0,-1,1,0,PRIVS_EMPTY,NULL},
	{"chats",	cmd_interface_chats,	0,3,1,0,PRIVS_EMPTY,NULL},
	{"delete",	cmd_interface_delete,	0,0,1,0,PRIVS_EMPTY,NULL},
	{"info",	cmd_interface_info,	0,0,0,0,PRIVS_EMPTY,NULL},
	{"rename",	cmd_interface_rename,	1,1,1,0,PRIVS_EMPTY,NULL},
	{"save",	cmd_interface_save,	0,0,1,0,PRIVS_EMPTY,NULL},
	{"makedefault",	cmd_interface_makedefault,0,0,1,0,PRIVS_EMPTY,NULL},
	{"settings",	cmd_interface_settings,	0,2,1,0,PRIVS_EMPTY,NULL},
	{"uploads",	cmd_interface_uploads,	0,3,1,0,PRIVS_EMPTY,NULL},
	{NULL}
};

/** Network Menu Entry **/
MENU_CHECK(cmd_interface_check) {
	interface_t *i = (interface_t *)ins->data;
	int ret = 0;
	LOCK(i);
	if(i->deleted != 0) {
		admin_msg(ac,"Interface %s, Deleted.",i->name);
		ret = -1;
	}
	UNLOCK(i);
	return ret;
}

MENU_CMD(cmd_interface_entry) {
	char buffer[96];
	interface_t *intf;
	int new = 0;
	
	intf = interface_find(args[1]);
	if(intf == NULL) {
		if(no_args >= 3) {
			if(strcasecmp(args[2],"send") == 0) 
				intf = alloc_interface(INTERFACE_SEND);
			else if(strcasecmp(args[2],"recv") == 0)
				intf = alloc_interface(INTERFACE_RECV);
			else if(strcasecmp(args[2],"chat") == 0)
				intf = alloc_interface(INTERFACE_CHAT);
			if(intf != NULL) {
				new = 1;
				intf->name = xstrdup(args[1]);
				admin_msg(ac,"New Interface: %s",args[1]);
			} else
				admin_msg(ac,"%s <name> [SEND|RECV|CHAT]",args[0]);
		} else
			admin_msg(ac,"%s <name> [SEND|RECV|CHAT]",args[0]);
	}
	if(intf != NULL) {
		LOCK(intf);
		if(intf->type == INTERFACE_SEND) {
			snprintf(buffer,sizeof(buffer),
					"interface: %s (SEND)",intf->name);
		} else if(intf->type == INTERFACE_RECV) {
			snprintf(buffer,sizeof(buffer),
					"interface: %s (RECV)",intf->name);
		} else /*if(intf->type == INTERFACE_CHAT)*/ {
			snprintf(buffer,sizeof(buffer),
					"interface: %s (CHAT)",intf->name);
		}
		UNLOCK(intf);
		if(ins->prompt != NULL && ins->free_prompt)
			xfree(ins->prompt);
		ins->prompt		= xstrdup(buffer);
		ins->free_prompt 	= 1;
		ins->check 		= cmd_interface_check;
		ins->data 		= intf;
		if(new)
			ins->free = (void (*)(void *))free_interface;
		return 0;
	} else
		return -1;
}

static void register_interface_menu(void) {
	menu_t *intf_menu = menu_from_cmds(
			"interface",			// name
			1,				// min_args
			2,				// max_args
			1,				// log
			0,				// hide
			PRIV_TO_MASK(PRIV_INTERFACES),	// privs
			cmd_interface_entry,		// func
			cmd_interfaces_helper,		// helper
			(cmd_t *)interface_menu);
	
	register_pool_menu(intf_menu);
	menu_add(main_menu_root,intf_menu);
	menu_deref(intf_menu);
}
