#include <ctype.h>
#include <stdlib.h>
#include <errno.h>
#include <signal.h>
#include <string.h>
#include <sys/time.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <termios.h>
#include <unistd.h>
#include <fcntl.h>
#include <stdarg.h>
#include <assert.h>
#include "x10io.H"
#include "cp290.H"
#include "general.H"

static const char *rcs_id = "@(#) $Id: cp290.C 1 2022-02-25 17:20:12Z perette $" ;

typedef enum commands_t {
	cp290_setBaseHouseCode = 0,
	cp290_set = 1
} commands;

typedef enum setcmd_t {
	set_on = 2,
	set_off = 3,
	set_level = 5
} setcommand;

static char housecode_to_id [] = {
	0x6,
	0xe,
	0xc,
	0xd,
	0xe,
	0xf,
	0x5,
	0xd,
	0x7,
	0xf,
	0x3,
	0xb,
	0x0,
	0x8,
	0xc
};

static char id_to_housecode [16];

#define get_id(hc) ((housecode_to_id [toupper (hc) - 'A']) << 4)
#define get_housecode(id) (id_to_housecode [(id) >> 4])




CP290::CP290 (CONFIGURATION *config) : X10 (config)
{
	char c;
	log (INFO, "Initializing CP290");
	for (c = 'A'; c <= 'P'; c++) {
		int id = housecode_to_id [c - 'A'];
		id_to_housecode [id] = c;
	}
}


CP290::~CP290()
{
}




void CP290::ConfigDevice (struct termios *tio)
{
        // tio->c_cflag |= CSTOPB;
       	tio->c_cflag &= CRTSCTS;
        tio->c_iflag &= ICRNL;
        cfsetispeed(tio, B600);
        cfsetospeed(tio, B600);
}





int CP290::CreateCommand (int command, int hc, int uc)
{
	char buff[50], *ptr;

	memset (buff, 0xff, 16);
	ptr = &(buff [16]);
	*(ptr++) = cp290_set;
	*ptr = 0;
	switch (command) {
		case X10_ON:
			*ptr |= set_on;
			break;
		case X10_OFF:
		case X10_ALLOFF:
			*ptr |= set_off;
			break;
		case X10_SELECTUNIT:
			return 0;
		default:
			return 1;
	}
	ptr++;
	*(ptr++) = get_id (hc);

	int unitflags;
	switch (command) {
		case X10_ON:
		case X10_OFF:
			unitflags = 1 << (16 - uc);
			break;
		case X10_ALLOFF:
			unitflags = 0xffff;
			break;
		default:
			assert (0);
	}
	*(ptr++) = unitflags & 0xff;
	*(ptr++) = (unitflags >> 8) & 0xff;

	char *sumptr;
	int cksum = 0;
	for (sumptr = &(buff [17]); sumptr < ptr; sumptr++)
		cksum += *sumptr;
	*(ptr++) = cksum & 0xff;
	return (QueueBinary (command, hc, uc, ptr - buff, buff));
}






int CP290::ParseCommand (int size, char *cmd)
{
	if (size < 8) return 0;

	if (*cmd != '\377') {
		// toss out up to a 0xff
		int eat;
		for (eat = 1; eat < size && cmd [eat] != '\377'; eat++);
		return (eat);
	}

	// Check for an ACK message by checking to see if the first byte
	// past the end of ack is 0xff (it's an ACK) or not (it's an
	// upload).
	if (cmd [7] == '\377')
		return (7);

	if (size < 12)
		return 0;	

	int i, sum;
	for (i = 0; i < 6; i++) {
		if (cmd [i] != '\377')
			return (i);
	}
	for (sum = 0, i = 7; i < 11; i++) {
		sum = sum + (unsigned char) cmd [i];
	}
	if ((sum & 0xff) != (unsigned char) cmd [11]) {
		log (WARN, "Invalid checksum: calculated %d, expected %d",
				(sum & 0xff), (int) cmd [11]);
		return 12;
	}
	int hc = id_to_housecode [cmd [8] >> 4];
	const char *func = "unknown";
	switch (cmd [7] & 0xf) {
		case 0x02:
			func = "on";
			break;
		case 0x03:
			func = "off";
			break;
		case 0x05:
			func = "dim";
			break;
	}
	int unitflags = (((int) cmd [9]) << 8) | cmd [8];
	for (i = 1; i <= 16; i++) {
		if (unitflags & (1 << (16 - i))) {
			log (INFO, "Received: %c%d %s",
				hc + 'A', i, func);
		}
	}
	
	return 12;
}




