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

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


// These match up to values in the header file
static const char *commands[] = {
	"A0",
	"L1",
	"ON",		/* On */
	"OF",		/* Off */
	"DI",		/* Dim */
	"BR",		/* Bright */
	"L0",
	"HR",
	"HA",
	"P0",
	"P1",
	"S1",
	"S0",
	"SR"
};



TwoWay::TwoWay (CONFIGURATION *config) : X10 (config)
{
	/* nothing */
}


TwoWay::~TwoWay()
{
}


void TwoWay::ConfigDevice (struct termios *tio)
{
	// Request 2 stop bits.
	tio->c_cflag = CS8 | CSTOPB | CREAD | CLOCAL | CRTSCTS;
        tio->c_iflag = IGNBRK | IGNPAR | ISTRIP | ICRNL;
        cfsetispeed(tio, B2400);
        cfsetospeed(tio, B2400);
}



int TwoWay::CreateCommand (int command, int hc, int uc)
{
	char buff[25];
	if (command == X10_SELECTUNIT) {
		sprintf(buff, "%c%02d\r", (char) hc, uc);
	} else {
		sprintf(buff, "%c%s\r", hc, commands[command]);
		uc = 0;
	}
	return (QueueText (command, hc, uc, buff));
}






int TwoWay::ParseCommand (int size, char *cmd)
{
	char *end_of_cmd;

	// We may receive multiple new characters at once, so we must search
	// for the end-of-cmd character.
	for (end_of_cmd = cmd;
	     end_of_cmd < (cmd + size) && *end_of_cmd != '\n'; end_of_cmd++) {
		// nothing
	}

	if (end_of_cmd >= (cmd + size))
		return 0;

	// We have a full command received.  Null terminate & extract it.
	*end_of_cmd = '\0';
	log (TRACE, "Current receive_buffer: <%s>", cmd);

	// Attempt to skip spurious junk.
	char *start_of_cmd = cmd + strcspn (cmd, "!?>");
	if (*start_of_cmd == '\0') {
		start_of_cmd = cmd;
	}
	log (TRACE, "Command portion: <%s>", start_of_cmd);
	
	switch (*start_of_cmd) {
		case '!': ProcessLineData(start_of_cmd + 1);  break;
		case '?': Retry (); break;
		case '>': ProcessLocalData(start_of_cmd + 1); break;
		case '\0':
		case '\n':
		case '\r':
			break;
		default:
			log (WARN, "Received unrecognized command <%s>", cmd);
	}  // switch
	return (end_of_cmd - cmd) + 1;
}



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

void TwoWay::ProcessLineData(char *cmd)
{
	// Translate back out of specific messaging, tell base class what we got
	int housecode = cmd [0];
	if (cmd [1] == '1' || cmd [1] == '0') {
		Receive (X10_SELECTUNIT, housecode, atoi (cmd + 1));
	} else {
		int id;
		for (id = 0; id < (int) countof (commands); id++) {
			if (strcmp (commands [id], cmd + 1) == 0) {
				Receive (id, housecode, 0);
			}
		}
	}
}



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

void TwoWay::ProcessLocalData(char *cmd)
{
	switch (cmd[0]) {
		case 'X':
			// Don't retry immediately.  Other devices do that.
			// Instead, allow a timeout so the other device
			// finishes.  (Ideally, we should do a random
			// delay like binary exponential backoff used
			// to handle Ethernet collisions.)
			log (INFO, "Collision.  Retrying after timeout...");
			break;
		case 'R':
			log (TRACE, "Repeat value set to %d", atoi(cmd + 1));
			break;
		default:
			log (WARN, "Unknown interface response: <%s>", cmd);
	}
}

