/*
 * $RCSfile: ilink_signals.c,v $  $Revision: 1.32 $  $Name:  $
 * $Id: ilink_signals.c,v 1.32 2007/02/20 22:42:49 bpaauwe Exp $
 * $Author: bpaauwe $
 * $Date: 2007/02/20 22:42:49 $
 * ----------------------------------------------------------------------------
 *
 *  Copyright (c) Bob Paauwe (2006)
 *
 *  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
 *
 * ----------------------------------------------------------------------------
 *
 *  Description:
 *    Signal handlers
 * ----------------------------------------------------------------------------
 */

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <gtk/gtk.h>
#include <glade/glade.h>
#include <glade/glade-build.h>

#ifdef WITH_GNOME
#  include <libgnomeui/libgnomeui.h>
#endif
#include "globals.h"
#include "ilink_signals.h"
#include "devices.h"
#include "scenes.h"
#include "progress_bar.h"

extern scene_t *scene_list;
extern device_t device_array[2000];
extern int last_device;

extern void free_combo_boxes();
extern void populate_combo_boxes();
extern void ilink_add_device(int index);

extern char *current_file;
static int scene_updating = 0;

void msg_box(char *msg_text);

/*
 * on_add_device_button_clicked
 *
 * Called when the user clicks the add new device button. This adds a 
 * new INSTEON device into the in-memory device list.  This device then
 * becomes available for use in scenes.
 *
 * The link table for this device will initially be empty.
 */
void on_add_device_button_clicked(GtkWidget *widget, gpointer user_data) {
	int dev_type;
	char *id;
	char *name;
	device_t *dev;
	GtkWidget *msg;

	dev_type = gtk_combo_box_get_active((GtkComboBox *)device_type);
	id = (char *)gtk_entry_get_text((GtkEntry *)device_id);
	name = (char *)gtk_entry_get_text((GtkEntry *)device_name);

	/* Do some sanity checking on the id */
	if ((id[2] != '.') && (id[5] != '.')) {
		msg = gtk_message_dialog_new(GTK_WINDOW(main_window),
			GTK_DIALOG_DESTROY_WITH_PARENT, GTK_MESSAGE_ERROR,
			GTK_BUTTONS_CLOSE,
			"Invalid device ID, format must be \"xx.xx.xx\".");
		gtk_dialog_run (GTK_DIALOG (msg));
		gtk_widget_destroy (msg);
		return;
	}

	/* Check that a device type was entered */
	if (dev_type <= 0) {
		msg = gtk_message_dialog_new(GTK_WINDOW(main_window),
			GTK_DIALOG_DESTROY_WITH_PARENT, GTK_MESSAGE_ERROR,
			GTK_BUTTONS_CLOSE,
			"Please select a device type from the list.");
		gtk_dialog_run (GTK_DIALOG (msg));
		gtk_widget_destroy (msg);
		return;
	}

	/* Check that a device name was entered */
	if (strlen(name) == 0) {
		msg = gtk_message_dialog_new(GTK_WINDOW(main_window),
			GTK_DIALOG_DESTROY_WITH_PARENT, GTK_MESSAGE_ERROR,
			GTK_BUTTONS_CLOSE,
			"Please enter a name for the device.");
		gtk_dialog_run (GTK_DIALOG (msg));
		gtk_widget_destroy (msg);
		return;
	}

	/* Does the device already exist? If so, don't add it again */
	if ((dev = find_device((unsigned char *)id)) != NULL) {
		msg = gtk_message_dialog_new(GTK_WINDOW(main_window),
				GTK_DIALOG_DESTROY_WITH_PARENT, GTK_MESSAGE_ERROR,
				GTK_BUTTONS_CLOSE,
				"Device already exist (%s)", dev->device_name);
		gtk_dialog_run (GTK_DIALOG (msg));
		gtk_widget_destroy (msg);
		return;
	}

	/* Ok, lets add the new device */
	last_device++; /* allocate new device record */

	device_array[last_device].device_index = last_device;
	device_array[last_device].num_of_entries = 0;

	device_array[last_device].device_name = (char *)malloc(strlen(name)+1);
	strcpy(device_array[last_device].device_name, name);

	device_array[last_device].device_type = _device_types_ids[dev_type];

	memcpy(device_array[last_device].device_id, str_id_to_hex(id), 3);

	/* Now add it to all the combo boxes */
	ilink_add_device(last_device);
}

/*
 * on_add_responder_clicked
 *
 * Called when the user clicks the add button to add a new responder device
 * to the currenttly selected scene.  The scene must already exist.
 *
 * Ideally, this could be used to update an existing entry.
 */
void on_add_responder_clicked(GtkWidget *widget, gpointer user_data) {
	int sel;
	scene_t *active;
	device_list_t *resp, *rl;
	device_t *dev;
	link_record_t *link;
	GtkListStore *store;
	GtkTreeIter iter;
	char d1[10], d2[10], d3[10];
	int dev_index;

	sel = gtk_combo_box_get_active((GtkComboBox *)scene_selector);
	if (sel == -1) {
		printf("add responder: No scene selected!\n");
		return;
	}

	active = scene_list;
	while (active != NULL) {
		if (sel == active->index) {
			if (!active->controller) {
				printf("No scene controller defined, can't add responder\n");
				return;
			}


			/* Get device id from dropdown */
			dev_index =
				gtk_combo_box_get_active((GtkComboBox *)responder_selector);
			if (dev_index == -1) {
				printf("No responder selected!\n");
				return;
			}

			/* Check existing responder list to see if this device exists */
			rl = active->responder_list;
			while (rl) {
				if (rl->device->device_index == dev_index) {
					break;
				}
				rl = rl->next;
			}

			/* Set up responder record */
			if (rl) {
				resp = rl;
			} else {
				resp = (device_list_t *)malloc(sizeof(device_list_t));
				resp->device = &device_array[dev_index];
			}

			resp->link.data1 = (int)gtk_spin_button_get_value_as_int(
					(GtkSpinButton *)responder_data1);
			resp->link.data2 = (int)gtk_spin_button_get_value_as_int(
					(GtkSpinButton *)responder_data2);
			resp->link.data3 = (int)gtk_spin_button_get_value_as_int(
					(GtkSpinButton *)responder_data3);
			resp->link.group_number = active->group;
			resp->link.flags = 0xa2; /* always a slave record */
			memcpy(resp->link.device_id, active->controller->device_id, 3);

			/* add responder record to scene */
			if (!rl) {
				resp->next = active->responder_list;
				active->responder_list = resp;
				active->responders++;
			}

			/*
			 * Re-display the responder list with the new record
			 * I think we need to re-build the whole store since it is
			 * not stored anywhere. Maybe we can get the existing store
			 * and append to it?
			 */
			resp = active->responder_list;
			store = gtk_list_store_new(4, G_TYPE_STRING, G_TYPE_STRING,
					G_TYPE_STRING, G_TYPE_STRING);

			while (resp != NULL) {
				dev = resp->device;
				link = &resp->link;
				gtk_list_store_append(store, &iter);
				sprintf(d1, "%d", link->data1);
				sprintf(d2, "%d", link->data2);
				sprintf(d3, "%d", link->data3);
				gtk_list_store_set(store, &iter,
						0, dev->device_name,
						1, d1,
						2, d2,
						3, d3,
						-1);
				resp = resp->next;
			}
			gtk_tree_view_set_model((GtkTreeView *)responder_list,
					(GtkTreeModel *)store);
			g_object_unref (G_OBJECT (store));

			break;
		}
		active = active->next;
	}

	/* Rebuild the device link database */
	build_tables();
}


/*
 * on_program_scene_clicked
 *
 * Called when the user wants to program all devices in a scene. This
 * would be done after a new scene was added or after changing an existing
 * scene.
 *
 * It is assumed that the device link tables are up-to-date when this is
 * called.
 */
void on_program_scene_clicked(GtkWidget *widget, gpointer user_data)
{
	int scene_index;
	scene_t *scene;
	device_list_t *respd;
	char msg[35];

	scene_index = gtk_combo_box_get_active((GtkComboBox *)scene_selector);
	if (scene_index == -1) {
		printf("program_scene: No scene selected!\n");
		return;
	}

	scene = find_scene(scene_index);
	if (scene != NULL) {
		/* program the controller */
		sprintf(msg, "Programming %-20.20s..", scene->controller->device_name);
		progress_bar_open(msg, (scene->controller->num_of_entries * 8));
		printf("**** Programming Scene controller %s\n",
				scene->controller->device_name);
		program_device(iplc, scene->controller);

		/* program each responder */
		respd = scene->responder_list;
		while(respd) {
			sprintf(msg, "Programming %-20.20s..", respd->device->device_name);
			progress_bar_open(msg, (respd->device->num_of_entries * 8));
		printf("**** Programming responder %s\n", respd->device->device_name);
			program_device(iplc, respd->device);
			respd = respd->next;
		}
		progress_bar_close();
	}
}

/*
 * on_delete_scene_clicked
 *
 * Called when the user wants to delete a scene.  This removes the
 * scene from the internal data structure and updates all the device link
 * tables.  The devices won't be changed until they are programmed.
 *
 * Ideally, this should mark all devices who's link table gets modified as
 * dirty so that "program all" only really needs to update dirty devices.
 */
void on_delete_scene_clicked(GtkWidget *widget, gpointer user_data)
{
	int scene_index;

	scene_index = gtk_combo_box_get_active((GtkComboBox *)scene_selector);
	if (scene_index == -1) {
		printf("delete_scene: No scene selected!\n");
		return;
	}

	free_combo_boxes();
	delete_scene(scene_index);
	build_tables();
	populate_combo_boxes();
	gtk_combo_box_set_active((GtkComboBox *)scene_selector, 0);
}


/*
 * on_scene_delete_clicked
 *
 * Called when the user wants to delete a responder from a scene. After
 * modifying the scene, the device link tables will need to be re-built.
 */
void on_scene_delete_clicked(GtkWidget *widget, gpointer user_data)
{
	scene_t *scene;
	device_list_t *rlist, *rplist = NULL;
	device_t *dev;
	link_record_t *link;
	GtkTreeIter iter;
	GtkTreeModel *model;
	GtkTreeSelection *select;
	gchar *ch_data;
	int sindex;
	GtkListStore *store;
	char d1[10], d2[10], d3[10];

	/* Get Scene pointer */
	sindex = gtk_combo_box_get_active((GtkComboBox *)scene_selector);
	if (sindex == -1) {
		printf("delete_from_scene: No scene selected!\n");
		return;
	}

	scene = find_scene(sindex);
	if (scene == NULL) {
		printf("Can't find matching scene!\n");
		return;
	}

	select = gtk_tree_view_get_selection((GtkTreeView *)responder_list);
	if (gtk_tree_selection_get_selected (select, &model, &iter)) {

		/* Find the responder entry that matches this device name */
		gtk_tree_model_get (model, &iter, 0, &ch_data, -1);
		rlist = scene->responder_list;
		while (rlist) {
			if (rlist->device == NULL) {
				printf("Error: responder device is NULL\n");
				break;
			}
			if (strcmp(rlist->device->device_name, ch_data) == 0) {
				if (rplist == NULL) {
					scene->responder_list = rlist->next;
				} else {
					rplist->next = rlist->next;
				}
				free(rlist);
				break;
			}
			rplist = rlist;
			rlist = rlist->next;
		}
	}

	/* Rebuild and display the scene responder list */
	if (scene->responder_list) {
		printf("create pointers\n");
		rlist = scene->responder_list;
		dev = scene->responder_list->device;
		link = &scene->responder_list->link;
		printf("Set widgets\n");
		gtk_combo_box_set_active((GtkComboBox *)responder_selector,
			dev->device_index);
		gtk_spin_button_set_value((GtkSpinButton *)responder_data1,
			(gdouble)link->data1);
		gtk_spin_button_set_value((GtkSpinButton *)responder_data2,
			(gdouble)link->data2);
		gtk_spin_button_set_value((GtkSpinButton *)responder_data3,
			(gdouble)link->data3);

		store = gtk_list_store_new(4, G_TYPE_STRING, G_TYPE_STRING,
				G_TYPE_STRING, G_TYPE_STRING);
		while (rlist != NULL) {
			dev = rlist->device;
			link = &rlist->link;
			if (dev && link) {
				gtk_list_store_append(store, &iter);
				sprintf(d1, "%d", link->data1);
				sprintf(d2, "%d", link->data2);
				sprintf(d3, "%d", link->data3);
				gtk_list_store_set(store, &iter,
						0, dev->device_name,
						1, d1,
						2, d2,
						3, d3,
						-1);
			}
			rlist = rlist->next;
		}
	} else {
		/* Need to create an empty list? */
		store = gtk_list_store_new(4, G_TYPE_STRING, G_TYPE_STRING,
				G_TYPE_STRING, G_TYPE_STRING);
		gtk_list_store_append(store, &iter);
	}
	gtk_tree_view_set_model((GtkTreeView *)responder_list,
			(GtkTreeModel *)store);
	g_object_unref (G_OBJECT (store));
}



#if 0
/*
 * on_save_scene_clicked
 *
 * Called when the user wants to create a new scene. Once the new scene
 * is created, then responders can be added to the scene.
 */
void on_save_scene_clicked(GtkWidget *widget, gpointer user_data) {
	int scene_index;
	int dev_index;
	int group;
	scene_t *scene;

	scene_index = gtk_combo_box_get_active((GtkComboBox *)scene_selector);
	if (scene_index == -1) {
		printf("save_scene: No scene selected!\n");
		return;
	}

	dev_index = gtk_combo_box_get_active((GtkComboBox *)scene_controller);
	group = (int)gtk_spin_button_get_value_as_int((GtkSpinButton *)scene_group);

	/* does this scene already exist? */
	scene = find_scene(scene_index);
	if (scene) {
		/*
		 * update the scene info, but not the responders. Those are done
		 * seperately
		 */
		if (dev_index != -1) {
			scene->controller = &device_array[dev_index];
		}
		scene->group = group;
		build_tables(); /* Since the controller and/or group may have changed */
	} else {
		/* create new scene record and add it */
		scene = (scene_t *)malloc(sizeof(scene_t));
		if (dev_index != -1) {
			scene->controller = &device_array[dev_index];
		}
		scene->group = group;
		scene->responders = 0;
		scene->responder_list = NULL;
		scene->index = scene_index;
	}

	/* populate_combo_boxes(); */
}
#endif


/*
 * on_delete_device_clicked
 *
 * Called when the user wants to remove a device from the internal data
 * structures. The device will also be removed from all scenes.
 */
void on_delete_device_clicked(GtkWidget *widget, gpointer user_data) {
	int device_index;

	device_index = gtk_combo_box_get_active((GtkComboBox *)device_selector);
	if (device_index == -1) {
		printf("No device selected!\n");
		return;
	}

	/*
	 * Need to remove the device id from all scenes.  If it is the
	 * controller of a scene, then the whole scene gets deleted. If it
	 * is the responder in a scene, just remove the responder entry.
	 */
	delete_device_from_scenes(device_index);

	/* delete the device. */
	free_combo_boxes();
	delete_device(device_index);
	build_tables();
	populate_combo_boxes();
}


/*
 * on_add_scene_clicked
 *
 * Create a new empty scene record with the name specified in the
 * new_scene_name box.
 */
void on_add_scene_clicked(GtkWidget *widget, gpointer user_data)
{
	char *name;
	scene_t *new_scene, *tmp;

	/* Get scene name from text entry box */
	name = (char *)gtk_entry_get_text((GtkEntry *)new_scene_name);
	if (!name || strlen(name) == 0) {
		msg_box("Missing scene name!");
		printf("Failed to get new scene name.\n");
		return;
	}

	/* Check if name already in use */
	if (get_scene_by_name(name) != NULL) {
		msg_box("Scene name already in use!");
		return;
	}

	new_scene = (scene_t *)malloc(sizeof(scene_t));
	if (!new_scene) {
		printf("Memory allocation failed allocating new scene record.\n");
		return;
	}
	memset(new_scene, 0, sizeof(scene_t));

	new_scene->name = (char *)malloc(strlen(name)+1);
	if (!new_scene->name) {
		printf("Memory allocation failed allocating new scene record.\n");
		return;
	}
	strcpy (new_scene->name, name);
	new_scene->group = (int)gtk_spin_button_get_value_as_int(
			(GtkSpinButton *)scene_group);

	/* Ok, lets add the new device */
	tmp = scene_list;
	while (tmp && tmp->next) {
		tmp = tmp->next;
	}
	if (tmp) {
		tmp->next = new_scene;
		new_scene->index = tmp->index + 1;
	} else {
		scene_list = new_scene;
		new_scene->index = 0;
	}

	/* Now add it to all the combo boxe */
	gtk_combo_box_append_text((GtkComboBox *)scene_selector, name);
	gtk_combo_box_set_active((GtkComboBox *)scene_selector, new_scene->index);
}


/*
 * on_list_links_clicked
 *
 * Called when the user wants to get the link table from a device.
 */
void on_list_links_clicked(GtkWidget *widget, gpointer user_data)
{
	GtkWidget *msg;

	msg = gtk_message_dialog_new(GTK_WINDOW(main_window),
			GTK_DIALOG_DESTROY_WITH_PARENT, GTK_MESSAGE_ERROR,
			GTK_BUTTONS_CLOSE,
			"Command not implemented yet.");
	gtk_dialog_run (GTK_DIALOG (msg));
	gtk_widget_destroy (msg);
}


/*
 * on_program_device_clicked
 *
 * Called when the user wants to update the link table on a specific
 * device.
 */
void on_program_device_clicked(GtkWidget *widget, gpointer user_data)
{
	int sel;

	sel = gtk_combo_box_get_active((GtkComboBox *)device_selector);
	if (sel == -1) {
		printf("No device selected!\n");
		return;
	}

	progress_bar_open("Programming device...",
			(device_array[sel].num_of_entries * 8));
	program_device(iplc, &(device_array[sel]));
	progress_bar_close();
}


/*
 * on_program_all_clicked
 *
 * Called when the user wants to update the link table on all devices.
 * Since this can be a time consuming operation, it would be nice if
 * a dirty flag could be used to only program devices that really need
 * to be updated.
 */
void on_program_all_clicked(GtkWidget *widget, gpointer user_data)
{
	int i;
	char msg[35];

	/* This could take a while, maybe prompt the user for confirmation */
	/* Could we run this in a thread?, might be possible */
	for (i = 0; i <= last_device; i++) {
		sprintf(msg, "Programming %-20.20s..", device_array[i].device_name);
		progress_bar_open(msg, (device_array[i].num_of_entries * 8));
		program_device(iplc, &(device_array[i]));
	}
	progress_bar_close();
}

void on_about_close_clicked(GtkWidget *widget, gpointer user_data)
{
}


/*
 * on_scene_selector_changed
 *
 * Called when the user wants to view/modify a specific scene. All the
 * other scene fields get updated with this scenes values.
 *
 * What happens if the user enters a value in this box for a new scene?
 * This probably gets called but should it clear all the fields or should
 * it leave all the fields populated?
 */
void on_scene_selector_changed(GtkWidget *widget, gpointer user_data) {
	scene_t *active;
	device_t *dev;
	device_list_t *responders;
	link_record_t *link;
	int sel;
	GtkListStore *store;
	GtkTreeIter iter;
	char d1[10], d2[10], d3[10];

	sel = gtk_combo_box_get_active((GtkComboBox *)scene_selector);
	if (sel == -1) {
		printf("scene selector changed: No scene selected!\n");
		return;
	}

	scene_updating = 1;
	if ((active = find_scene(sel)) != NULL) {
		if (active->controller) {
			gtk_combo_box_set_active((GtkComboBox *)scene_controller,
				active->controller->device_index);
			switch (active->controller->device_type) {
				case 0x0109: /* keypadlinc */
					gtk_spin_button_set_range((GtkSpinButton *)scene_group,
							1.0, 8.0);
					break;
				case 0x0004: /* controllinc */
					gtk_spin_button_set_range((GtkSpinButton *)scene_group,
							1.0, 5.0);
					break;
				case 0x0001:
				case 0x0000:
					gtk_spin_button_set_range((GtkSpinButton *)scene_group,
							1.0, 254.0);
					break;
				default:
					gtk_spin_button_set_range((GtkSpinButton *)scene_group,
							1.0, 1.0);
					break;
			}
		} else {
			gtk_combo_box_set_active((GtkComboBox *)scene_controller, -1);
		}

		gtk_spin_button_set_value((GtkSpinButton *)scene_group,
				(gdouble)(active->group * 1.0));

		if (active->responder_list) {
			responders = active->responder_list;
			dev = active->responder_list->device;
			link = &active->responder_list->link;
			gtk_combo_box_set_active((GtkComboBox *)responder_selector,
				dev->device_index);
			gtk_spin_button_set_value((GtkSpinButton *)responder_data1,
				(gdouble)link->data1);
			gtk_spin_button_set_value((GtkSpinButton *)responder_data2,
				(gdouble)link->data2);
			gtk_spin_button_set_value((GtkSpinButton *)responder_data3,
				(gdouble)link->data3);

			store = gtk_list_store_new(4, G_TYPE_STRING, G_TYPE_STRING,
					G_TYPE_STRING, G_TYPE_STRING);
			while (responders != NULL) {
				dev = responders->device;
				link = &responders->link;
				gtk_list_store_append(store, &iter);
				sprintf(d1, "%d", link->data1);
				sprintf(d2, "%d", link->data2);
				sprintf(d3, "%d", link->data3);
				gtk_list_store_set(store, &iter,
						0, dev->device_name,
						1, d1,
						2, d2,
						3, d3,
						-1);
				responders = responders->next;
			}
			gtk_tree_view_set_model((GtkTreeView *)responder_list,
					(GtkTreeModel *)store);
			g_object_unref (G_OBJECT (store));
		}
	}
	scene_updating = 0;
}

void on_device_selector_state_changed(GtkWidget *widget, gpointer user_data) {
}


/*
 * on_device_selector_changed
 *
 * Called when the user selects a device to view the link table of or program.
 */
void on_device_selector_changed(GtkWidget *widget, gpointer user_data) {
	int sel;
	GtkListStore *store;
	GtkTreeIter iter;
	char str[25];
	int i;


	sel = gtk_combo_box_get_active((GtkComboBox *)device_selector);
	if (sel == -1) {
		printf("No device selected!\n");
		return;
	}

	gtk_statusbar_pop((GtkStatusbar *)device_bar, 2);
	store = gtk_list_store_new(6, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING,
			G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING);

	for (i = 0; i < device_array[sel].num_of_entries; i++) {
		gtk_list_store_append(store, &iter);
		sprintf(str, "0x%02x", device_array[sel].db_records[i].flags);
		gtk_list_store_set(store, &iter, 0, str, -1);
		sprintf(str, "%d", device_array[sel].db_records[i].group_number);
		gtk_list_store_set(store, &iter, 1, str, -1);
		sprintf(str, "%02x.%02x.%02x",
				device_array[sel].db_records[i].device_id[0],
				device_array[sel].db_records[i].device_id[1],
				device_array[sel].db_records[i].device_id[2]);
		gtk_list_store_set(store, &iter, 2, str, -1);
		sprintf(str, "%d", device_array[sel].db_records[i].data1);
		gtk_list_store_set(store, &iter, 3, str, -1);
		sprintf(str, "%d", device_array[sel].db_records[i].data2);
		gtk_list_store_set(store, &iter, 4, str, -1);
		sprintf(str, "%d", device_array[sel].db_records[i].data3);
		gtk_list_store_set(store, &iter, 5, str, -1);
	}
	gtk_tree_view_set_model((GtkTreeView *)link_list,
			(GtkTreeModel *)store);
	g_object_unref (G_OBJECT (store));
}


/*
 * on_scene_controller_changed
 *
 * Called when the user changes a scene master. Nothing is done with
 * this event.
 *
 * If a scene is currently selected, then this should change the controller
 * device associated with that scene. If no scene is currently selected, it
 * does nothing.
 */
void on_scene_controller_changed(GtkWidget *widget, gpointer user_data)
{
	int sel;
	int dev;
	scene_t *scene;

	printf("Enter scene_controller changed.\n");
	sel = gtk_combo_box_get_active((GtkComboBox *)scene_selector);
	if (sel == -1) {
		printf("scene controller changed: No scene selected!\n");
		return;
	}

	scene = scene_list;
	while (scene != NULL) {
		if (scene->index == sel) {
			dev = gtk_combo_box_get_active((GtkComboBox *)widget);
			if (dev == -1) {
				printf("Controller changed, but currently empty!\n");
				return;
			}
			scene->controller = &device_array[dev];
			switch (scene->controller->device_type) {
				case 0x0109: /* keypadlinc */
					gtk_spin_button_set_range((GtkSpinButton *)scene_group,
							1.0, 8.0);
					break;
				case 0x0004: /* controllinc */
					gtk_spin_button_set_range((GtkSpinButton *)scene_group,
							1.0, 5.0);
					break;
				case 0x0001:
				case 0x0000:
					gtk_spin_button_set_range((GtkSpinButton *)scene_group,
							1.0, 254.0);
					break;
				default:
					gtk_spin_button_set_range((GtkSpinButton *)scene_group,
							1.0, 1.0);
					break;
			}
			return;
		}
		scene = scene->next;
	}
	printf("Didn't find scene number %d, controller not added.\n", sel);
}


/*
 * on_scene_group_editing_done
 *
 * Called when the user edits a value in the scene group spinbox. If a
 * scene is currently selected, then the scene gets updated with the new
 * value. Otherwise, this is ignored.
 */
void on_scene_group_editing_done(GtkWidget *widget, gpointer user_data)
{
	int sel;
	scene_t *scene;

	printf("scene_group_editing_done called\n");
	sel = gtk_combo_box_get_active((GtkComboBox *)scene_selector);
	if (sel == -1) {
		printf("scene group editing: No scene selected!\n");
		return;
	}

	printf("Setting scene group number!\n");
	scene = scene_list;
	while (scene != NULL) {
		if (scene->index == sel) {
			scene->group = (int)gtk_spin_button_get_value_as_int(
					(GtkSpinButton *)scene_group);
			printf("scene group number set to %d\n", scene->group);
			free_tables();
			build_tables();
			return;
		}
		scene = scene->next;
	}
}
void on_scene_group_changed(GtkWidget *widget, gpointer user_data)
{
	int sel;
	scene_t *scene;
	device_list_t *respd;
	int group;

	/*
	 * This should only be called if the user is changing the
	 * the group number. So we use the global 'scene_updating'
	 * to let us know that the group number is being updated
	 * by another function.
	 */
	if (scene_updating) {
		return;
	}

	sel = gtk_combo_box_get_active((GtkComboBox *)scene_selector);
	if (sel == -1) {
		printf("scene group editing: No scene selected!\n");
		return;
	}

	scene = scene_list;
	while (scene != NULL) {
		if (scene->index == sel) {
			group = (int)gtk_spin_button_get_value_as_int(
					(GtkSpinButton *)scene_group);
			if (group != scene->group) {
				scene->group = group;

				/* Fix all the responder links */
				respd = scene->responder_list;
				while (respd) {
					respd->link.group_number = group;
					respd = respd->next;
				}
				build_tables();

				/* FIXME: Need to possibly update the device links list box */
			}
			return;
		}
		scene = scene->next;
	}
}


/*
 * on_save_as1_activate
 *
 * Called when the user selects the "Save As" menu item. This opens a
 * dialog box and allows the user to select a path/file to save the
 * current scene info.
 */
void on_save_as1_activate(GtkWidget *widget, gpointer user_data) {
	GtkWidget *dialog;
	gchar *window_name;

	dialog = gtk_file_chooser_dialog_new ("Save ilink configuration",
			(GtkWindow *)main_window,
			GTK_FILE_CHOOSER_ACTION_SAVE,
			GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
			GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT, NULL);

	/* Need newer version of GTK+ for this
	gtk_file_chooser_set_do_overwrite_confirmation (GTK_FILE_CHOOSER (dialog),
			TRUE);
	*/
	if (current_file) {
		gtk_file_chooser_set_filename (GTK_FILE_CHOOSER (dialog), current_file);
	}

	if (gtk_dialog_run (GTK_DIALOG (dialog)) == GTK_RESPONSE_ACCEPT) {
		char *filename;
		filename = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (dialog));
		printf("Picked file %s\n", filename);
		save_scene_info(filename);

		/* Save the name of the file so it can be saved later */
		if (current_file) {
			free(current_file);
		}
		current_file = malloc(strlen(filename) + 1);
		strcpy(current_file, filename);

		window_name = (gchar *)malloc(strlen(filename) + 31);
		sprintf(window_name, "ilink INSTEON Scene Manager - %s", filename);
		gtk_window_set_title((GtkWindow *)main_window, window_name);
		free(window_name);

		g_free (filename);
	}
	gtk_widget_destroy (dialog);
}


/*
 * on_new1_activate
 *
 * Called when the user wants to create a new scene database.  This should
 * clear out all the current data.
 */
void on_new1_activate(GtkWidget *widget, gpointer user_data) {
}


void on_clear1_activate(GtkWidget *widget, gpointer user_data) {
}

void on_paste1_activate(GtkWidget *widget, gpointer user_data) {
}


/*
 * on_open1_activate
 *
 * Called when the user wants to open a new scene database file. This opens
 * a dialog box and allows the user to select a file. Any existing data
 * is cleared and replaced with the data from the selected file.
 */
void on_open1_activate(GtkWidget *widget, gpointer user_data) {
	GtkWidget *dialog;
	char *filename;
	gchar *window_name;

	dialog = gtk_file_chooser_dialog_new ("Open File", (GtkWindow *)main_window,
			GTK_FILE_CHOOSER_ACTION_OPEN,
			GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
			GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT, NULL);

	if (gtk_dialog_run (GTK_DIALOG (dialog)) == GTK_RESPONSE_ACCEPT) {
		filename = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (dialog));
		printf("Picked file %s\n", filename);

		/* Save the name of the file so it can be saved later */
		if (current_file) {
			free(current_file);
		}
		current_file = malloc(strlen(filename) + 1);
		strcpy(current_file, filename);

		/* Update all the internal data structure with the new data */
		free_combo_boxes();
		free_tables();
		get_scene_info(filename);
		build_tables();
		populate_combo_boxes();
		window_name = (gchar *)malloc(strlen(filename) + 31);
		sprintf(window_name, "ilink INSTEON Scene Manager - %s", filename);
		gtk_window_set_title((GtkWindow *)main_window, window_name);
		free(window_name);
		g_free (filename);
	}

	gtk_widget_destroy (dialog);

}


/*
 * on_about1_activate
 *
 * Called to display an about dialog box.
 */
void on_about1_activate(GtkWidget *widget, gpointer user_data)
{

	gtk_widget_show(about_dialog);

#if 0
	/*
	 * This would be really nice to use, except that it needs GTK+ 2.6
	 * or later and I only have version 2.4.
	 */
	gtk_show_about_dialog((GtkWindow *)main_window,
			"name", "ilink INSTEON Scene Manager",
			"copyright", "Copyright (C) 2006 Bob Paauwe",
			"authors", "Bob Paauwe",
			"license", "GPL version 2",
			"version", "0.1",
			"website", "http://www.bobsplace.com/ilink/",
			NULL);
#endif
}

void on_about_closebutton_clicked(GtkWidget *widget, gpointer user_data)
{
	gtk_widget_hide(about_dialog);
}

void on_cut1_activate(GtkWidget *widget, gpointer user_data) {
}


/*
 * on_quit1_activate
 *
 * Called when the user wants to exit the application.
 */
void on_quit1_activate(GtkWidget *widget, gpointer user_data) {
	GtkWidget *msg;
	gint ret = 0;


	if (need_to_save != 0) {
		printf("Need to save?\n");

		msg = gtk_message_dialog_new(GTK_WINDOW(main_window),
			GTK_DIALOG_DESTROY_WITH_PARENT, GTK_MESSAGE_ERROR,
			GTK_BUTTONS_OK_CANCEL,
			"Exit without Saving?");
		ret = gtk_dialog_run (GTK_DIALOG (msg));
		gtk_widget_destroy (msg);
	}
	if ((ret == -5) || (ret == 0)) {
		gtk_main_quit();
	} else {
		printf("Quit cancelled.\n");
	}
}

void on_new_scene1_activate(GtkWidget *widget, gpointer user_data) {
}

void on_preferences1_activate(GtkWidget *widget, gpointer user_data) {
}

/*
 * on_save1_activate
 *
 * Called when the user selects the "Save" menu item. This opens a
 * dialog box and allows the user to select a path/file to save the
 * current scene info.
 */
void on_save1_activate(GtkWidget *widget, gpointer user_data) {
	GtkWidget *dialog;
	gchar *window_name;

	if (!current_file) {
		dialog = gtk_file_chooser_dialog_new ("Save ilink configuration",
				(GtkWindow *)main_window,
				GTK_FILE_CHOOSER_ACTION_SAVE,
				GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
				GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT, NULL);

#if 0
		gtk_file_chooser_set_do_overwrite_confirmation (
				GTK_FILE_CHOOSER (dialog), TRUE);

		if (current_file) {
			gtk_file_chooser_set_filename (
					GTK_FILE_CHOOSER (dialog), current_file);
		}
#endif

		if (gtk_dialog_run (GTK_DIALOG (dialog)) == GTK_RESPONSE_ACCEPT) {
			char *fname;
			fname = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (dialog));
			save_scene_info(fname);

			/* Save the name of the file so it can be saved later */
			if (current_file) {
				free(current_file);
			}
			current_file = malloc(strlen(fname) + 1);
			strcpy(current_file, fname);

			window_name = (gchar *)malloc(strlen(fname) + 31);
			sprintf(window_name, "ilink INSTEON Scene Manager - %s", fname);
			gtk_window_set_title((GtkWindow *)main_window, window_name);
			free(window_name);
			g_free (fname);
		}
		gtk_widget_destroy (dialog);
	} else {
		save_scene_info(current_file);
	}
}

void on_copy1_activate(GtkWidget *widget, gpointer user_data) {
}


/*
 * on_responder_list_cursor_changed
 *
 * Called when the user clicks on one of the responder lines. The responder
 * data fields are then filled in with the data from this line.
 */
void on_responder_list_cursor_changed(GtkWidget *widget, gpointer user_data) {
	GtkTreeIter iter;
	GtkTreeModel *model;
	GtkTreeSelection *select;
	gchar *ch_data;
	gdouble data;
	int i;

	select = gtk_tree_view_get_selection((GtkTreeView *)widget);
	if (select == NULL) {
		return;
	}
	if (gtk_tree_selection_get_selected (select, &model, &iter)) {

		/* Find device index that matches name */
		gtk_tree_model_get (model, &iter, 0, &ch_data, -1);
		if (ch_data == NULL) {
			return;
		}
		for (i = 0; i <= last_device; i++) {
			if (strcmp(device_array[i].device_name, ch_data) == 0) {
				gtk_combo_box_set_active((GtkComboBox *)responder_selector, i);
				break;
			}
		}

		gtk_tree_model_get (model, &iter, 1, &ch_data, -1);
		data = (gdouble)(atoi(ch_data) & 0xff);
		gtk_spin_button_set_value((GtkSpinButton *)responder_data1, data);

		gtk_tree_model_get (model, &iter, 2, &ch_data, -1);
		data = (gdouble)(atoi(ch_data) & 0xff);
		gtk_spin_button_set_value((GtkSpinButton *)responder_data2, data);

		gtk_tree_model_get (model, &iter, 3, &ch_data, -1);
		data = (gdouble)(atoi(ch_data) & 0xff);
		gtk_spin_button_set_value((GtkSpinButton *)responder_data3, data);
	}
}


/*
 * on_link_list_cursor_changed
 *
 * Called when the user clicks on one of the lines in the device link table.
 * The status bar is then updated with info an the link entry.
 */
void on_link_list_cursor_changed(GtkWidget *widget, gpointer user_data) {
	GtkTreeIter iter;
	GtkTreeModel *model;
	GtkTreeSelection *select;
	gchar *ch_data;
	int group;
	int flags;
	device_t *device;
	char message[256];

	select = gtk_tree_view_get_selection((GtkTreeView *)widget);
	if (gtk_tree_selection_get_selected (select, &model, &iter)) {

		/* Get flags */
		gtk_tree_model_get (model, &iter, 0, &ch_data, -1);
		flags = ahtoi(ch_data);

		/* Get device group */
		gtk_tree_model_get (model, &iter, 1, &ch_data, -1);
		group = atoi(ch_data);

		/* Get device id */
		gtk_tree_model_get (model, &iter, 2, &ch_data, -1);
		device = find_device((unsigned char *)ch_data);
		if (device != NULL) {
			if (flags & 0x40) {
				sprintf(message, "Controls %s on group %d", device->device_name,
						group);
			} else {
				sprintf(message, "Responds to group %d messages from %s", group,
						device->device_name);
			}

			gtk_statusbar_pop((GtkStatusbar *)device_bar, 2);
			gtk_statusbar_push((GtkStatusbar *)device_bar, 2, message);
		} else {
			printf("Find device returned null for %s\n", ch_data);
		}
	}
}


/*
 * replace device dialog box handlers
 * Called when the replace device dialog box gets a response from the
 * user.
 */
void on_replace_close(GtkWidget *widget, gpointer user_data)
{
	printf("Replace dialog box closed?\n");
	gtk_widget_hide(widget);
}

void on_replace_device_activate(GtkWidget *widget, gpointer user_data)
{
	gtk_widget_show(replace_dialog);
}

void on_replace_cancelbutton_clicked(GtkWidget *widget, gpointer user_data)
{
	gtk_widget_hide(replace_dialog);
}

void on_replace_okbutton_clicked(GtkWidget *widget, gpointer user_data)
{
	scene_t *scene;
	device_t *src;
	device_t *dst;
	device_list_t *resp;
	int sel;

	printf("Do device replacement.....\n");

	sel = gtk_combo_box_get_active((GtkComboBox *)replace_src);
	if (sel == -1) {
		printf("No source device selected!\n");
		return;
	}
	src = &device_array[sel];

	sel = gtk_combo_box_get_active((GtkComboBox *)replace_dst);
	if (sel == -1) {
		printf("No destination device selected!\n");
		return;
	}
	dst = &device_array[sel];

	scene = scene_list;
	while (scene) {
		if (scene->controller == src) {
			printf("Replace controller in scene %s\n", scene->name);
			scene->controller = dst;
		}

		resp = scene->responder_list;
		while (resp) {
			if (resp->device == src) {
				printf("Replacing responder in scene %s\n", scene->name);
				resp->device = dst;
			}
			resp = resp->next;
		}

		scene = scene->next;
	}

	/* Rebuild the device link database */
	build_tables();

	gtk_widget_hide(replace_dialog);
}


/*
 * Menu items not yet working
 */
void on_device_tools_activate(GtkWidget *widget, gpointer user_data)
{
}
void on_ping_device_activate(GtkWidget *widget, gpointer user_data)
{
}

/*
 * on_update_plc_activate
 *
 * This is a menu item for updating the link table in the PLC itself.
 * It should probably bring up a dialog box and allow the user to
 * select various options. For now this simply makes sure the PLC
 * link table has an entry for every device. The only real reason
 * to do this is so that the PLC can monitor traffic and see messages
 * from all the devices.
 */
void on_initialize_plc_links_activate(GtkWidget *widget, gpointer user_data)
{
	int i;
	unsigned char buf[10];

	/*
	 * maybe this should open a dialog box with options to add
	 * a single device or all devices. Maybe options to clear
	 * entries, set the group number and class too.
	 *
	 * For now it just loops through all the devices and add the
	 * ones that don't already exist.
	 */

	for (i = 0; i <= last_device; i++) {
		if (iplc_get_entry(iplc, device_array[i].device_id, 1, buf) == 0) {
			iplc_add_id(iplc, device_array[i].device_id, 1, 4);
		}
	}
}

void on_scene_menu_activate(GtkWidget *widget, gpointer user_data)
{
}
void on_query_devices_for_scenes_activate(GtkWidget *widget, gpointer user_data)
{
	/* This will take a long time, it should maybe be in a new thread? */
	free_combo_boxes();
	discover_scenes(iplc);
	populate_combo_boxes();
}



/*
 * Scene rename dialog box functions
 */
void on_rename_scenes_activate(GtkWidget *widget, gpointer user_data)
{
	/*
	GladeXML *dlg;

	dlg = glade_xml_new("ilink-2.glade", "sr_dialog", NULL);
	if (!dlg) {
		printf("Failed to create dialog ....\n");
	}
	glade_xml_signal_autoconnect(dlg);
	*/
	gtk_widget_show(sr_dialog);

}

void on_sr_apply_clicked(GtkWidget *widget, gpointer user_data)
{
	int sel;
	char *new_name;

	sel = gtk_combo_box_get_active((GtkComboBox *)sr_selector);
	if (sel == -1) {
		printf("No device selected!\n");
		return;
	}

	new_name = (char *)gtk_entry_get_text((GtkEntry *)sr_entry);

	/* Check if name already in use */
	if (get_scene_by_name(new_name) != NULL) {
		msg_box("Scene name already in use!");
		return;
	}
	rename_scene(sel, new_name);
	gtk_combo_box_remove_text((GtkComboBox *)sr_selector, sel);
	gtk_combo_box_insert_text((GtkComboBox *)sr_selector, sel, new_name);
	gtk_combo_box_remove_text((GtkComboBox *)scene_selector, sel);
	gtk_combo_box_insert_text((GtkComboBox *)scene_selector, sel, new_name);
}

void on_sr_ok_clicked(GtkWidget *widget, gpointer user_data)
{
	int sel;
	char *new_name;

	sel = gtk_combo_box_get_active((GtkComboBox *)sr_selector);
	if (sel == -1) {
		printf("No device selected!\n");
		return;
	}

	new_name = (char *)gtk_entry_get_text((GtkEntry *)sr_entry);
	rename_scene(sel, new_name);
	gtk_combo_box_remove_text((GtkComboBox *)sr_selector, sel);
	gtk_combo_box_insert_text((GtkComboBox *)sr_selector, sel, new_name);
	gtk_combo_box_remove_text((GtkComboBox *)scene_selector, sel);
	gtk_combo_box_insert_text((GtkComboBox *)scene_selector, sel, new_name);

	gtk_widget_hide(sr_dialog);
}

void on_sr_cancel_clicked(GtkWidget *widget, gpointer user_data)
{
	gtk_widget_hide(sr_dialog);
}



/*
 * on_ilink_main_window_destroy
 *
 * Called when the main window is asked to go away. This exits the
 * application.
 */
void on_ilink_main_window_destroy(GtkWidget *widget, gpointer user_data) {
	GtkWidget *msg;
	gint ret = 0;

	if (need_to_save != 0) {
		printf("Need to save?\n");

		msg = gtk_message_dialog_new(GTK_WINDOW(main_window),
			GTK_DIALOG_DESTROY_WITH_PARENT, GTK_MESSAGE_ERROR,
			GTK_BUTTONS_OK_CANCEL,
			"Exit without Saving?");
		ret = gtk_dialog_run (GTK_DIALOG (msg));
		gtk_widget_destroy (msg);
	}
	if ((ret == -5) || (ret == 0)) {
		gtk_main_quit();
	}
	return;
}


void msg_box(char *msg_text)
{
	GtkWidget *msg;

	msg = gtk_message_dialog_new(GTK_WINDOW(main_window),
			GTK_DIALOG_DESTROY_WITH_PARENT, GTK_MESSAGE_ERROR,
			GTK_BUTTONS_CLOSE,
			msg_text);
	gtk_dialog_run (GTK_DIALOG (msg));
	gtk_widget_destroy (msg);
}
