/*
 * astconfig: Asterisk Configuration Tool
 *
 * Configure Asterisk
 *
 * Copyright (C) 2002 Linux Support Services, Inc.
 *
 */
 
#include <stdlib.h>
#include <unistd.h>
#include <dlfcn.h>
#include <dirent.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <sys/stat.h>

#include <gtk/gtk.h>
#include <asterisk/astconfig.h>
#include <asterisk/config.h>

#include "asterisk.xpm"

#define MODULES_DIR "/usr/lib/asterisk/config-modules"
#define KEY_DIR "/var/lib/asterisk/keys"


static struct config_module *mods = NULL;

static int modified_things=0;

static void save_changes()
{
	fprintf(stderr, "!!! Save changes... !!!\n");
}

static char *yesno[2] = { "Yes", "No" };
static char *ok[1] = { "OK" };
static char *okcancel[2] = { "OK", "Cancel" };

static FILE *open_config_file(char *filename, char *mode)
{
	char fn[256];
	char *home = getenv("HOME");
	if (!home)
		home="/etc";
	snprintf(fn, sizeof(fn), "%s/.astconfig", home);
	mkdir(fn, 0755);
	snprintf(fn, sizeof(fn), "%s/.astconfig/%s", home, filename);
	return fopen(fn, mode);
	
}

static int check_entity(char *entity)
{
	FILE *f;
	char buf[256];
	int res = 0;
	f = open_config_file("confirms.txt", "r");
	while(f && !feof(f)) {
		fgets(buf, sizeof(buf), f);
		if (!feof(f) && strlen(buf)) {
			/* Trim trailing \n */
			buf[strlen(buf) -1] = '\0';
			if (!strcmp(entity, buf)) {
				res = 1;
				break;
			}
		}
		
	}
	if (f) fclose(f);
	return res;
}

static int add_entity(char *entity)
{
	FILE *f;
	f = open_config_file("confirms.txt", "a");
	if (f) {
		fprintf(f, "%s\n", entity);
		fclose(f);
	}
	return 0;
}

static GtkWidget *dialog;

static int choice = -1;
static int remember = 0;

static GtkWidget *mbox;	/* Place to but buttons */

static GtkWidget *ew;	/* Button for the "Don't ask again" */

static GtkWidget *descrip; /* Box for description to show */

static GtkWidget *execute; /* Click to run module */

void dialog_answer(GtkWidget *widget, gpointer data)
{
	if (ew && gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(ew))) {
		remember = 1;
	}
	gtk_widget_destroy(dialog);
	dialog = NULL;
	choice = (int)(long)data;
}

static GtkWidget *window;

int dialog_ask_maybe(GtkWidget *window, char *title, char *text, char *entity, int choices, int def, char *ctext[])
{
	GtkWidget *button;
	GtkWidget *tw, *defw;
	char rtext[256];
	int x;

	choice = -1;
	remember = 0;
	if (entity) {
		if (check_entity(entity)) {
			printf("Using default answer for '%s'\n", entity);
			return def;
		}
	}
	if (!choices) {
		/* Reasonable defaults */
		choices = 1;
		ctext = ok;
	}
	snprintf(rtext, sizeof(rtext),"AstConfig: %s", title);
	dialog = gtk_dialog_new();
	tw = gtk_label_new(text);
	gtk_window_set_title(GTK_WINDOW(dialog), rtext);
	gtk_widget_show(tw);
	gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox), tw, TRUE, TRUE, 5);
	if (entity) {
		ew = gtk_check_button_new_with_label("Do not ask me again");
		gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox), ew, TRUE, TRUE, 5);
		
	} else
		ew = NULL;
	for (x=0;x<choices;x++) {
		button = gtk_button_new_with_label(ctext[x]);
		if (x == def)
			defw = button;
		gtk_widget_set_usize(button, 80, 30);
		gtk_widget_show(button);
		gtk_signal_connect(GTK_OBJECT(button), "clicked", dialog_answer, (gpointer)(long)x);
		gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->action_area), button, FALSE, FALSE, 10);
	}
	if (defw)
		gtk_widget_grab_focus(defw);
	gtk_widget_set_sensitive(window, FALSE);
	gtk_widget_show_all(dialog);
	/* Block as long as dialog is here */
	while(dialog) 
		gtk_main_iteration_do(1);
	if (remember) {
		if (!check_entity(entity))
			add_entity(entity);
	}
	gtk_widget_set_sensitive(window, TRUE);
	return choice;
}

int dialog_ask(GtkWidget *window, char *title, char *text, int choices, int def, char *ctext[])
{
	return dialog_ask_maybe(window, title, text, NULL, choices, def, ctext);
}

int dialog_yesno(GtkWidget *window, char *title, char *text, int defaultyes)
{
	if (!dialog_ask(window, title, text, 2, defaultyes ? 0 : 1, yesno))
		return 1;
	return 0;
}

int dialog_confirm(GtkWidget *window, char *title, char *text, int defaultyes, char *entity)
{
	if (!dialog_ask_maybe(window, title, text, entity, 2, defaultyes ? 0 : 1, yesno))
		return 1;
	return 0;
}

GtkWidget *widget_listbox(int argc, char *argv[]) 
{
	GtkWidget *amaflags;
	GList *l;
	int x;
	amaflags = gtk_combo_new();
	l = NULL;	
	for (x=0;x<argc; x++)
		l = g_list_append(l, argv[x]);
	gtk_combo_set_popdown_strings(GTK_COMBO(amaflags), l);
	gtk_editable_set_editable(GTK_EDITABLE(GTK_COMBO(amaflags)->entry), FALSE);
	return amaflags;
}

static char *amaflags[] = { "", "default", "omit", "billing", "documentation" };

GtkWidget *widget_amaflags(void)
{
	return widget_listbox(5, amaflags);
}


static GtkWidget *widget_list(GList *l, int list)
{
	GtkWidget *w;
	GtkWidget *f;
	if (list) {
		w = gtk_list_new();
		gtk_list_append_items(GTK_LIST(w), l);
		gtk_widget_set_usize(w, 100, 20);
		f = gtk_frame_new(NULL);
		gtk_frame_set_shadow_type(GTK_FRAME(f), GTK_SHADOW_IN);
		gtk_container_add(GTK_CONTAINER(f), w);
		w = f;
	} else {
		w = gtk_combo_new();
		gtk_combo_set_popdown_strings(GTK_COMBO(w), l);
	}
	return w;
}

GtkWidget *widget_contexts(int list)
{
	GList *l = NULL;
	struct ast_config *cfg;
	char *cat;
	if (!list)
		l = g_list_append(l, "");
	cfg = ast_load("extensions.conf");
	if (cfg) {
		cat = ast_category_browse(cfg, NULL);
		while((cat = ast_category_browse(cfg, cat))) {
			l = g_list_append(l, strdup(cat));
		}
		ast_destroy(cfg);
	}
	return widget_list(l, list);
}

GtkWidget *widget_keys(int ktype, int list)
{
	DIR *d;
	struct dirent *de;
	GList *l = NULL;
	if (!list)
		l = g_list_append(l, "");
	d = opendir(KEY_DIR);
	if (d) {
		while((de = readdir(d))) {
			if (strlen(de->d_name) > 4) {
				char *ext = de->d_name + strlen(de->d_name) - 4;
				if ((!strcasecmp(ext, ".pub") && (ktype & KEY_PUBLIC)) ||
					(!strcasecmp(ext, ".key") && (ktype & KEY_PRIVATE))) {
					*ext = '\0';
					if (list)
						l = g_list_append(l, gtk_list_item_new_with_label(de->d_name));
					else
						l = g_list_append(l, strdup(de->d_name));
					
				}
			}
		}
		closedir(d);
	}
	return widget_list(l, list);
}

static int ask_exit(void)
{
	if (dialog_confirm(window, "Quit?", "Are you sure you want to quit?", 1, "Confirm Exit")) {
		if (modified_things && dialog_yesno(window, "Save Changes?", "Save configuration changes?", 1)) 
			save_changes();
		gtk_exit(0);
	}
	return 1;
}

GtkWidget *make_pixmap_from_data(char **xpmdata)
{
	GdkPixmap *pm;
	GdkBitmap *bm;
	GtkWidget *pixmap;
	pm = gdk_pixmap_create_from_xpm_d(window->window, &bm, NULL, xpmdata);
	pixmap = gtk_pixmap_new(pm, bm);
	gdk_pixmap_unref(pm);
	gdk_bitmap_unref(bm);
	return pixmap;
}

static struct config_module *current;

static void configure_click(GtkWidget *w, void *data)
{
	if (current) {
		gtk_widget_set_sensitive(window, FALSE);
		current->launch();
		gtk_widget_set_sensitive(window, TRUE);
	}
}

static void module_click(GtkWidget *w, void *data)
{
	struct config_module *mod;
	int tmppos=0;

	mod = data;
	current = mod;
	if (mod->descrip) {
		gtk_editable_delete_text(GTK_EDITABLE(descrip), 0, -1);
		gtk_editable_insert_text(GTK_EDITABLE(descrip), mod->descrip, strlen(mod->descrip), &tmppos);
	} else
		fprintf(stderr, "Module '%s' lacks description\n", mod->title);
		
	gtk_widget_set_sensitive(execute, TRUE);
}

int module_register(struct config_module *mod)
{
	GtkWidget *button;
	GtkWidget *pixmap;
	GtkWidget *hbox;
	GtkWidget *label;
	printf("Registering module '%s'\n", mod->title);
	label = gtk_label_new(mod->title);
	button = gtk_button_new();
	hbox = gtk_hbox_new(0, 0);
	pixmap = make_pixmap_from_data(mod->xpmdata);
	gtk_box_pack_start(GTK_BOX(hbox), pixmap, FALSE, FALSE, 10);
	gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 10);
	gtk_container_set_border_width(GTK_CONTAINER(hbox), 5);
	gtk_container_add(GTK_CONTAINER(button), hbox);
	gtk_widget_set_usize(label, 0, 40);
	gtk_box_pack_start(GTK_BOX(mbox), button, FALSE, FALSE, 0);
	gtk_signal_connect(GTK_OBJECT(button), "clicked", module_click, mod);
	gtk_widget_show_all(button);
	mod->next = mods;
	mods = mod;
	return 0;
}

static void build_gui(void)
{
	GtkWidget *vbox;
	GtkWidget *bbox;
	GtkWidget *hbox;
	GtkWidget *quit;
	GtkWidget *frame;
	GtkWidget *scroll;
	GtkWidget *vbox2;
	GtkWidget *hbox2;
	GtkWidget *hbox3;
	GtkWidget *logo;

	window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
	gtk_widget_realize(window);

	vbox = gtk_vbox_new(0, 10);
	hbox = gtk_hbox_new(0, 0);
	bbox = gtk_hbox_new(0, 10);
	vbox2 = gtk_vbox_new(0, 10);
	hbox2 = gtk_hbox_new(0, 10);
	hbox3 = gtk_hbox_new(0, 10);
	gtk_box_pack_start(GTK_BOX(vbox), hbox, TRUE, TRUE, 0);

	scroll = gtk_scrolled_window_new(NULL, NULL);
	mbox = gtk_vbox_new(0,0);
	gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(scroll), mbox);
	frame = gtk_frame_new("Configuration Modules");

	gtk_container_set_border_width(GTK_CONTAINER(scroll), 10);
	gtk_container_add(GTK_CONTAINER(frame), scroll);
	gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scroll), GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
	gtk_widget_set_usize(frame, 300, 0);

	gtk_box_pack_start(GTK_BOX(hbox), frame, FALSE, FALSE, 10);

	descrip = gtk_text_new(NULL, NULL);
	gtk_text_set_editable(GTK_TEXT(descrip), FALSE);
	gtk_text_set_word_wrap(GTK_TEXT(descrip), TRUE);
	
	execute = gtk_button_new_with_label("Configure...");
	gtk_signal_connect(GTK_OBJECT(execute), "clicked", configure_click, NULL);
	gtk_widget_set_sensitive(execute, FALSE);
	gtk_widget_set_usize(execute, 120, 35);
	
	logo = make_pixmap_from_data(asterisk_xpm);
	
	gtk_box_pack_end(GTK_BOX(hbox3), logo, FALSE, FALSE, 0);
	gtk_box_pack_start(GTK_BOX(vbox2), hbox3, FALSE, FALSE, 0);
	gtk_box_pack_start(GTK_BOX(vbox2), descrip, TRUE, TRUE, 0);
	gtk_box_pack_start(GTK_BOX(vbox2), hbox2, FALSE, FALSE, 0);
	gtk_box_pack_end(GTK_BOX(hbox2), execute, FALSE, FALSE, 0);
	gtk_widget_set_usize(descrip, 200, 250);
	gtk_container_set_border_width(GTK_CONTAINER(vbox2), 5);
	gtk_box_pack_start(GTK_BOX(hbox), vbox2, TRUE, TRUE, 0);

	gtk_box_pack_start(GTK_BOX(vbox), gtk_hseparator_new(), FALSE, FALSE, 0);
	gtk_box_pack_end(GTK_BOX(vbox), bbox, FALSE, FALSE, 0);

	quit = gtk_button_new_with_label("Quit");
	gtk_widget_set_usize(quit, 80, 30);
	gtk_box_pack_end(GTK_BOX(bbox), quit, FALSE, FALSE, 0);

	gtk_widget_set_usize(window, 640, 480);
	gtk_widget_show_all(vbox);
	gtk_signal_connect(GTK_OBJECT(window), "delete_event", GTK_SIGNAL_FUNC(ask_exit), NULL);
	gtk_signal_connect(GTK_OBJECT(quit), "clicked", GTK_SIGNAL_FUNC(ask_exit), NULL);
	gtk_container_set_border_width(GTK_CONTAINER(bbox), 10);
	gtk_container_set_border_width(GTK_CONTAINER(window), 10);

	gtk_container_add(GTK_CONTAINER(window), vbox);
	
	gtk_window_set_title(GTK_WINDOW(window), "AstConfig: The Asterisk Configuration Tool");
}


static void show_gui(void)
{
	gtk_widget_show(window);
}

static int load_module(char *modname)
{
	char fn[256];
	void *dl;
	int (*mod_init)(void);
	snprintf(fn, sizeof(fn), "%s/%s", MODULES_DIR, modname);
	dl = dlopen(fn, RTLD_NOW);
	if (!dl) {
		fprintf(stderr, "Unable to open module '%s': %s\n", fn, dlerror());
		return -1;
	}
	mod_init = dlsym(dl, "mod_init");
	if (!mod_init) {
		fprintf(stderr, "Warning: module '%s' lacks init\n", fn);
	}
	if (mod_init()) {
		fprintf(stderr, "Warning: module '%s' failed to load properly\n", fn);
	}
	return 0;
}

static int load_modules(void)
{
	DIR *d;
	struct dirent *ent;
	d = opendir(MODULES_DIR);
	if (!d) {
		fprintf(stderr, "Unable to open modules directory %s\n", MODULES_DIR);
		return -1;
	}
	while((ent = readdir(d))) {
		if (strlen(ent->d_name) > 3) {
			if (!strcasecmp(ent->d_name + strlen(ent->d_name) - 3, ".so")) 
				load_module(ent->d_name);
		}
	}
	closedir(d);
	return 0;
	
}

int main(int argc, char *argv[])
{
	gtk_init(&argc, &argv);
	build_gui();
	if (load_modules())
		exit(1);
	show_gui();
	gtk_main();
	exit(0);
}
