#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 "x10io.H"
#include "x10map.H"

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

const short HC[] = {0x06, 0x0e, 0x02, 0x0a, 0x01
const char *commands[] = {"A0", "L1", "ON", "OF", "DI", "BR", "L0",
        "HR", "HA", "P0", "P1", "S1", "S0", "SR"};


CM11A::CM11A(char *tty)
{
	struct termios tio;

	ac = open(tty, O_RDWR | O_EXCL | O_NOCTTY | O_NONBLOCK);
	tcgetattr(ac, &tio);
	cfmakeraw(&tio);
	tio.c_iflag = IGNBRK | IGNPAR | ISTRIP | ICRNL;
	tio.c_oflag = OLCUC;
	tio.c_cflag = CS8 | CSTOPB | CREAD | CLOCAL | CRTSCTS;
	tio.c_lflag = 0;
	cfsetispeed(&tio, B2400);
	cfsetospeed(&tio, B2400);
	tcsetattr(ac, TCSANOW, &tio);
	// if (write(ac, "\r", 1) != 1) {
	// 	printf("Initial write failed\n");
	// 	exit(1);
	// }
	// fclose(ac);
	sprintf(ctty, "%s", tty);
	q = NULL;
	tail = NULL;
	rpend = 0;
	shutdown = 0;
	strcpy(rbuf, "");
	map = new X10map();
	printf("Using device: %s\n", tty); fflush(stdout);
}





int CM11A::ProcessPacket(int s)
{
	int retval;
	int buf[64];

	retval = recvfrom(s, buf, sizeof(buf)-1, 0, NULL, NULL);
	buf[retval] = '\0';
	if (buf[0] == -1) {
		printf("Received shutdown packet.\n");
		return 0;
	}
	Queue(buf[1], buf[2], buf[3]);
	return 0;
}


int CM11A::Queue(int hc, int uc, int cmd)
{
	if (tail == NULL) {
		tail = (struct x10cmdq *)malloc(sizeof(struct x10cmdq));
		tail->next = NULL;
		q = tail;
	} else {
		tail->next = (struct x10cmdq *)malloc(sizeof(struct x10cmdq));
		tail = tail->next;
	}
	
	sprintf(tail->cmd, "%c%02d\r", hc, uc);
	tail->pending = 0;
	tail->tries = 5;
	tail->lasttry = 0;
	printf("Queued command: %s\n", tail->cmd);
	tail->next = (struct x10cmdq *)malloc(sizeof(struct x10cmdq));
	tail = tail->next;
	tail->next = NULL;
	sprintf(tail->cmd, "%c%s\r", hc, commands[cmd]);
	tail->pending = 0;
	tail->tries = 5;
	tail->lasttry = 0;
	printf("Queued command: %s\n", tail->cmd); fflush(stdout);
	return 0;
}



int CM11A::Send()
{
	if (q->pending) return 0;
	// printf("Sending command: %s\n", q->cmd);
	write(ac, q->cmd, 4);
	q->pending = 1;
	q->tries--;
	q->lasttry = time(NULL);
	rpend = 1;
	usleep(10);
	return 0;
}


int CM11A::ProcessInput(char *data)
{
	char cmd[16];		// More than enough space.
	char *endCmd;
	chat *startCmd;

	strcat(rbuf, data);	// Append new data
	// printf("Current rbuf: <%s>\n", rbuf);
	while (endCmd = strstr(rbuf, "\n")) {
		// We have a full command received.  Extract it.
		int length = endCmd - rbuf;
// deal with overflow

		// Resilience: Ignore spurious junk in the receive buffer.
		startCmd = rbuf + strcspn (rbuf, "!?>");
		if (*startCmd == '\0') {
			startCmd = rbuf;
		}

		// Extracted and remove the command from the receive buffer.
		strncpy(startCmd, rbuf, MIN (length, 15));
		cmd [15] = '\0';
		strcpy(rbuf, rbuf+length);
		rpend = 0;

		log (TRACE, "Received command: <%s>, rbuf has <%s>\n", cmd, rbuf);
		switch (cmd[0]) {
		case '!': ProcessLineData(cmd);  break;
		case '?': ProcessError(cmd); break;
		case '>': ProcessLocalData(cmd); break;
		case '\0':
		case '\n':
		case '\r':
			break;
		default:
			printf("Received unrecognized command <%s>\n", cmd);
		}  // switch
	}
	return 0;
}


//
//  Handle an X10 code received from the power line.
//

int CM11A::ProcessLineData(char *cmd)
{
	cmd++;
	if (q && (!strncmp(cmd, q->cmd, 3))) {
		printf("Confirmed: <%s>\n", cmd);
		Dequeue();
	} else printf("Power line: <%s>\n", cmd);
	map->Update(cmd);
	return 0;
}


//
//  Handle any errors returned, indicated by a ? message.
//

int CM11A::ProcessError(char *cmd)
{
	cmd++;
	printf("Invalid command: <%s>\n", cmd);
	if (q->tries) {
		q->pending = 0;
		rpend = 0;
	} else Dequeue();
	return 0;
}


//
//  Process the two possible locally generated replies:
//    XXX -- Collision, resend last packet
//    Rnn -- Repeat command set.
//

int CM11A::ProcessLocalData(char *cmd)
{
	cmd++;
	switch (cmd[0]) {
	case 'X':
		printf("Collision.  Resending...\n");
		q->pending = 0;
		rpend = 0;
		break;
	case 'R':
		cmd++;
		printf("Repeat value set to %d\n", atoi(cmd));
		break;
	default:
		printf("Unknown interface response: <%s>\n", cmd);
	}
	return 0;
}

