/* IRG Daemon to translate IR remote functions.  Listens for
 * recognized packets, and sends out other packets.
 *
 * Copyright 1994 David Deaven version 0.1
 * Permission is granted to use, copy, and distribute this code,
 * provided that the use is non-commercial.
 * deaven@iastate.edu
 */

#include <stdio.h>
#include <string.h>
#include "irg.h"
#ifdef M6502
#define void int
#else
int start(FILE *f);
int hextoc(char *s);
int which(char *s, char c);
void printp(char *s);
int gettok(FILE *f, char *t);
int iswhite(int c);
void *pmalloc(size_t n);
int getkey();
#endif

/* These values work well with Mitsubishi, Sony, and Sharp remotes.
 */
int sample = 2;			/* timebase */
int ton = 20, toff = 20;	/* pulse width -- sending */
int repeat = 3;			/* repeat send code */
int tonmin = 10, toffmax = 30;	/* pulse width -- receiving */
int qtime = 100, totime = 25;	/* quiet time, timeout */

struct IRcode {
  char *name;		/* user-defined string */
  char *packet;		/* IRG buffer */
  void *map;		/* pointer to a mapped code */
  void *next;		/* next code in list */
};

struct Program {
  char act;		/* action type */
  void *action;		/* pointer to that action */
  void *nxt;		/* next action in program */
};

enum acts { NONE, CODE, EXEC };

struct IRcode *vocabulary = NULL;

#define BUFLEN 16
char buffer[BUFLEN];

char *digit = "0123456789abcdef";

/* Search for a packet in the known vocabulary.
 */
struct IRcode *identify(packet)
char *packet;
{
  struct IRcode *p;
  p = vocabulary;
  while(strcmp(packet, p->packet))if((p = p->next) == NULL)return NULL;
  return p;
}

/* Search for a packet by name in the known vocabulary.
 */
struct IRcode *nameid(name)
char *name;
{
  struct IRcode *p;
  p = vocabulary;
  while(strcmp(name, p->name))if((p = p->next) == NULL)return NULL;
  return p;
}

/* Execute a program.
 */
void doProg(p)
struct Program *p;
{
  struct IRcode *irc;
  printf("(execute: ");
  while(p != NULL) {
    switch(p->act) {
      case CODE:
	irc = p->action;
	printf(" %s", irc->name);
	IRGwrite(irc->packet);
	break;
      case EXEC:
	printf("[no EXEC yet]\n");
	break;
      default:
	break;
    }
    p = p->nxt;
  }
  printf(")\n");
}

void main()
{
  int r;
  struct IRcode *irc;
  FILE *f;

  printf("IRG driver demo version 0.1\n");
  printf("David Deaven c.1994 deaven@iastate.edu\n");

  if((f = fopen("D:IRGDEMO.RC","r")) == NULL)
    fprintf(stderr, "Cannot open init file\n");
  else start(f);

  printf("starting IRG\n");
  IRGinit(sample, ton, toff, repeat, tonmin, toffmax, qtime, totime);

  while(1) {
    if((r = getkey()) > 0) {
      buffer[0] = 0xff;
      buffer[1] = r;
      r = 2;
    } else r = IRGread(buffer, BUFLEN);
    if(r) {
      buffer[r] = '\0'; printp(buffer);
      printf("...");
      if((irc = identify(buffer)) == NULL) {
        printf("unknown packet\n(copied)\n");
	IRGwrite(buffer);
      } else {
        printf("%s\n", irc->name);
	if(irc->map)doProg(irc->map);
      }
    }
  }
}

/* Read definitions from file f
 */
int start(f)
FILE *f;
{
  struct IRcode *irc, *ircn, *v;
  struct Program *irl, *vl;
  char s[64];
  int i, n;
  vocabulary=NULL;
  while(gettok(f, s)) {
    if(strcmp(s, "def") == 0) {
      if(gettok(f, s) == 0)goto eoff;
      if((irc = pmalloc(sizeof(struct IRcode))) == NULL)return 3;
      irc->name = strdup(s);
      if(gettok(f, s) == 0)goto eoff;
      for(i=n=0; i<strlen(s); i+=2) s[n++] = hextoc(s+i); s[n++] = '\0';
      irc->packet = strdup(s);
      irc->map = NULL;
      irc->next = NULL;
      printf("define %s = ", irc->name);
      printp(irc->packet);
      printf("\n");
      if(vocabulary == NULL)vocabulary = v = irc;
      else {
	v->next = irc; v = irc;
      }
    } else if(strcmp(s, "map") == 0) {
      if(gettok(f, s) == 0)goto eoff;
      if((irc = nameid(s)) == NULL)goto unk;
      printf("map %s to (", irc->name);
      while(1) {
        if(gettok(f, s) == 0)goto eoff;
        if(strcmp(s, "end") == 0)break;
        if((ircn = nameid(s)) == NULL)goto unk;
	if((irl = pmalloc(sizeof(struct Program))) == NULL)return 3;
	printf(" %s", ircn->name);
        irl->action = ircn;
        irl->act = CODE;
        irl->nxt = NULL;
        if(irc->map == NULL)irc->map = vl = irl;
        else {
	  vl->nxt = irl; vl = irl;
        }
      }
      printf(" )\n");
    } else {
      printf("input error near: %s\n", s);
      return 1;
    }
  }
  return 0;
eoff:
  printf("unexpected end of file\n");
  return 1;
unk:
  printf("unknown tag %s\n", s);
  return 1;
}

/* Convert two hex digits to a char.
 */
int hextoc(s)
char *s;
{
  int hi, lo;
  hi = which(digit, s[0]);
  lo = which(digit, s[1]);
  return lo + (hi << 4);
}

/* Determine which character in s is c
 */
int which(s,c)
char *s;
char c;
{
  int i;
  for(i=0; s[i]!='\0'; ++i)if(s[i] == c)return i;
  return -1;
}

/* Print a string in hexadecimal
 */
void printp(s)
char *s;
{
  while(s[0]!='\0') {
    fputc(digit[s[0]>>4 & 0xf], stdout);
    fputc(digit[s[0] & 0xf], stdout);
    ++s;
  }
}

/* Read one token from f
 */
int gettok(f, t)
FILE *f;
char *t;
{
  int i, n;
  do {
    i = fgetc(f);
    if(i == EOF)return 0;
  } while(iswhite(i));

  t[n = 0] = i;

  do {
    i=fgetc(f);
    if(i == EOF)return 0;
    t[++n] = i;
  } while(!iswhite(i));

  t[n] = '\0';
  return 1;
}

/* White space on the Atari
 */
int iswhite(c)
int c;
{
  if(c==' ' || c=='\t' || c==155 || c=='\n')return 1;
  return 0;
}

int getkey()
{
#ifdef M6502
  int r;
  char *ch;
  ch = 0x2fc;
  if((r = (*ch)) != 0xff) {
    (*ch) = 0xff;
    return r;
  }
#endif
  return -1;
}
