/*
 * foxtel.c:	A foxtel (Aussie cable TV) IR control client routines
 *
 *	By:	Alan Yates <alany@ay.com.au>
 */
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <string.h>
#include <time.h>

#include "irctrl-cl.h"

struct code_t foxtel_codes[] = {
	{"pwr",		"x100010000"},
	{"0",		"x100000000"},
	{"1",		"x100000001"},
	{"2",		"x100000010"},
	{"3",		"x100000011"},
	{"4",		"x100000100"},
	{"5",		"x100000101"},
	{"6",		"x100000110"},
	{"7",		"x100000111"},
	{"8",		"x100001000"},
	{"9",		"x100001001"},
	{"up",		"x100011010"},
	{"rcl",		"x100011010"},
	{"f",		"x100011001"},
	{"-",		"x100010110"},
	{"left",	"x100010110"},
	{"menu",	"x100010111"},
	{"+",		"x100010001"},
	{"right",	"x100010001"},
	{"i",		"x100011111"},
	{"down",	"x100001011"},
	{"fav",		"x100001011"},
	{"store",	"x100010011"},
	{"norm",	"x100010101"},
	{"mute",	"x100011110"},
	{0, 		0}
};

char foxtel_press = 0;		/* toggle key-press state */

/* foxtel protocol definitions */
#define foxtel_ir_t 21		/* IR carrier period */
#define foxtel_bit_t 600	/* IR TX time */
#define foxtel_one_t 7000	/* delay that codes '1' */
#define foxtel_zero_t 4400	/* delay that codes '0' */
#define foxtel_sync_t 3200	/* sync pulse spacing */
#define foxtel_tailspace 1	/* last 'space' time given to kernel */

/* transmit code */
int
foxtel_tx(char *cmd) {
	int i = 0, p = 0;
	char bit = 0, buf[BUFSIZE], *data;
	struct stat sb;

	while(strcmp(foxtel_codes[i++].name, cmd)) {
		if(foxtel_codes[i].name == 0) {
			fprintf(stderr, "%s: foxtel packet '%s' unknown\n", PROG, cmd);
			return -1;
		}
	}
	data = foxtel_codes[--i].code;

	/* x bit toggle per press */
	(foxtel_press == '0') ? (foxtel_press = '1') : (foxtel_press = '0');

	/* irctrl carrier period and compensate values */
	p += sprintf(&buf[p], "%d\n", foxtel_ir_t);

	/* IR data sync header */
	p += sprintf(&buf[p], "%d %d\n", foxtel_bit_t, foxtel_sync_t);
	p += sprintf(&buf[p], "%d %d\n", foxtel_bit_t, foxtel_sync_t);

	/* IR data packet */
	for(i = 0; i < strlen(data); i++) {
		bit = data[i];
		if(bit == 'x') bit = foxtel_press;
		if(bit == '1') p += sprintf(&buf[p], "%d %d\n", foxtel_bit_t, foxtel_one_t);
		else p += sprintf(&buf[p], "%d %d\n", foxtel_bit_t, foxtel_zero_t);
	}

	/* Stop bit */
	p += sprintf(&buf[p], "%d %d\n", foxtel_bit_t, foxtel_tailspace);
	buf[p] = 0;

	/* write */
	if(write(DEV, buf, p) == p) return 0;
	return -1;
}

int new = 0;
unsigned long startsec;
unsigned long startusec;
char frame_buffer[32];
int frame_cursor = 0;

/* bit string comparison with 'dont cares' */
int
dcbitcmp(char *p1, char *p2) {
	int i;
	if(strlen(p1) != strlen(p2)) return -1;
	for(i = 0; i < strlen(p1); i++) {
		if((p1[i] != p2[i]) & (p1[i] != 'x') & (p2[i] != 'x'))
			return -1;
	
	}
	return 0;
}

/* truely ugly code begins here - its horrible and I _will_ clean it up shortly */

int
decode_frame() {
	int i = 0;

	if(frame_cursor != 10) return 0;
	frame_buffer[frame_cursor] = 0;

	while(foxtel_codes[i].name) {
		if(!dcbitcmp(foxtel_codes[i].code, frame_buffer)) {
			printf("\n%s: received frame '%s' => %s\n", PROG, foxtel_codes[i].code, foxtel_codes[i].name);
			frame_cursor = 0;
			return -1;
		}
		i++;
	}
	printf("\n%s: received unknown frame '%s'\n", PROG, frame_buffer);
	return -1;
}

int
foxtel_rx(int *dc) {
	char buf[BUFSIZE];
	unsigned int i = 0, j = 0, sec_time, usec_time, ret;
	unsigned long long t, last_t, delay;
	struct timeval tv[1024];

	while((i + *dc)%BUFSIZE != recv_cursor)
		buf[i] = rxbuffer[(*dc + i++)%BUFSIZE];
	*dc = recv_cursor;

	while(sscanf(&buf[j++*17], "%lu%lu", &sec_time, &usec_time) > 0) {	
		if(new == 0) {
			new = 1;
			startsec = sec_time;
			startusec = usec_time;
		}

		t = sec_time*1000000 + usec_time;
		delay = t - last_t;
		last_t = t;

		if(delay > 10000) {
			ret = decode_frame();
			frame_cursor = 0;
		}
		else if(delay > 3*2500) frame_buffer[frame_cursor++] = '1';
		else if(delay > 2*2500) frame_buffer[frame_cursor++] = '0';
		ret = decode_frame();
	}

	if(ret) return -1;
	return 0;
}
