/*
 * $RCSfile: spyder.c,v $  $Revision: 1.6 $  $Name:  $
 * $Id: spyder.c,v 1.6 2007/09/09 18:16:26 bpaauwe Exp $
 * $Author: bpaauwe $
 * $Date: 2007/09/09 18:16:26 $
 * ----------------------------------------------------------------------------
 *
 *  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:
 *    A program that converts my existing file format into a new one.
 * ----------------------------------------------------------------------------
 */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <memory.h>
#include <time.h>
#include "libilib/ilib.h"
#include "device.h"

#define VERSION "file_cvt version 0.2.20a Copyright (C) 2007, Bob Paauwe"

typedef struct _responders_t {
	char *name;
	unsigned char id[3];
	link_record_t link;
	struct _responders_t *next;
} responders_t;

typedef struct _group_t {
	char *name;
	int group;
	unsigned char id[3];
	responders_t *list;
	struct _group_t *next;
} group_t;

device_t *read_file(char *fname);
device_t *find_device(device_t *list, unsigned char *id);
link_record_t *find_link(device_t *dev, unsigned char *id, int group);
void print_link(link_record_t *link);
group_t *add_group(group_t *group, device_t *dev, device_t *resp,
		link_record_t *link, int gnum);
device_t *get_device(device_t *dev_list, char *name);
void print_devices(device_t *dev_list);
void add_link(device_t *dev, char *mst, int group, char *resp, char *on,
		char *ramp);
void get_plc_links(ilib_t *iplc, device_t *dev);

void usage(char *prog)
{
	printf("usage: %s <old_file> [new_file]\n", prog);
}

main (int argc, char **argv)
{
	ilib_t *iplc;
	device_t *devices, *dev_ptr;
	char f_name[256];
	char buffer[15];
	unsigned char version;
	int i;
	unsigned short response;
	link_record_t entry;

	strcpy (f_name, "device_info.txt");
	devices = read_file(f_name);
	if (devices == NULL) {
		printf("No known devices, can't do anything\n");
		return -2;
	}

	dev_ptr = devices;
	print_devices(dev_ptr);
	list_groups(dev_ptr);
	/* save_device_info(f_name, devices); */

}



/*
 * list_groups
 *
 * Display all the groups in an easy to read format.
 */
int list_groups(device_t *devices)
{
	device_t *dev;
	device_t *resp;
	link_record_t *link;
	int i, g;
	group_t *group = NULL;
	responders_t *rnode;

	/*
	 * Search for every master link record, output it and verify that
	 * there is a coresponding record in the responder.
	 */
	dev = devices;
	while (dev) {
		for (i = 0; i < dev->link_cnt; i++) {
			if (dev->links[i].flags & 0x40) {
				resp = find_device(devices, dev->links[i].device_id);
				/* Find matching link in responder */
				if (resp) {
					link = find_link(resp, dev->id, dev->links[i].group_number);
					group = add_group(group, dev, resp, link,
							dev->links[i].group_number);
				} else {
					printf("Unkown device in link table\n");
				}

			}
		}
		dev = dev->next;
	}

	i = 1;
	g = 1;
	while (group) {
		rnode = group->list;
		while (rnode) {
			printf("%04d|G%04d|%02x.%02x.%02x|%02x|%02x|%02x.%02x.%02x|%02x|%02x|%02x\n",
					g, i, rnode->id[0], rnode->id[1], rnode->id[2],
					rnode->link.flags, rnode->link.group_number,
					rnode->link.device_id[0],
					rnode->link.device_id[1],
					rnode->link.device_id[2],
					rnode->link.data1, rnode->link.data2, rnode->link.data3);
			rnode = rnode->next;
			g++;
		}
		i++;
		/*
		printf("%s  GROUP:%d\n", group->name, group->group);
		while (rnode) {
			printf("   %-25.25s ON %3d%% @ %2d\n",
					rnode->name,
					((rnode->link.data1 * 100) / 255), rnode->link.data2);
			rnode = rnode->next;
		}
		printf("\n");
		*/
		group = group->next;
	}

}


/*
 * get_device_link
 *
 * link_num is zero based.
 */
int get_device_link(ilib_t *iplc, unsigned char *dev_id,
		int link_num, link_record_t *entry)
{
	unsigned short laddr;
	int ret;
	int i;

	memset(entry, 0, sizeof(link_record_t));

	/* Link data base grows downward from 0x0ff8 and are 8 bytes each */
	laddr = 0x0ff8 - (link_num * 8);

	if ((ret = insteon_read_byte(iplc, dev_id, laddr++)) != -1) {
		entry->flags = ret & 0xff;
		if (!(ret & 0x80)) { /* record not in use */
			return 0;
		} else if ((ret & 0x02) == ret) {
			/* record was in use, but now deleted */
			entry->group_number = 0;
			entry->device_id[0] = 0;
			entry->device_id[1] = 0;
			entry->device_id[2] = 0;
			entry->data1 = 0;
			entry->data2 = 0;
			entry->data3 = 0;
			return entry->flags;
		}

		/* Read link group number */
		if ((ret = insteon_read_byte(iplc, dev_id, laddr++)) != -1) {
			entry->group_number = ret & 0xff;
		} else {
			return -1;
		}

		/* Read link device id */
		for (i = 0; i < 3; i++) {
			if ((ret = insteon_read_byte(iplc, dev_id, laddr++)) != -1) {
				entry->device_id[i] = ret & 0xff;
			} else {
				return -1;
			}
		}

		/* Read link data 1 byte */
		if ((ret = insteon_read_byte(iplc, dev_id, laddr++)) != -1) {
			entry->data1 = ret & 0xff;
		} else {
			return -1;
		}

		/* Read link data 2 byte */
		if ((ret = insteon_read_byte(iplc, dev_id, laddr++)) != -1) {
			entry->data2 = ret & 0xff;
		} else {
			return -1;
		}

		/* Read link data 3 byte */
		if ((ret = insteon_read_byte(iplc, dev_id, laddr++)) != -1) {
			entry->data3 = ret & 0xff;
		} else {
			return -1;
		}
	} else {
		return -1;
	}

	return entry->flags;
}


/*
 * set_device_link
 *
 * write a new device link into a device's link table.
 */
int set_device_link(ilib_t *iplc, unsigned char *dev_id,
		int link_num, link_record_t *entry)
{
	unsigned short laddr;
	int ret;

	/* Link data base grows downward from 0x0ff8 and are 8 bytes each */
	laddr = 0x0ff8 - (link_num * 8);

	if ((ret = insteon_write_byte(iplc, dev_id, laddr++, entry->flags)) == -1) {
		printf("set_device_link: Failed to send message to %02x.%02x.%02x\n",
				dev_id[0], dev_id[1], dev_id[2]);
		return 0;
	}

	ret = 0;
	ret |= insteon_write_byte(iplc, dev_id, laddr++, entry->group_number);
	ret |= insteon_write_byte(iplc, dev_id, laddr++, entry->device_id[0]);
	ret |= insteon_write_byte(iplc, dev_id, laddr++, entry->device_id[1]);
	ret |= insteon_write_byte(iplc, dev_id, laddr++, entry->device_id[2]);
	ret |= insteon_write_byte(iplc, dev_id, laddr++, entry->data1);
	ret |= insteon_write_byte(iplc, dev_id, laddr++, entry->data2);
	ret |= insteon_write_byte(iplc, dev_id, laddr++, entry->data3);
	if (ret == -1) {
		printf("set_device_link: Failed to write link %d to %02x.%02x.%02x\n",
				link_num, dev_id[0], dev_id[1], dev_id[2]);
	}
	return ret;
}


/*
 * clear_device_link
 *
 * Clear a device link in a device's link table.
 */
int clear_device_link(ilib_t *iplc, unsigned char *dev_id, int link_num)
{
	unsigned short laddr;
	int ret;

	/* Link data base grows downward from 0x0ff8 and are 8 bytes each */
	laddr = 0x0ff8 - (link_num * 8);

	/* Write the record has been used before bit in the flag */
	if ((ret = insteon_write_byte(iplc, dev_id, laddr, 0x00)) == -1) {
		printf("clear_device_link: Failed to clear link # %d on "
				"%02x.%02x.%02x\n", link_num, dev_id[0], dev_id[1], dev_id[2]);
		return -1;
	}
	return 0;
}

/*
 * find_link
 *
 * Locate a link record in the device's link table that matches the given
 * device id and group.
 */
link_record_t *find_link(device_t *dev, unsigned char *id, int group)
{
	int i;

	for (i = 0; i < dev->link_cnt; i++) {
		if (memcmp(dev->links[i].device_id, id, 3) == 0) {
			if (dev->links[i].group_number == group) {
				if ((dev->links[i].flags & 0x40) == 0) {
					return  &dev->links[i];
				}
			}
		}
	}

	return NULL;
}


/*
 * add_group
 *
 * Add a new entry into the group list. This may add a new group or
 * add a new responder to an existing group list.
 */
group_t *add_group(group_t *group, device_t *dev, device_t *resp,
		link_record_t *link, int gnum)
{
	group_t *gnode;
	responders_t *rnode;

	if (group == NULL) {
		/* empty list */
		gnode = (group_t *)malloc(sizeof(group_t));
	} else {
		/* Search for match */
		gnode = group;
		while (gnode) {
			if (strcmp(gnode->name, dev->name) == 0) {
				if (gnode->group == gnum) {
					/* Match, add new responder */
					rnode = (responders_t *)malloc(sizeof(responders_t));
					rnode->name = resp->name;
					memcpy(rnode->id, resp->id, 3);
					if (link) {
						memcpy(&(rnode->link), link, sizeof(link_record_t));
					} else {
						memset(&(rnode->link), 0, sizeof(link_record_t));
					}
					rnode->next = gnode->list;
					gnode->list = rnode;
					return group;
				}
			}
			gnode = gnode->next;
		}
		/* No match found, add new node */
		gnode = (group_t *)malloc(sizeof(group_t));
	}

	gnode->name = dev->name;
	gnode->group = gnum;
	memcpy(gnode->id, dev->id, 3);
	gnode->next = group;
	gnode->list = (responders_t *)malloc(sizeof(responders_t));
	gnode->list->name = resp->name;
	memcpy(gnode->list->id, resp->id, 3);
	if (link) {
		memcpy(&(gnode->list->link), link, sizeof(link_record_t));
	} else {
		memset(&(gnode->list->link), 0, sizeof(link_record_t));
	}
	gnode->list->next = NULL;

	return gnode;
}


/*
 * get_device_info
 *
 * Query a device for it firmware revision.
 */
int get_device_info(ilib_t *iplc, device_t *dev_ptr)
{
	unsigned short response;
	unsigned char buffer[15];

	dev_ptr->status &= ~(DEVICE_UNREACHABLE | DEVICE_OK);
	response = insteon_send_ping(iplc, dev_ptr->id, buffer, 10);

	printf("  %s Rev = %d.%d  0x%02x\n",
			device_type_name(buffer[5], buffer[6]),
			(buffer[7] >> 4), (buffer[7] & 0x0f), buffer[9]);
	if (response != 0x1000) {
		printf("  No response from device, skipping. (0x%04x)\n", response);
		dev_ptr->status |= DEVICE_UNREACHABLE;
		dev_ptr->link_cnt = 0;
	} else {
		dev_ptr->status |= DEVICE_OK;
		dev_ptr->version = buffer[7];
		dev_ptr->type = (buffer[5] << 8) | buffer[6];
	}
	return 0;
}


/*
 * Return a pointer to the device node that matches the passed in device
 * name.
 */
device_t *get_device(device_t *dev_list, char *name)
{
	device_t *dev;

	while (dev_list) {
		if (strncasecmp(name, dev_list->name, strlen(name)) == 0) {
			dev = (device_t *)malloc(sizeof(device_t));
			memcpy(dev, dev_list, sizeof(device_t));
			dev->next = NULL;
			return dev;
		}
		dev_list = dev_list->next;
	}

	return NULL;
}


/*
 * find_device
 *
 * Given a device id, check the list to see if it exist. Return the pointer
 * to the device.
 */
device_t *find_device(device_t *dev_list, unsigned char *name)
{
	device_t *dev;
	unsigned char id[3];

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

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

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

	return NULL;
}

void print_devices(device_t *dev_list)
{
	int i = 1;
	while (dev_list) {
		printf("%04d|%02x.%02x.%02x|%04d|%d.%d|%03d|%03d|%02x|%s\n",
				i, dev_list->id[0], dev_list->id[1], dev_list->id[2],
				dev_list->type,
				(dev_list->version & 0xf0) >> 4, (dev_list->version & 0x0f),
				dev_list->local_on_level, dev_list->local_ramp_rate,
				dev_list->link_cnt, dev_list->name);

		dev_list = dev_list->next;
		i++;
	}
}

/*
 * Print a link record.
 */
void print_link(link_record_t *link)
{
	if (link) {
		printf("%s %d %02x.%02x.%02x 0x%02x 0x%02x 0x%02x  / 0x%02x\n",
				(link->flags & 0x40) ? "MST" : "SLV",
				link->group_number,
				link->device_id[0],
				link->device_id[1],
				link->device_id[2],
				link->data1,
				link->data2,
				link->data3, link->flags);
	}
}
