/* ARISA - Admin Interface - Queue 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_queue_new(queue_t *q) {
	int i;
	LOCK(global);
	for(i = 0; i < global->no_queues; ++i) {
		if(global->queues[i] == q) {
			UNLOCK(global);
			return 0;
		}
	}
	UNLOCK(global);
	return 1;
}

/** Queue Menu Commands **/

MENU_HELPER(cmd_queues_helper) {
	int i,j;
	LOCK(global);
	for(j = 0; args[no_args-1][j] != '\0'; ++j) {
		if(args[no_args-1][j] == '?')
			break;
	}
	for(i = 0; i < global->no_queues; ++i) {
		LOCK(global->queues[i]);
		if(global->queues[i]->deleted == 0 &&
			(j == 0 ||
			 strncmp(global->queues[i]->name,args[no_args-1],j) == 0))
			admin_msg(ac,"%s",global->queues[i]->name);
		UNLOCK(global->queues[i]);
	}
	UNLOCK(global);
}

MENU_CMD(cmd_queue_delete) {
	queue_t *q = (queue_t *)ins->data;
	int done = 1;
	if(!is_queue_new(q)) {
		queue_del(q);
		LOCK(q);
		if(q->deleted != 0)
			admin_msg(ac,"Queue %s deleted.",q->name);
		else {
			done = 0;
			admin_msg(ac,"Queue %s deletion not possible.",q->name);
		}
		UNLOCK(q);
	}
	if(done)
		return RET_UP;
	else
		return 0;
}

MENU_CMD(cmd_queue_rename) {
	queue_t *q = (queue_t *)ins->data;
	char buffer[96];
	
	if(queue_rename(q,args[1]) == 0) {
		if(ins->prompt != NULL && ins->free_prompt != 0)
			xfree(ins->prompt);
		snprintf(buffer,sizeof(buffer),"queue: %s",args[1]);
		ins->prompt = xstrdup(buffer);
		ins->free_prompt = 1;

		admin_output_prompt(ac);
	} else
		admin_msg(ac,"Failed to rename to %s.",args[1]);

	return 0;
}

MENU_CMD(cmd_queue_save) {
	queue_t *q = (queue_t *)ins->data;
	int ret;
	if(is_queue_new(q)) {
		LOCK(q);
		if(q->pool != NULL) {
			LOCK(q->pool);
			if(q->pool->deleted != 0) {
				UNLOCK(q->pool);
				q->pool = NULL;
			} else
				UNLOCK(q->pool);
		}
		if(q->pool != NULL) {
			UNLOCK(q);
			ret = queue_add(q);
			if(ret == 0) {
				ins->free = NULL;
				LOCK(q);
				LOCK(q->pool);
				if(q->pool->deleted != 0) {
					UNLOCK(q->pool);
					UNLOCK(q);
					queue_del(q);
					admin_msg(ac,"Pool was deleted as queue was saved, queue has now also been deleted.");
					return RET_UP;
				}
				UNLOCK(q->pool);
				UNLOCK(q);
				admin_msg(ac,"Successfully saved.");
			} else
				admin_msg(ac,"Save failed.");
		} else {
			UNLOCK(q);
			admin_msg(ac,"Unable to save no pool set.");
		}
	} else
		admin_msg(ac,"Queue is already saved.");
	return 0;
}

MENU_CMD(cmd_queue_sends) {
	queue_t *q = (queue_t *)ins->data;
	int ret = RET_EXE_ERR;
	LOCK(q);
	if(q->pool != NULL)
		ret = cmd_pool_sends_p(q->pool,ac,args,no_args);
	else
		admin_msg(ac,"No Pool Set");
	UNLOCK(q);
	return ret;
}

MENU_CMD(cmd_queue_settings) {
	queue_t *q = (queue_t *)ins->data;
	
	if(no_args == 1) {
		pqueue_t settings;
		pqueue_init(&settings,0);
		
		LOCK(q);
		if(q->pool != NULL) {
			char buffer[256], iname[128], pname[128];
			pool_t *p = q->pool;
			int i,j,found;
			
			LOCK(p);
			xstrncpy(pname,p->name,sizeof(pname));
			UNLOCK(p);
			UNLOCK(q);
			
			LOCK(global);
			for(i = 0, found = 0; i < global->no_interfaces
					&& !found; ++i) {
				interface_t *intf = global->interfaces[i];
				LOCK(intf);
				if(intf->type == INTERFACE_SEND) {
					for(j = 0; j < intf->send.no_pools && !found; ++j) {
						if(intf->send.pools[j] == p) {
							xstrncpy(iname,intf->name,sizeof(iname));
							found = 1;
						}
					}
				}
				UNLOCK(intf);
			}
			UNLOCK(global);
			
			assert(found != 0);	
			
			snprintf(buffer,sizeof(buffer),"%s %s",iname,pname);	
			pqueue_push_back(&settings,
					value_string("pool",buffer));
		} else {
			UNLOCK(q);
			pqueue_push_back(&settings,
					value_string("pool","not set"));
		}
		
		queue_read_settings(q,&settings);
		
		LOCK(q);
		output_settings(ac,"-- Queue Settings for %s --",
				q->name,&settings);
		UNLOCK(q);
	} else if(no_args >= 3) {
		if(strncasecmp(args[1],"po",2) == 0) {
			if(no_args >= 3) {
				interface_t *intf = interface_find(args[2]);
				if(intf != NULL && no_args >= 4) {
					pool_t *p = pool_find(intf,args[3]);
					if(p != NULL) {
						LOCK(q);
						LOCK(p);
						if(p->deleted == 0) {
							q->pool = p;
							admin_msg(ac,"Pool set.");
						} else {
							admin_msg(ac,"Invalid pool %s.",args[3]);
						}
						UNLOCK(p);
						UNLOCK(q);
					} else if(strchr(args[3],'?') != NULL) {
						cmd_int_pools_helper(ins,ac,args+1,no_args-1);
					} else {
						admin_msg(ac,"Invalid pool %s.",args[3]);
					}
				} else if(strchr(args[2],'?') != NULL) {
					cmd_int_pools_helper(ins,ac,args+1,no_args-1);
				} else {
					admin_msg(ac,"Invalid interface %s.",args[2]);
				}
			} else {
				admin_msg(ac,"%s pool <interface> <pool>",args[0]);
			}
		} else {
			if(queue_apply_setting(q,args[1],args[2]) == -1)
				admin_msg(ac,"Invalid setting: %s",args[1]);
		}
	} else
		admin_msg(ac,"%s [<settings> <value>]",args[0]);
	return 0;
}

static void cmd_queue_show_info(acontext_t *ac, queue_t *q) {
	char buffer[32];

	LOCK(q);
	admin_msg(ac,"-- Queue %s --",q->name);
	admin_msg(ac,"Queues: %d/%d",q->no_qsends,q->length);
	admin_msg(ac,"Last Checked: %s",
			q->last_checked == 0 ? "Unknown" :
			timestr(q->last_checked,buffer,sizeof(buffer)));
	admin_msg(ac,"Last Dequeue: %s",
			q->last_dequeue == 0 ? "Unknown" :
			timestr(q->last_dequeue,buffer,sizeof(buffer)));
	admin_msg(ac,"Last Notify: %s",
			q->last_notify == 0 ? "Unknown" :
			timestr(q->last_notify,buffer,sizeof(buffer)));
	UNLOCK(q);
}

MENU_CMD(cmd_queue_info) {
	queue_t *q = (queue_t *)ins->data;
	if(no_args > 1) {
		if(tolower(args[1][0]) == 'p') {
			pool_t *p;
			LOCK(q);
			p = q->pool;
			UNLOCK(q);
			if(p != NULL)
				cmd_pool_show_info(ac,p);
		} else
			cmd_queue_show_info(ac,q);
	} else
		cmd_queue_show_info(ac,q);
	return 0;
}

static void cmd_queue_display_queue(acontext_t *ac, send_t *q, 
		int number, time_t now) {
	char buffer[256],buffer2[32];
	
	LOCK(q);
	RLOCK(q->pack);
	if(q->network != NULL)
		LOCK(q->network);
	admin_cmsg(ac,"%%r%-3d%%n For: [%%9%s:%s%%n] (%s)",
			number+1,
			q->network != NULL ? q->network->name : "Unknown",
			q->nick != NULL ? q->nick : "Unknown", q->host);
	if(q->network != NULL)
		UNLOCK(q->network);
	pathtofn(q->pack->file,buffer,sizeof(buffer));
	admin_cmsg(ac,"    File: %%9%s%%n (%lldKiB)",
			buffer,q->pack->size / 1024ULL);
	admin_msg(ac,"    Elp:%s, Retries: %d",
			etimestr(now - q->started,buffer2,sizeof(buffer2)),
			q->retries);
	RUNLOCK(q->pack);
	UNLOCK(q);
}

static int cmd_queue_queues_q(queue_t *q, acontext_t *ac,
		char **args, int no_args) {
	pool_t *p;
	send_t *s;
	char *ptr;
	int i,j;
	if(no_args == 1) {
		time_t now = xtime();
		LOCK(q);
		admin_msg(ac,"-- Queues on Queue %s --",q->name);
		for(i = 0; i < q->no_qsends; ++i)
			cmd_queue_display_queue(ac,q->qsends[i],i,now);
		if(i == 0)
			admin_msg(ac,"None");
		UNLOCK(q);
		return 0;
	}
	if(tolower(args[1][0]) == 'd' || strcasecmp(args[1],"cl") == 0) {
		if(no_args >= 3) {
			i = (int)strtol(args[2],&ptr,10);
			if(*ptr == '\0') {
				j = queue_remove_no(q,i);
				if(j == 0)
					admin_msg(ac,"Removed Queue %d",i);
				else
					admin_msg(ac,"Invalid Queue Number: %d",i);
			} else {
				if(no_args >= 4) {
					i = queue_remove(q,args[2],NULL,
						args[3],NULL,NULL,NULL);
					i += queue_remove(q,args[2],NULL,
						NULL,args[3],NULL,NULL);
					i += queue_remove(q,args[2],NULL,
						NULL,NULL,args[3],NULL);
				} else
					i = queue_remove(q,args[2],NULL,
						NULL,NULL,NULL,NULL);
				admin_msg(ac,"Removed %d Matching Queues",i);
				UNLOCK(q);
			}
		} else
			admin_msg(ac,"%s del <number> | <host> [<nick|file>]",args[0]);
	} else if(tolower(args[1][0]) == 'm' ||
			strncasecmp(args[1],"sw",2) == 0) {
		if(no_args >= 4) {
			i = atoi(args[2]) - 1;
			j = atoi(args[3]) - 1;
			if(tolower(args[1][0]) == 'm') {
				if(queue_send_move(q,i,j) != 0)
					admin_msg(ac,"Invalid Move: %d to %d",i,j);
			} else {
				if(queue_send_swap(q,i,j) != 0)
					admin_msg(ac,"Invalid Swap: %d to %d",i,j);
			}
		} else
			admin_msg(ac,"%s %s <src no> <dst no>",args[0],args[1]);
	} else if(strncasecmp(args[1],"ch",2) == 0) {
		queue_check_users(q);
	} else if(strncasecmp(args[1],"se",2) == 0) {
		if(no_args >= 3) {
			LOCK(q);
			i = atoi(args[2]);
			if(i > 0 && i <= q->no_qsends) {
				s = q->qsends[i-1];
				PTRARR_DEL(&q->qsends,&q->no_qsends,s);
				p = q->pool;
				UNLOCK(q);
				j = pool_add_send(p,s,SEND_FORCE,NULL);
				if(j == -1) {
					admin_msg(ac,"Error send deleted.");
					free_send(s);
				}
			} else {
				UNLOCK(q);
				admin_msg(ac,"Invalid Queue Number: %s",i);
			}
		} else
			admin_msg(ac,"%s send <queue number>",args[0]);
	} else
		admin_msg(ac,"%s [check|del|move|send|swap] ...",args[0]);
	return 0;
}

MENU_CMD(cmd_queue_queues) {
	queue_t *q = (queue_t *)ins->data;
	return cmd_queue_queues_q(q,ac,args,no_args);
}

/** Queue Menu Structure **/

static const cmd_t queue_menu[] = {
	{"delete",	cmd_queue_delete,	0,0,1,0,PRIVS_EMPTY,NULL},
	{"info",	cmd_queue_info,		0,1,0,0,PRIVS_EMPTY,NULL},
	{"queues",	cmd_queue_queues,	0,3,1,0,PRIVS_EMPTY,NULL},
	{"rename",	cmd_queue_rename,	1,1,1,0,PRIVS_EMPTY,NULL},
	{"save",	cmd_queue_save,		0,0,1,0,PRIVS_EMPTY,NULL},
	{"sends",	cmd_queue_sends,	0,4,1,0,
			PRIV_TO_MASK(PRIV_SEND_DEL),	NULL},
	{"settings",	cmd_queue_settings,	0,3,1,0,PRIVS_EMPTY,NULL},
	{NULL}
};

/** Queue Menu Entry **/
MENU_CHECK(cmd_queue_check) {
	queue_t *q = (queue_t *)ins->data;
	int ret = 0;
	LOCK(q);
	if(q->deleted != 0) {
		admin_msg(ac,"Queue %s, Deleted.",q->name);
		ret = -1;
	}
	UNLOCK(q);
	if(ret == 0 && is_queue_new(q)) {
		LOCK(q);
		if(q->pool != NULL) {
			LOCK(q->pool);
			if(q->pool->deleted != 0) {
				UNLOCK(q->pool);
				q->pool = NULL;
			} else
				UNLOCK(q->pool);
		}
		UNLOCK(q);
	}
	return ret;
}

MENU_CMD(cmd_queue_entry) {
	char buffer[96];
	queue_t *q;
	
	snprintf(buffer,sizeof(buffer),"queue: %s",args[1]);
	if(ins->prompt != NULL && ins->free_prompt)
		xfree(ins->prompt);
	ins->prompt	= xstrdup(buffer);
	ins->free_prompt= 1;
	ins->check 	= cmd_queue_check;
	
	q = queue_find(args[1]);
	if(q == NULL) {
		q = alloc_queue();
		q->name = xstrdup(args[1]);
		ins->free = (void (*)(void *)) free_queue;
		admin_msg(ac,"New Queue: %s",args[1]);
	}
	ins->data = q;
	
	return 0;
}

static void register_queue_menu(void) {
	menu_t *q_menu = menu_from_cmds(
			"queue",			// name
			1,				// min_args
			1,				// max_args
			1,				// log
			0,				// hide
			PRIV_TO_MASK(PRIV_QUEUES),	// privs
			cmd_queue_entry,		// func
			cmd_queues_helper,		// helper
			(cmd_t *)queue_menu);

	menu_add(main_menu_root,q_menu);
	menu_deref(q_menu);
}	
