/*
 * foxtel.c:	A foxtel (Aussie cable TV) IR control client
 *
 *	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>

/* note: all times are in mirco seconds */
#define BUFSIZE 2048	/* buffer size */
#define IR_DEV "irctrl"	/* device file */
#define ir_carrier 21	/* IR carrier period */
#define bit_t 600	/* IR TX time */
#define one_t 7000	/* delay that codes '1' */
#define zero_t 4400	/* delay that codes '0' */
#define sync_t 3200	/* sync pulse spacing */
#define cmd_delay 100000	/* min delay between consective cmds */
#define tailspace 1	/* last 'space' time given to kernel */

/* code table */
struct code_t {
	char *name;
	char *code;
};
struct code_t 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 press = 0;	/* toggle flag, each new press has toggled value */
char *PROG = 0; /* set to argv[0] */
int DEV = 0;	/* set to open(IR_DEV, O_WRONLY) */ 
struct timeval rxbuf[BUFSIZE];	/* rx timing buffer */
int rxwcursor = 0;		/* rx write cursor */
int rxrcursor = 0;		/* rx read cursor */

void
dump_table() {
	int i = 0, p = 0;
	char buf[2048];

	fprintf(stderr, "%s: code table:\n", PROG);
	while(codes[i].name) {
		p += sprintf(&buf[p], "%-16s %s\n", codes[i].name, codes[i].code);
		i++;
	}
	p += sprintf(&buf[p], "\n");
	buf[p] = 0;
	fprintf(stderr, buf);
}

void
dump_cmds() {
	int i = 0, p = 0;
	char buf[10240];

	p += sprintf(&buf[p], "%s: known codes:\n", PROG);
	while(codes[i].name)
		p += sprintf(&buf[p], "%s ", codes[i++].name);
	p += sprintf(&buf[p], "\n. for a delay, = for table, ? for codes\n\n");
	buf[p] = 0;
	fprintf(stderr, buf);
}

void
tx(char *data) {
	int i = 0, p = 0;
	char bit = 0;
	struct stat sb;
	char buf[10240];

	/* open the device file */
	if(stat(IR_DEV, &sb) < 0) {
		perror(IR_DEV);
		return;
	}
	DEV = open(IR_DEV, O_WRONLY);

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

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

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

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

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

	/* write and release device */
	write(DEV, buf, p);
	close(DEV);
}

void
send(char *cmd) {
	int i = 0;
	struct timeval tv;

	/* check to see if its a delay element or table dump */
	if(!strcmp(cmd, ".")) {		/* wait */
		/* do nothing */
	} else if(!strcmp(cmd, "?")) {	/* cmd listing */
		dump_cmds();
	} else if(!strcmp(cmd, "=")) {	/* table dump */
		dump_table();
	} else if(cmd[0] == '/') {	/* raw send */
		tx(&cmd[1]);
	} else {			/* cmd in table */
		while(codes[i].name) {
			if(!strcmp(codes[i].name, cmd)) break;
			else i++;
		}
		if(!codes[i].name) {
			fprintf(stderr, "%s: unknown command: '%s'\n", PROG, cmd);
			return;
		}
		tx(codes[i].code);
	}

	/* delay */
	tv.tv_sec = 0;
	tv.tv_usec = cmd_delay;
	select(0, 0, 0, 0, &tv);
}

void
interactive() {
	char buf[2048];
	struct timeval tv;
	char *p1, *p2;
	fd_set fds;

	fprintf(stdout, "%s> ", PROG);
	fflush(stdout);
	while(fgets(buf, 2048, stdin)) {
		buf[strlen(buf)-1] = 0;
		p1 = buf;
		p2 = buf;
		while((p2 = strsep(&p1, " "))) {
			send(p2);
		}
		fprintf(stdout, "%s> ", PROG);
		fflush(stdout);
	}
}

int
main(int argc, char **argv) {
	int i;
	struct timeval tv;

	PROG = argv[0];

	if(argc < 2) {
		dump_cmds();
		interactive();
	} else {
		for(i = 1; i < argc; i++) {
			send(argv[i]);
			tv.tv_sec = 0;
			tv.tv_usec = cmd_delay;
			select(0, 0, 0, 0, &tv);
		}
	}

	return 0;
}
