/*
 Runs two GIER simulators running damspil.asc playing against each others.

 Threads

 sort_punch
 hvid_punch
*/

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <pthread.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <string.h>
#include <fcntl.h>
#include <errno.h>
#include <sys/ioctl.h>

#define SORT 0
#define HVID 1
#define LINELEN 64

static int levels[2]={2,2};
static char *colorname[2]={"sort","hvid"};
static long ran;

#define PRINTER_FILE "%s_printer.ps"
#define PUNCH_FIFO "%s_punch.flx"
#define PUNCH_LOG "%s_log.flx"
#define GIER_LOG "%s_log.out"
#define DEMO_FIFO "%s.demo"

#define GIERprog "/home/mk/gier/20220106/GIER"
#define DAMDATA "/home/mk/gier/20220106/tapes/mk/damdata.asc"
#define CONFIGfile "/tmp/dam.gier"
typedef struct
{
  int color,uc,fh_in,fh_demo,fh_migdemo,fh_log,pline;
  char linebuffer[LINELEN];
  char *punch_fifo;
  char *punch_log;
  pthread_t pt;
} punchtype;

static punchtype punches[2];

typedef struct
{
  int color,fh_demo;
  char *demo_fifo;
  pthread_t pt;
  pthread_mutex_t mintur;
  char *printer_file;
  int GIERpid;
} GIERtype;

static GIERtype GIERs[2];

unsigned char flx2a(char c,int *uppercase)
{
  if(c==64) return '\n';
  if(c>64)
  {
    return 255;
  }

  switch(c+64*(*uppercase))
  {
    case   0: return ' ';
    case   1: return '1';
    case   2: return '2';
    case   3: return '3';
    case   4: return '4';
    case   5: return '5';
    case   6: return '6';
    case   7: return '7';
    case   8: return '8';
    case   9: return '9';
    case  13: return 0xe5; /* å */
    case  14: return '_';
    case  16: return '0';
    case  17: return '<';
    case  18: return 's';
    case  19: return 't';
    case  20: return 'u';
    case  21: return 'v';
    case  22: return 'w';
    case  23: return 'x';
    case  24: return 'y';
    case  25: return 'z';
    case  27: return ',';
    case  30: /* tabulator */ break;
    case  32: return '-';
    case  33: return 'j';
    case  34: return 'k';
    case  35: return 'l';
    case  36: return 'm';
    case  37: return 'n';
    case  38: return 'o';
    case  39: return 'p';
    case  40: return 'q';
    case  41: return 'r';
    case  43: return 0xf8; /* ø */
    case  48: return 0xe6; /* æ */
    case  49: return 'a';
    case  50: return 'b';
    case  51: return 'c';
    case  52: return 'd';
    case  53: return 'e';
    case  54: return 'f';
    case  55: return 'g';
    case  56: return 'h';
    case  57: return 'i';
    case  58: *uppercase=0; break;
    case  59: return '.';
    case  60: *uppercase=1; break;
    case  64: return ' ';
    case  65: return 0xa3; /* £ */
    case  66: return '*';
    case  67: return '/';
    case  68: return '=';
    case  69: return ';';
    case  70: return '[';
    case  71: return ']';
    case  72: return '(';
    case  73: return ')';
    case  77: return 0xc5; /* Å */
    case  78: return '|';
    case  80: return '&';
    case  81: return '>';
    case  82: return 'S';
    case  83: return 'T';
    case  84: return 'U';
    case  85: return 'V';
    case  86: return 'W';
    case  87: return 'X';
    case  88: return 'Y';
    case  89: return 'Z';
    case  91: return '\'';
    case  94: /* tabulator */ break;
    case  96: return '+';
    case  97: return 'J';
    case  98: return 'K';
    case  99: return 'L';
    case 100: return 'M';
    case 101: return 'N';
    case 102: return 'O';
    case 103: return 'P';
    case 104: return 'Q';
    case 105: return 'R';
    case 107: return 0xd8; /* Ø */
    case 112: return 0xc6; /* Æ */
    case 113: return 'A';
    case 114: return 'B';
    case 115: return 'C';
    case 116: return 'D';
    case 117: return 'E';
    case 118: return 'F';
    case 119: return 'G';
    case 120: return 'H';
    case 121: return 'I';
    case 122: *uppercase=0; break;
    case 123: return ':';
    case 124: *uppercase=1; break;
    default: break;
  }
  return 255;
}

unsigned char a2flx(unsigned char c, int *uppercase)
{
  switch(c)
  {
    case ' ': return 0;
    case '1': *uppercase=0; return 1;
    case '2': *uppercase=0; return 2;
    case '3': *uppercase=0; return 3;
    case '4': *uppercase=0; return 4;
    case '5': *uppercase=0; return 5;
    case '6': *uppercase=0; return 6;
    case '7': *uppercase=0; return 7;
    case '8': *uppercase=0; return 8;
    case '9': *uppercase=0; return 9;
    case 0xe5: *uppercase=0; return 13; /* å */
    case '_': *uppercase=0; return 14;
    case '0': *uppercase=0; return 16;
    case '<': *uppercase=0; return 17;
    case 's': *uppercase=0; return 18;
    case 't': *uppercase=0; return 19;
    case 'u': *uppercase=0; return 20;
    case 'v': *uppercase=0; return 21;
    case 'w': *uppercase=0; return 22;
    case 'x': *uppercase=0; return 23;
    case 'y': *uppercase=0; return 24;
    case 'z': *uppercase=0; return 25;
    case ',': *uppercase=0; return 27;
    case '\t': return 30;
    case '-': *uppercase=0; return 32;
    case 'j': *uppercase=0; return 33;
    case 'k': *uppercase=0; return 34;
    case 'l': *uppercase=0; return 35;
    case 'm': *uppercase=0; return 36;
    case 'n': *uppercase=0; return 37;
    case 'o': *uppercase=0; return 38;
    case 'p': *uppercase=0; return 39;
    case 'q': *uppercase=0; return 40;
    case 'r': *uppercase=0; return 41;
    case 0xf8: *uppercase=0; return 43; /* ø */
    case 0xe6: *uppercase=0; return 48; /* æ */
    case 'a': *uppercase=0; return 49;
    case 'b': *uppercase=0; return 50;
    case 'c': *uppercase=0; return 51;
    case 'd': *uppercase=0; return 52;
    case 'e': *uppercase=0; return 53;
    case 'f': *uppercase=0; return 54;
    case 'g': *uppercase=0; return 55;
    case 'h': *uppercase=0; return 56;
    case 'i': *uppercase=0; return 57;
    case '.': *uppercase=0; return 59;
    case '\n': return 64;
    case '\r': return 64;
    case 0xa3: *uppercase=1; return 1; /* £ */
    case '*': *uppercase=1; return 2;
    case '/': *uppercase=1; return 3;
    case '=': *uppercase=1; return 4;
    case ';': *uppercase=1; return 5;
    case '[': *uppercase=1; return 6;
    case ']': *uppercase=1; return 7;
    case '(': *uppercase=1; return 8;
    case ')': *uppercase=1; return 9;
    case 0xc5: *uppercase=1; return 13; /* Å */
    case '|': *uppercase=1; return 14;
    case '&': *uppercase=1; return 16;
    case '>': *uppercase=1; return 17;
    case 'S': *uppercase=1; return 18;
    case 'T': *uppercase=1; return 19;
    case 'U': *uppercase=1; return 20;
    case 'V': *uppercase=1; return 21;
    case 'W': *uppercase=1; return 22;
    case 'X': *uppercase=1; return 23;
    case 'Y': *uppercase=1; return 24;
    case 'Z': *uppercase=1; return 25;
    case '\'': *uppercase=1; return 27;
    case '+': *uppercase=1; return 32;
    case 'J': *uppercase=1; return 33;
    case 'K': *uppercase=1; return 34;
    case 'L': *uppercase=1; return 35;
    case 'M': *uppercase=1; return 36;
    case 'N': *uppercase=1; return 37;
    case 'O': *uppercase=1; return 38;
    case 'P': *uppercase=1; return 39;
    case 'Q': *uppercase=1; return 40;
    case 'R': *uppercase=1; return 41;
    case 0xd8: *uppercase=1; return 43; /* Ø */
    case 0xc6: *uppercase=1; return 48; /* Æ */
    case 'A': *uppercase=1; return 49;
    case 'B': *uppercase=1; return 50;
    case 'C': *uppercase=1; return 51;
    case 'D': *uppercase=1; return 52;
    case 'E': *uppercase=1; return 53;
    case 'F': *uppercase=1; return 54;
    case 'G': *uppercase=1; return 55;
    case 'H': *uppercase=1; return 56;
    case 'I': *uppercase=1; return 57;
    case ':': *uppercase=1; return 59;
    default:
      break;
  }
  return 0;
}


unsigned char remove_parity(unsigned char c)
{
  return ((c&0xe0)>>1) | (c&0x0f);
}

static void send(int fh, char *t, char *color)
{
  int st,l,sz,nwait;
  nwait=-1;
  ioctl(fh, FIONREAD, &sz);
  do
  {
  ioctl(fh, FIONREAD, &sz);
  nwait++;
  usleep(1000);
  } while(sz>40);
  printf("%s send: size: %d nwait: %d %s",color,sz,nwait,t);
  fflush(stdout);
  l=strlen(t);
  st=write(fh, t, l);
  if(st!=l)
  {
    printf("write l: %d st: %d errno: %d fh: %d\n",l,st,errno,fh);
  }
}

static void maketext(int fh, char *t, char *color)
{
  int len,i,uc;
  unsigned char c;
  char buf[20];
  printf("%s maketext: '%s'\n",color,t);
  fflush(stdout);
  len=strlen(t);
  uc=0;
  for(i=0;i<len;i++)
  {
    c=a2flx((unsigned char) t[i],&uc);
    send(fh, "WAITINPUT\n", color);
    sprintf(buf, "TYPE %d\n", (int) c);
    send(fh, buf, color);
  }
  if(uc)
  {
    // End in LC
    send(fh, "WAITINPUT\n", color);
    send(fh, "TYPE 58\n", color);
  }
}

static int linecmpoffset(punchtype *p, int offset, int len, char *t)
{
  int i,pnt;

  for(i=0;i<len;i++)
  {
    pnt=p->pline-len+i-offset;
    if(pnt<0) pnt+=LINELEN;
    if(pnt>=LINELEN) pnt-=LINELEN;
    if(p->linebuffer[pnt] != t[i]) return 0;
  }
  return 1;
}

static int linecmp(punchtype *p, int len, char *t)
{
  return(linecmpoffset(p,0,len,t));
}


void *punch_thread(void *arg)
{
  punchtype *p = (punchtype *) arg;
  int st,i,j,pnt,sendlen;
  char ch2,sendbuf[LINELEN];
  unsigned char ch;

  p->fh_in = open(p->punch_fifo, O_RDONLY);
  printf("fh_in: %d\n",p->fh_in);
  do
  {
  p->fh_demo = open(GIERs[1-p->color].demo_fifo, O_WRONLY|O_NDELAY);
  printf("fh_demo %s: %d\n",GIERs[1-p->color].demo_fifo,p->fh_demo);
  } while(p->fh_demo == -1);
  do
  {
  p->fh_migdemo = open(GIERs[p->color].demo_fifo, O_WRONLY|O_NDELAY);
  printf("fh_migdemo %s: %d\n",GIERs[p->color].demo_fifo,p->fh_migdemo);
  } while(p->fh_migdemo == -1);
  p->fh_log = open(p->punch_log, O_WRONLY|O_CREAT|O_TRUNC, 0644);
  for(;;)
  {
    st=read(p->fh_in,&ch2,1);
    if(st==1)
    {
      st=write(p->fh_log, &ch2, 1);
      if(st!=1) printf("write to log failed\n");
      ch=flx2a(remove_parity(ch2), &(p->uc));
      if(ch==255) continue;
//    printf("%s punch read '%c'\n",colorname[p->color],(char)ch);
//    printf("%c",(char)ch);
      p->linebuffer[p->pline++] = ch;
      if(p->pline == LINELEN) p->pline=0;
      if(linecmp(p,4,"igen"))
      {
	send(p->fh_migdemo, "WAITINPUT\n", colorname[p->color]);
	maketext(p->fh_migdemo, "n", colorname[p->color]);
	send(p->fh_migdemo, "WAITINPUT\n", colorname[p->color]);
	send(p->fh_migdemo, "FINISHPRINTER\n", colorname[p->color]);
	send(p->fh_demo, "FINISHPRINTER\n", colorname[1-p->color]);
	send(p->fh_migdemo, "QUIT\n", colorname[p->color]);
	send(p->fh_demo, "QUIT\n", colorname[1-p->color]);
	sleep(2);
	exit(0);
      }
      else if(linecmp(p,9,"din tur  "))
      {
	printf("%s unlock mutex\n",colorname[p->color]);
	pthread_mutex_unlock(&(GIERs[p->color].mintur));
	printf("sæt tur=1 for %s\n",colorname[p->color]);
      }
      else if(linecmp(p,1,"<"))
      {
	// a1,a2,a3,a4<
	// -  987654321
	// o    6543210
	if(linecmpoffset(p,3,1,","))
	{
	  for(i=6;;i+=3)
	  {
	    pnt = p->pline-i-1;
	    if(pnt<0) pnt+=LINELEN;
	    if(pnt>=LINELEN) pnt-=LINELEN;
	    printf("i: %d pline: %d pnt: %d c: '%c'\n",i,p->pline,pnt,p->linebuffer[pnt]);
	    if(linecmpoffset(p,i,1," "))
	    {
	      sendlen=i;
	      printf("sendlen: %d\n",sendlen);
	      for(j=0;j<sendlen;j++)
	      {
		pnt=p->pline-i+j;
		if(pnt<0) pnt+=LINELEN;
		if(pnt>=LINELEN) pnt-=LINELEN;
		sendbuf[j]=p->linebuffer[pnt];
	      }
	      sendbuf[sendlen]=0;
	      printf("%s venter på tur,,,\n",colorname[1-p->color]);
	      fflush(stdout);
	      printf("%s lock mutex\n",colorname[1-p->color]);
	      pthread_mutex_lock(&(GIERs[1-p->color].mintur));
	      printf("vent på tur OK\n");
	      fflush(stdout);
	      maketext(p->fh_demo,sendbuf, colorname[p->color]);
	      send(p->fh_demo, "WAITINPUT\n", colorname[p->color]);
	      break;
	    }
	  }
	}
      }
    }
    else
    {
      if(errno!=EAGAIN)
      printf("%s punch read failed: %d %d\n",colorname[p->color],st,errno);
    }
  }
}

void *GIER_thread(void *arg)
{
  char tmp[1024];
  GIERtype *g = (GIERtype *) arg;
  int st,fh_gier;
  char **argv2;
  pthread_mutex_init(&(g->mintur), NULL);
  printf("%s lock mutex\n",colorname[g->color]);
  pthread_mutex_lock(&(g->mintur));
  do
  {
  g->fh_demo = open(g->demo_fifo, O_RDWR|O_NDELAY);
  printf("fh_demo %s: %d\n",g->demo_fifo,g->fh_demo);
  } while(g->fh_demo == -1);
  g->GIERpid = fork();
  if(g->GIERpid == 0)
  {
    argv2 = (char **) malloc(sizeof(*argv2)*3);
    argv2[0] = malloc(strlen(GIERprog)+1);
    strcpy(argv2[0], GIERprog);
    argv2[1] = malloc(strlen(g->demo_fifo)+1);
    strcpy(argv2[1], g->demo_fifo);
    argv2[2] = NULL;
    sprintf(tmp, GIER_LOG, colorname[g->color]);
    fh_gier = open(tmp, O_WRONLY|O_CREAT|O_TRUNC, 0644);
    close(1);
    dup(fh_gier);
    st=execvp(argv2[0], argv2);
  }
  sprintf(tmp, "LOADCONFIG %s\n", CONFIGfile);
  send(g->fh_demo, tmp, colorname[g->color]);
  send(g->fh_demo, "HIDE MAGTAPE\n", colorname[g->color]);
  sprintf(tmp, "LOADPUNCH %s\n", punches[g->color].punch_fifo);
  send(g->fh_demo, tmp, colorname[g->color]);
  sprintf(tmp, "LOADPRINTER %s\n", g->printer_file);
  send(g->fh_demo, tmp, colorname[g->color]);
  send(g->fh_demo, "PRESS SETKB\n", colorname[g->color]);
  send(g->fh_demo, "PRESS CLEARKB\n", colorname[g->color]);
  if(g->color == 1)
  {
//  send(g->fh_demo, "DEBUG 901\n", colorname[g->color]);
  }
  sprintf(tmp, "LOADREADER %s\n", DAMDATA);
  send(g->fh_demo, tmp, colorname[g->color]);
  maketext(g->fh_demo, "y<", colorname[g->color]);
  send(g->fh_demo, "WAITINPUT\n", colorname[g->color]);
  if(g->color)
    maketext(g->fh_demo, "h", colorname[g->color]);
  else
    maketext(g->fh_demo, "s", colorname[g->color]);
  send(g->fh_demo, "WAITINPUT\n", colorname[g->color]);
  sprintf(tmp,"%d,",levels[g->color]);
  maketext(g->fh_demo, tmp, colorname[g->color]);
  send(g->fh_demo, "WAITINPUT\n", colorname[g->color]);
  sprintf(tmp,"%ld,",ran);
  maketext(g->fh_demo, tmp, colorname[g->color]);
  send(g->fh_demo, "WAITINPUT\n", colorname[g->color]);
}


void main(int argc, char **argv)
{
  int color,st;
  /* create fifos */
  char *tmp,tmpbuf[100];

  ran=atol(argv[1]);

  for(color=0;color<2;color++)
  {
    punches[color].color=color;
    punches[color].uc=0;
    punches[color].pline=0;
    punches[color].fh_in=-1;
    punches[color].fh_demo=-1;
    punches[color].fh_migdemo=-1;

    GIERs[color].color = color;
    GIERs[color].fh_demo = -1;

    sprintf(tmpbuf, PRINTER_FILE, colorname[color]);
    GIERs[color].printer_file = malloc(strlen(tmpbuf)+1);
    strcpy(GIERs[color].printer_file,tmpbuf);

    sprintf(tmpbuf, PUNCH_FIFO, colorname[color]);
    st=mkfifo(tmpbuf,0600);
    punches[color].punch_fifo = malloc(strlen(tmpbuf)+1);
    strcpy(punches[color].punch_fifo,tmpbuf);

    sprintf(tmpbuf, PUNCH_LOG, colorname[color]);
    punches[color].punch_log = malloc(strlen(tmpbuf)+1);
    strcpy(punches[color].punch_log,tmpbuf);

    sprintf(tmpbuf, DEMO_FIFO, colorname[color]);
    st=mkfifo(tmpbuf,0600);
    GIERs[color].demo_fifo = malloc(strlen(tmpbuf)+1);
    strcpy(GIERs[color].demo_fifo,tmpbuf);
  }


  // start threads

  for(color=0;color<2;color++)
  {
    pthread_create( &(punches[color].pt), NULL, punch_thread, &punches[color] );
    pthread_create( &(GIERs[color].pt), NULL, GIER_thread, &GIERs[color] );
  }

  // wait

  for(color=0;color<2;color++)
  {
    pthread_join( punches[color].pt, NULL);
    pthread_join( GIERs[color].pt, NULL);
  }
}

