/*
 * $RCSfile: link.c,v $  $Revision: 1.31 $  $Name:  $
 * $Id: link.c,v 1.31 2007/09/09 18:16:26 bpaauwe Exp $
 * $Author: bpaauwe $
 * $Date: 2007/09/09 18:16:26 $
 * ----------------------------------------------------------------------------
 *
 *  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:
 *    A program to build a copy of device link tables.
 * ----------------------------------------------------------------------------
 */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <memory.h>
#include <time.h>
#include "libilib/ilib.h"
#include "timers.h"
#include "scenes.h"

#define VERSION "link version 0.2.20a Copyright (C) 2006, Bob Paauwe"

device_t device_array[2000];
int last_device = -1;
timer_timerec_t *timers = NULL;

scene_t *scene_list = NULL;

void usage(char *prog)
{
	printf("usage: %s\n", prog);
	printf("     [-l <xx.xx.xx | all>]  list linktable\n");
	printf("     [-t <xx.xx.xx | all>]  test\n");
	printf("     [-q <xx.xx.xx | all>]  query (ping)\n");
	printf("     [-a <xx.xx.xx | all>]  assign to group\n");
	printf("     [-e <xx.xx.xx | all>]  do read ee\n");
	printf("     [-s <xx.xx.xx | all>]  status request\n");
	printf("     [-p <xx.xx.xx | all>]  program\n");
	printf("     [-b <xx.xx.xx | all> <address> <byte>]  write a byte to device memory\n");
	printf("     [-d <xx.xx.xx | all> <start> <size>] dump size bytes of memory from device\n");
	printf("     [-f <xx.xx.xx | all>]  list PLC link database\n");
	printf("     [-v <xx.xx.xx | all>]  verify device link database\n");
	printf("     [-x <xx.xx.xx | all>]  update device link database\n");
	printf("     [-i <xx.xx.xx> <group> <class>]  add id to PLC database\n");
	printf("     [-u <xx.xx.xx> <cmd> <data>] Send command to device\n");
	printf("     [-g <group> <cmd>]     Send a group broadcast message\n");
	printf("     [-o <file>]            Open scene file\n");
	printf("     [-j]                   Join with me broadcast message\n");
	printf("     [-w]                   add all devices to PLC database\n");
	printf("     [-c]                   clear PLC link database\n");
	printf("     [-m]                   enter monitor mode\n");
	printf("     [-V]                   print version\n");

}

main (int argc, char **argv)
{
	ilib_t *iplc;
	unsigned char test[100];
	unsigned char buffer[256];
	char f_name[1024];
	time_t tm;
	int len;
	int i, j;
	int cs;
	link_record_t entry;
	link_record_t link;
	scene_t *scene;
	char device_id[10];
	unsigned int start_addr;
	int list_tables = 0;
	int list_device = 0;
	int program = 0;
	int ee_read = 0;
	int status_request = 0;
	int assign = 0;
	int ping = 0;
	int monitor = 0;
	int dump = 0;
	int list_plc = 0;
	int add_id_plc[3];
	int set_byte[3];
	int clear_plc = 0;
	int group_bcast = 0;
	int group_cmd = 0;
	int join = 0;
	int verify = 0;
	int cmd = 0;
	int fill_db = 0;
	int update_db = 0;


	memset(device_id, 0, 10);
	set_byte[0] = 0;
	add_id_plc[0] = 0;
	strcpy (f_name, "ilink_scene.inf");

	if (argc > 0) {
		for (i = 1; i < argc; i++) {
			if (argv[i][0] == '-') {
				switch (argv[i][1]) {
					case 'V':
						printf("%s\n", VERSION);
						return 0;
						break;
					case 'h':
						usage(argv[0]);
						return 0;
						break;
					case 'v': /* verify a device link table */
						verify = 1;
						i++;
						if (strcasecmp(argv[i], "all") == 0) {
							device_id[0] = 0;
						} else {
							memcpy(&device_id[1], str_id_to_hex(argv[i]), 3);
							device_id[0] = 0xff;
						}
						break;
					case 'x': /* update a device link table */
						update_db = 1;
						i++;
						if (strcasecmp(argv[i], "all") == 0) {
							device_id[0] = 0;
						} else {
							memcpy(&device_id[1], str_id_to_hex(argv[i]), 3);
							device_id[0] = 0xff;
						}
						break;
					case 'f': /* list PLC link table */
						list_plc = 1;
						i++;
						if (strcasecmp(argv[i], "all") == 0) {
							device_id[0] = 0;
						} else {
							memcpy(&device_id[1], str_id_to_hex(argv[i]), 3);
							device_id[0] = 0xff;
						}
						break;
					case 'i': /* add id to plc link table */
						add_id_plc[0] = 1;
						i++;
						memcpy(&device_id[0], str_id_to_hex(argv[i]), 3);
						i++;
						add_id_plc[1] = atoi(argv[i]);
						i++;
						add_id_plc[2] = atoi(argv[i]);
						break;
					case 'c': /* clear plc link table */
						clear_plc = 1;
						break;
					case 'a': /* assign to group */
						assign = 1;
						i++;
						if (strcasecmp(argv[i], "all") == 0) {
							device_id[0] = 0;
						} else {
							memcpy(&device_id[1], str_id_to_hex(argv[i]), 3);
							device_id[0] = 0xff;
						}
						break;
					case 'g': /* group broadcast */
						group_bcast = atoi(argv[++i]);
						group_cmd = alpha_to_i(argv[++i]);
						break;
					case 'b': /* set byte */
						set_byte[0] = 1;
						i++;
						if (strcasecmp(argv[i], "all") == 0) {
							device_id[0] = 0;
						} else {
							memcpy(&device_id[1], str_id_to_hex(argv[i]), 3);
							device_id[0] = 0xff;
						}
						i++;
						set_byte[1] = alpha_to_i(argv[i]);
						i++;
						set_byte[2] = alpha_to_i(argv[i]);
						break;
					case 'l': /* list link tables */
						list_tables = 1;
						i++;
						if (strcasecmp(argv[i], "all") == 0) {
							device_id[0] = 0;
						} else {
							memcpy(&device_id[1], str_id_to_hex(argv[i]), 3);
							device_id[0] = 0xff;
						}
						break;
					case 'u': /* Control single device */
						cmd = 1;
						i++;
						memcpy(&device_id[1], str_id_to_hex(argv[i]), 3);
						i++;
						set_byte[1] = alpha_to_i(argv[i]);
						i++;
						set_byte[2] = alpha_to_i(argv[i]);
						break;
					case 'p': /* program device */
						program = 1;
						i++;
						if (strcasecmp(argv[i], "all") == 0) {
							device_id[0] = 0;
						} else {
							memcpy(&device_id[1], str_id_to_hex(argv[i]), 3);
							device_id[0] = 0xff;
						}
						break;
					case 't': /* test device */
						list_device = 1;
						i++;
						if (strcasecmp(argv[i], "all") == 0) {
							device_id[0] = 0;
						} else {
							memcpy(&device_id[1], str_id_to_hex(argv[i]), 3);
							device_id[0] = 0xff;
						}
						break;
					case 's': /* status request */
						status_request = 1;
						i++;
						if (strcasecmp(argv[i], "all") == 0) {
							device_id[0] = 0;
						} else {
							memcpy(&device_id[1], str_id_to_hex(argv[i]), 3);
							device_id[0] = 0xff;
						}
						break;
					case 'e': /* do eeread command */
						ee_read = 1;
						i++;
						if (strcasecmp(argv[i], "all") == 0) {
							device_id[0] = 0;
						} else {
							memcpy(&device_id[1], str_id_to_hex(argv[i]), 3);
							device_id[0] = 0xff;
						}
						break;
					case 'd': /* do memory dump */
						dump = 1;
						i++;
						if (strcasecmp(argv[i], "all") == 0) {
							device_id[0] = 0;
						} else {
							memcpy(&device_id[1], str_id_to_hex(argv[i]), 3);
							device_id[0] = 0xff;
						}
						start_addr = alpha_to_i(argv[++i]);
						dump = alpha_to_i(argv[++i]);
						break;
					case 'q': /* do ping command */
						ping = 1;
						i++;
						if (strcasecmp(argv[i], "all") == 0) {
							device_id[0] = 0;
						} else {
							memcpy(&device_id[1], str_id_to_hex(argv[i]), 3);
							device_id[0] = 0xff;
						}
						break;
					case 'j': /* Join/detect device */
						join = 1;
						break;
					case 'm': /* Monitor mode */
						monitor = 1;
						break;
					case 'w': /* fill PLC database */
						fill_db = 1;
						break;
					case 'o': /* Open file instead of scene_info.inf */
						i++;
						strcpy (f_name, argv[i]);
						break;
					default:
						usage(argv[0]);
						return 0;
						break;
				}
			}
		}
	}

	if ((iplc = ilib_open(USE_HIDDEV, "/dev/hiddev0")) != NULL) {
	/* if ((iplc = ilib_open(USE_IPLC, "/dev/usb/iplc0")) != NULL) { */
		/* Send some test commands */
		if (ibios_get_version(iplc, &test[0], buffer) == 0) {
			printf("PLC ID: %s   Firmware version %02x\n", buffer, test[0]);
		} else {
			printf("Command failed\n");
		}

		get_scene_info(f_name); /* get scene definitions from file */
		build_tables(); /* build all the device link tables */

		/* Do what the command line args tell us to do */

		if (list_tables) {
			/* Read the link tables from a device or all devices */
			for (i = 0; i <= last_device; i++) {
				if (!device_id[0] ||
					(memcmp(&device_id[1], device_array[i].device_id, 3) == 0)){
					printf("DEVICE_NAME=%s\n", device_array[i].device_name);
					printf("DEVICE_ID=%02x.%02x.%02x\n",
						device_array[i].device_id[0],
						device_array[i].device_id[1],
						device_array[i].device_id[2]);
					j = 0;
					do {
						memset(&link, 0, sizeof(link));
						get_device_link(iplc, device_array[i].device_id, j,
								&link);
						printf("DEVICE_LINK%d=0x%02x %d %02x.%02x.%02x 0x%02x "
								"0x%02x 0x%02x\n", j, link.flags,
								link.group_number, link.device_id[0],
								link.device_id[1], link.device_id[2],
								link.data1, link.data2, link.data3);
						j++;
					} while (link.flags != 0);
					/* } while (j < 128); */
					printf("\n");
				}
			}
		}

		if (list_device) {
			/* list the newly built link tables */
			for (i = 0; i <= last_device; i++) {
				if (!device_id[0] ||
					(memcmp(&device_id[1], device_array[i].device_id, 3) == 0)){
					printf("DEVICE_NAME=%s\n", device_array[i].device_name);
					printf("DEVICE_ID=%02x.%02x.%02x\n",
						device_array[i].device_id[0],
						device_array[i].device_id[1],
						device_array[i].device_id[2]);
					for (j = 0; j < device_array[i].num_of_entries; j++) {
						printf("DEVICE_LINK%d=0x%02x %d %02x.%02x.%02x 0x%02x "
								"0x%02x 0x%02x\n", j,
								device_array[i].db_records[j].flags,
								device_array[i].db_records[j].group_number,
								device_array[i].db_records[j].device_id[0],
								device_array[i].db_records[j].device_id[1],
								device_array[i].db_records[j].device_id[2],
								device_array[i].db_records[j].data1,
								device_array[i].db_records[j].data2,
								device_array[i].db_records[j].data3);
					}
					printf("\n");
				}
			}
		}

		/* danger! write link table to a device */
		if (program) {
			for (i = 0; i <= last_device; i++) {
				if (!device_id[0] ||
					(memcmp(&device_id[1], device_array[i].device_id, 3) == 0)){
					program_device(iplc, &device_array[i]);
				}
			}
		}

		/* EE Read command */
		if (ee_read) {
			for (i = 0; i <= last_device; i++) {
				if (!device_id[0] ||
					(memcmp(&device_id[1], device_array[i].device_id, 3) == 0)){
					j = salad_send_message(iplc, DO_READ_EE, 0x00,
							device_array[i].device_id);
					printf("%02x.%02x.%02x responded with 0x%04x\n",
							device_array[i].device_id[0],
							device_array[i].device_id[1],
							device_array[i].device_id[2], j);
				}
			}
		}

		/* Status Request command */
		if (status_request) {
			for (i = 0; i <= last_device; i++) {
				if (!device_id[0] ||
					(memcmp(&device_id[1], device_array[i].device_id, 3) == 0)){
					j = salad_send_message(iplc, STATUS_REQUEST, 0x00,
							device_array[i].device_id);
					printf("%02x.%02x.%02x responded with 0x%04x\n",
							device_array[i].device_id[0],
							device_array[i].device_id[1],
							device_array[i].device_id[2], j);
				}
			}
		}

		/* Assign to Group command */
		if (assign) {
			for (i = 0; i <= last_device; i++) {
				if (!device_id[0] ||
					(memcmp(&device_id[1], device_array[i].device_id, 3) == 0)){
					j = salad_send_message(iplc, ASSIGN_TO_GROUP, 0x04,
							device_array[i].device_id);
					printf("%02x.%02x.%02x responded with 0x%04x\n",
							device_array[i].device_id[0],
							device_array[i].device_id[1],
							device_array[i].device_id[2], j);
				}
			}
		}

		/* Ping a device */
		if (ping) {
			for (i = 0; i <= last_device; i++) {
				if (!device_id[0] ||
					(memcmp(&device_id[1], device_array[i].device_id, 3) == 0)){
					j = insteon_send_ping(iplc, device_array[i].device_id,
							buffer, 4);
					if (j == 0x1000) {
						printf("%-25.25s (%02x.%02x.%02x)",
							device_array[i].device_name,
							device_array[i].device_id[0],
							device_array[i].device_id[1],
							device_array[i].device_id[2]);
						printf("  %s Rev = %d.%d  0x%02x\n",
							device_type_name(buffer[5], buffer[6]),
							(buffer[7] >> 4), (buffer[7] & 0x0f), buffer[9]);
					} else {
						printf("%-25.25s (%02x.%02x.%02x)",
							device_array[i].device_name,
							device_array[i].device_id[0],
							device_array[i].device_id[1],
							device_array[i].device_id[2]);
						printf(" no response.\n");
					}
				}
			}
		}

		/*
		 * Monitor mode sits in an infinite loop, reading and displaying
		 * data from the PLC.
		 */
		if (monitor) {
			while (1) {
				if ((j = plc_read_response(iplc, buffer, 256, 0x4f)) > 0) {
					time(&tm);
					strftime(test, 100, "%F %T", localtime(&tm));
					printf("%s: ", test);
					if (buffer[1] == 0x02) {
						printf(" ** %02x.%02x.%02x -> %02x.%02x.%02x  0x%02x "
								"%s %d %s\n",
							buffer[5], buffer[6], buffer[7],
							buffer[2], buffer[3], buffer[4],
							buffer[8], command(buffer[9]), buffer[10],
							flags(buffer[8]));
					} else if (buffer[1] == 0x03) {
						printf(" rr %02x.%02x.%02x -> %02x.%02x.%02x  0x%02x "
								"%s %d %s\n",
							buffer[5], buffer[6], buffer[7],
							buffer[2], buffer[3], buffer[4],
							buffer[8], command(buffer[9]), buffer[10],
							flags(buffer[8]));
					} else {
						printf(" ?? %02x.%02x.%02x -> %02x.%02x.%02x  0x%02x "
								"%s %d %s\n",
							buffer[5], buffer[6], buffer[7],
							buffer[2], buffer[3], buffer[4],
							buffer[8], command(buffer[9]), buffer[10],
							flags(buffer[8]));
					}
				}
				usleep(10000);
			}
		}

		if(dump) {
			printf("Dump %d bytes starting at 0x%04x\n", dump, start_addr);
			for (i = 0; i <= last_device; i++) {
				if (!device_id[0] ||
					(memcmp(&device_id[1], device_array[i].device_id, 3) == 0)){
					get_memory_dump(iplc, device_array[i].device_id,
							start_addr, dump);
				}
			}
		}


		/* Set a byte in the devices memory */
		if (set_byte[0]) {
			for (i = 0; i <= last_device; i++) {
				if (!device_id[0] ||
					(memcmp(&device_id[1], device_array[i].device_id, 3) == 0)){
					salad_send_message(iplc, SET_ADDR_MSB,
							(set_byte[1] >> 8) & 0xff,
							device_array[i].device_id);
					salad_send_message(iplc, PEEK, (set_byte[1] & 0xff),
							device_array[i].device_id);
					salad_send_message(iplc, POKE, (set_byte[2] & 0xff),
							device_array[i].device_id);
				}
			}
		}

		if (list_plc) {
			for (i = 0; i <= last_device; i++) {
				if (!device_id[0] ||
					(memcmp(&device_id[1], device_array[i].device_id, 3) == 0)){
					cs = iplc_get_entry(iplc, device_array[i].device_id, 1,
							buffer);
					printf("%02x.%02x.%02x: ", device_array[i].device_id[0],
							device_array[i].device_id[1],
							device_array[i].device_id[2]);
					if (buffer[0] == 0) {
						printf("DEL ");
					} else if (buffer[0] == 0x2) {
						printf("SLV ");
					} else if (buffer[0] == 0x03) {
						printf("MST ");
					} else {
						printf("OTH ");
					}
					printf("group(%d)  0x%02x 0x%02x 0x%02x\n",
							buffer[4], buffer[5], buffer[6], buffer[7]);
					/* Get additional entries for this devcie id */
					for(j = 2; j <= cs; j++) {
						iplc_get_entry(iplc, device_array[i].device_id, j,
							buffer);
						printf("%02x.%02x.%02x: ", device_array[i].device_id[0],
							device_array[i].device_id[1],
							device_array[i].device_id[2]);
						if (buffer[0] == 0) {
							printf("DEL ");
						} else if (buffer[0] == 0x2) {
							printf("SLV ");
						} else if (buffer[0] == 0x03) {
							printf("MST ");
						} else {
							printf("OTH ");
						}
						printf("group(%d)  0x%02x 0x%02x 0x%02x\n",
							buffer[4], buffer[5], buffer[6], buffer[7]);
					}
				}
			}
		}

		if (add_id_plc[0]) {
			/* iplc_clear_entry(iplc, device_id); */
			iplc_add_id(iplc, device_id, add_id_plc[1], add_id_plc[2]);
		}

		if (clear_plc) {
			iplc_clear_links(iplc);
		}

		if (group_bcast) {
			unsigned char to[3];
			unsigned char from[3];

			to[0] = (unsigned char)group_bcast;
			to[1] = (unsigned char)group_bcast;
			to[2] = (unsigned char)group_bcast;
			from[0] = 0x01; /* keypadlinc group #4 */
			from[1] = 0x3e;
			from[2] = 0x0a;

			salad_send_message_group(iplc, (unsigned char)group_cmd,
					(unsigned char)group_bcast, to, from);
		}

		/* Device detection type code */
		if (join) {
			if (insteon_detect_device(iplc, buffer) != -1) {
				printf("Found device: %02x.%02x.%02x %s rev %d.%d\n",
						buffer[2], buffer[3], buffer[4],
						device_type_name(buffer[5], buffer[6]),
						(buffer[7] >> 4), (buffer[7] & 0x0f));

			} else {
				printf("device detect timed out\n");
			}
		}


		/* Update device link table */
		if (update_db) {
			for (i = 0; i <= last_device; i++) {
				if (!device_id[0] ||
					(memcmp(&device_id[1], device_array[i].device_id, 3) == 0)){
					printf("Checking %02x.%02x.%02x (%s)\n",
							device_array[i].device_id[1],
							device_array[i].device_id[2],
							device_array[i].device_id[3],
							device_array[i].device_name);

					if ((device_array[i].device_type == 0x0000) ||
							(device_array[i].device_type == 0x0001)) {
						printf("Skipping PowerLinc Controller.\n");
					} else {
						update_device_links(iplc, &device_array[i]);
					}
				}
			}
		}


		/* Verify device link table */
		if (verify) {
			for (i = 0; i <= last_device; i++) {
				if (!device_id[0] ||
					(memcmp(&device_id[1], device_array[i].device_id, 3) == 0)){
					if ((device_array[i].device_type == 0x0000) ||
							(device_array[i].device_type == 0x0001)) {
						printf("Skipping PowerLinc Controller.\n");
					} else {
						verify_device_links(iplc, &device_array[i]);
					}
				}
			}
		}

		/* Fill PLC database with each device as slave */
		if (fill_db) {
			for (i = 0; i <= last_device; i++) {
				if ((device_array[i].device_type == 0x0000) ||
						(device_array[i].device_type == 0x0001)) {
					printf("Skipping PowerLinc Controller.\n");
				} else {
					iplc_add_id(iplc, device_array[i].device_id, 1, 4);
				}
			}
		}

		/* Send command to device */
		if (cmd) {
			insteon_send_msg(iplc, set_byte[1], set_byte[2], &device_id[1]);
		}



#if 0
		printf("Sending command to read date/time\n");
		for (i = 0; i < 2; i++) {
			memset(buffer, 0, 256);
			len = ibios_read_memory(iplc, 0x015a, 0x0007, buffer);
			printf("%02d/%02d/%02d %02d:%02d:%02d  ",
					buffer[1], buffer[2], buffer[0], buffer[4], buffer[5],
					buffer[6]);
			switch (buffer[3]) {
				case 0x01: printf("Monday\n"); break;
				case 0x02: printf("Tuesday\n"); break;
				case 0x04: printf("Wednesday\n"); break;
				case 0x08: printf("Thursday\n"); break;
				case 0x10: printf("Friday\n"); break;
				case 0x20: printf("Saturday\n"); break;
				case 0x40: printf("Sunday\n"); break;
				default: printf("\n"); break;
			}
#endif

		ilib_close(iplc);
	}
}



/*
 * status_message
 *
 * print a status message
 */
void status_message(char *msg)
{
	printf("%s\n", msg);
}

int file_changed(int save)
{
	return 0;
}

void update_kpl_progress(int amount)
{
	return;
}

void progress_bar_update()
{
	return;
}
