/*
 * $RCSfile: file_io.c,v $  $Revision: 1.1 $  $Name:  $
 * $Id: file_io.c,v 1.1 2007/02/20 22:42:49 bpaauwe Exp $
 * $Author: bpaauwe $
 * $Date: 2007/02/20 22:42:49 $
 * ----------------------------------------------------------------------------
 *
 *  Copyright (c) Bob Paauwe (2007)
 *
 *  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:
 *    Functions to read and write data files.
 * ----------------------------------------------------------------------------
 */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "libilib/ilib.h"
#include "device.h"

static char *device_name(device_t *dev_list, unsigned char *id);

/*
 * read_file()
 *
 * Read a device data file and build a link list of devices.
 *
 * File structure:
 *
 *   DEVICE_NAME=<device name>
 *   DEVICE_ID=xx.xx.xx
 *
 */
device_t *read_file(char *fname)
{
	FILE *fp;
	char line[256];
	int len;
	char *tok;
	char *tstr;
	device_t *dev_list = NULL;
	device_t *node;
	scene_t *current = NULL;
	scene_t *last_scene = NULL;
	device_list_t *dev;
	int scene_index = 0;
	scene_t *scene_list;
	int last_device;

	fp = fopen(fname, "r");
	if (fp == NULL) {
		printf("Failed to open %s\n", fname);
		return NULL;
	}

	while ((len = getline(fp, line, 256)) != EOF) {
		/* Really simple parsing for now */
		if (len == 0) {
			/* skip */
		} else if (line[0] == '#') { /* comment */
			/* skip */
#if 0
		} else if (strncasecmp(line, "scene", 5) == 0) {
			/* allocate new scene record */
			if ((current = (scene_t *)malloc(sizeof(scene_t))) == NULL) {
				printf("Failed to allocate memory!\n");
				fclose(fp);
				return NULL;
			}
			current->responders = 0;
			current->next = NULL;
			current->index = scene_index++;
			current->responder_list = NULL;
			if (scene_list == NULL) {
				scene_list = current;
			} else {
				last_scene->next = current;
			}
			last_scene = current;

			tok = index(line, '=');
			if (tok++) {
				/* copy name into scene record */
				current->name = (char *)malloc(strlen(tok)+1);
				strcpy(current->name, tok);
			}
		} else if (strncasecmp(line, "master", 6) == 0) {
			tok = index(line, '=');
			if ((tok++) && (current)) {
				/* master device id */
				current->controller = find_device((unsigned char *)tok);
				if (current == NULL) {
					printf("Unable to find device id %s\n", tok);
				}
			}
		} else if (strncasecmp(line, "group", 5) == 0) {
			tok = index(line, '=');
			if ((tok++) && (current)) {
				/* group = atoi(tok);*/
				current->group = atoi(tok);
			}
		} else if (strncasecmp(line, "responder", 9) == 0) {
			tok = index(line, '=');
			if ((tok++) && (current)) {
				/* parse responder line */
				dev = (device_list_t *)malloc(sizeof(device_list_t));
				tstr = strtok(tok, " ");
				dev->device = find_device((unsigned char *)tstr);
				tstr = strtok(NULL, " ");
				dev->link.data1 = ahtoi(tstr);
				tstr = strtok(NULL, " ");
				dev->link.data2 = ahtoi(tstr);
				tstr = strtok(NULL, " ");
				dev->link.data3 = ahtoi(tstr);
				if (current->controller) {
					memcpy(dev->link.device_id, current->controller->device_id,
							3);
				}
				dev->link.group_number = current->group;
				dev->link.flags = 0xa2; /* this is alwasy a slave record */

				dev->next = current->responder_list;
				current->responder_list = dev;
				current->responders++;
			}
#endif
		} else if (strncasecmp(line, "device_name", 11) == 0) {
			/* 
			 * Finding a device name in the file means this is the
			 * start of a new device section. Allocate a new device node 
			 * and add it to the list.
			 */
			node = (device_t *)malloc(sizeof(device_t));
			if (dev_list == NULL) {
				node->next = NULL;
				dev_list = node;
			} else {
				node->next = dev_list;
				dev_list = node;
			}

			last_device++;

			tok = index(line, '=');
			if (tok++) {
				node->name = (char *)malloc(strlen(tok) + 1);
				if (node->name != NULL) {
					strcpy(node->name, tok);
					node->link_cnt = 0;
				} else {
					printf("Memory allocation failed\n");
				}
			}
		} else if (strncasecmp(line, "device_id", 9) == 0) {
			/* printf("Reading device (%s)\n", line); */
			tok = index(line, '=');
			if (tok++) {
				memcpy(node->id, str_id_to_hex(tok), 3);
			}
		} else if (strncasecmp(line, "device_type", 11) == 0) {
			tok = index(line, '=');
			if (tok++) {
				node->type = atoi(tok);
			}
		} else if (strncasecmp(line, "device_version", 14) == 0) {
			tok = index(line, '=');
			if (tok++) {
				tstr = strtok(tok, ".");
				node->version = atoi(tstr) << 4;
				tstr = strtok(NULL, ".");
				node->version |= (atoi(tstr) & 0x0f);
			}
		} else if (strncasecmp(line, "device_revision", 15) == 0) {
			tok = index(line, '=');
			if (tok++) {
				tstr = strtok(tok, ".");
				node->version = atoi(tstr) << 4;
				tstr = strtok(NULL, ".");
				node->version |= (atoi(tstr) & 0x0f);
			}
		} else if (strncasecmp(line, "device_local_level", 18) == 0) {
			tok = index(line, '=');
			if (tok++) {
				node->local_on_level = atoi(tok);
			}
		} else if (strncasecmp(line, "device_local_ramp", 17) == 0) {
			tok = index(line, '=');
			if (tok++) {
				node->local_ramp_rate = atoi(tok);
			}
		} else if (strncasecmp(line, "device_status", 13) == 0) {
			tok = index(line, '=');
			if (tok++) {
				node->status = atoi(tok);
			}
		} else if (strncasecmp(line, "device_link", 11) == 0) {
			tok = index(line, '=');
			if (tok++) {
				char *tstr;
				/* 0xa2 13 06.6a.e2 0xff 0x14 0x00 */
				tstr = strtok(tok, " ");
				node->links[node->link_cnt].flags = ahtoi(tstr);
				tstr = strtok(NULL, " ");
				node->links[node->link_cnt].group_number = atoi(tstr);
				tstr = strtok(NULL, " ");
				memcpy(node->links[node->link_cnt].device_id,
						str_id_to_hex(tstr), 3);
				tstr = strtok(NULL, " ");
				node->links[node->link_cnt].data1 = ahtoi(tstr);
				tstr = strtok(NULL, " ");
				node->links[node->link_cnt].data2 = ahtoi(tstr);
				tstr = strtok(NULL, " ");
				node->links[node->link_cnt].data3 = ahtoi(tstr);
				node->link_cnt++;
			}
		} else {
			/* skip for now */
		}
	}

	fclose(fp);
	return dev_list;
}


/*
 * save_scene_info
 *
 * Dump all the scene and device info to a file.
 */
void save_device_info(char *fname, device_t *dev_list)
{
	FILE *fp;
	int i;
	scene_t *scene;
	device_t *master;
	device_list_t *respd;
	/* timer_timerec_t *tl; */
	device_t *node;


	fp = fopen(fname, "w");
	if (fp == NULL) {
		printf("Failed to open %s\n", fname);
		return;
	}

	fprintf(fp, "#\n");
	fprintf(fp, "# ilink scene configuration file.\n");
	fprintf(fp, "#\n");

	node = dev_list;
	while (node) {
		fprintf(fp, "device_name=%s\n", node->name);
		fprintf(fp, "device_id=%02x.%02x.%02x\n",
				node->id[0],
				node->id[1],
				node->id[2]);
		fprintf(fp, "device_type=%d\n", node->type);
		fprintf(fp, "device_revision=%d.%d\n",
				(node->version & 0xf0) >> 4,
				(node->version & 0x0f));
		fprintf(fp, "device_local_level=%d\n", node->local_on_level);
		fprintf(fp, "device_local_ramp=%d\n", node->local_ramp_rate);
		fprintf(fp, "device_status=%d\n", node->status);
		for (i = 0; i < node->link_cnt; i++) {
			fprintf(fp, "device_link%03d=0x%02x %d %02x.%02x.%02x 0x%02x 0x%02x 0x%02x  - %s\n",
					i,
					node->links[i].flags,
					node->links[i].group_number,
					node->links[i].device_id[0],
					node->links[i].device_id[1],
					node->links[i].device_id[2],
					node->links[i].data1,
					node->links[i].data2,
					node->links[i].data3,
					device_name(dev_list, node->links[i].device_id));
		}
		fprintf(fp, "\n");
		node = node->next;
	}

	fprintf(fp, "\n");

#if 0
	scene = scene_list;
	while (scene) {
		master = scene->controller;
		respd = scene->responder_list;
		if (!master || !respd) {
			printf("Trying to save an invalid scene %s\n", scene->name);
			scene = scene->next;
			continue;
		}
		fprintf(fp, "scene=%s\n", scene->name);
		fprintf(fp, "group=%d\n", scene->group);
		fprintf(fp, "master=%02x.%02x.%02x\n",
				master->device_id[0],
				master->device_id[1],
				master->device_id[2]);

		i = 0;
		while(respd) {
			fprintf(fp, "responder%d=%02x.%02x.%02x 0x%02x 0x%02x 0x%02x\n", i,
					respd->device->device_id[0],
					respd->device->device_id[1],
					respd->device->device_id[2],
					respd->link.data1, respd->link.data2, respd->link.data3);
			respd = respd->next;
			i++;
		}
		fprintf(fp, "\n");
		scene = scene->next;
	}

	/* Write current timer list */
	if (timers) {
		tl = timers;
		fprintf(fp, "[timers]\n");
		while (tl) {
			if ((tl->flag0 & T_ACTIVE) == 0) {
				/* Skip records that aren't active for now */
				continue;
			}

			fprintf(fp, "timer-%03d=", tl->tid);

			/* What time? */
			if (tl->type == T_ABSOLUTE) {
				fprintf(fp, "%02d:%02d,", (tl->time / 60),
						((tl->time - (tl->time / 60) * 60)));
			} else if (tl->type == T_SUNRISE) {
				fprintf(fp, "Sunrise+%d,", tl->time);
			} else if (tl->type == T_SUNSET) {
				fprintf(fp, "Sunset+%d,", tl->time);
			} else {
				fprintf(fp, "Unknown,");
			}

			/*
			 * FIXME:
			 * Currently this output format only supports a command count
			 * of 1. At some point this all needs to be fixed to support
			 * any number of commands per timer.
			 */
			for (i = 0; i < tl->cmd_group.count; i++) {
				if (tl->cmd_group.cmds[i].type != T_INSTEON) {
					printf("Skipping command, only Insteon commands are "
							"supported at this time\n");
				} else {
					/* Print device ID or group number */
					fprintf(fp, "%02x.%02x.%02x:",
							tl->cmd_group.cmds[i].data[0],
							tl->cmd_group.cmds[i].data[1],
							tl->cmd_group.cmds[i].data[2]);
					if (tl->cmd_group.cmds[i].data[4] == ON) {
						fprintf(fp, "%d%%,",
								(tl->cmd_group.cmds[i].data[5] * 100) / 255);
					} else {
						fprintf(fp, "OFF,");
					}
				}
			}

			/* What days? */
			if (tl->dow & T_SATURDAY) {
				fprintf(fp, "Sa");
			}
			if (tl->dow & T_SUNDAY) {
				fprintf(fp, "Su");
			}
			if (tl->dow & T_MONDAY) {
				fprintf(fp, "M");
			}
			if (tl->dow & T_TUESDAY) {
				fprintf(fp, "Tu");
			}
			if (tl->dow & T_WEDNESDAY) {
				fprintf(fp, "W");
			}
			if (tl->dow & T_THURSDAY) {
				fprintf(fp, "Th");
			}
			if (tl->dow & T_FRIDAY) {
				fprintf(fp, "F");
			}

			/* Security? */
			if (tl->flag0 & T_SECURITY) {
				fprintf(fp, "*SEC\n");
			} else {
				fprintf(fp, "*NOSEC\n");
			}

			tl = tl->next;
		}
	}
#endif

	fclose (fp);
}


static char *device_name(device_t *dev_list, unsigned char *id)
{
	device_t *dev;

	/* Check for matching id */
	dev = dev_list;
	while (dev) {
		if (memcmp(id, dev->id, 3) == 0) {
			return dev->name;
		}
		dev = dev->next;
	}

	return NULL;
}

