#include <aspoutil.h>
#include <aspoprotocol.h>

#include <ctype.h>
#include <stdlib.h>
#include <time.h>
#include <unistd.h>

LOG_BUFFER(200)

/****************************/

#define BUFSZ (FEIG_MAX_STDPKT_LEN+20)
#define CMDSZ 100 /* input from user */

int Usage(int code);
int setset(fd_set *sr, fd_set *sw, int count, int *fd, struct buf_t *rd, struct buf_t *wr);
int run(int count, int *fd, struct buf_t *rd, struct buf_t *wr);

/****************************/

int Usage(int code) {
  printf("Name:\n\t%s - feig reader tester\n", cmdline_pgm);
  printf("Synopsis:\n\t%s serial_port:baudrate:settings ...\n", cmdline_pgm);
  printf("Description:\n");
  printf("\tserial_port is a device name, e.g. /dev/ttyS0\n");
  printf("\tbaudrate default is 38400\n");
  printf("\tsettings default is 8n1, it is 3 bytes: bytesize (5-8), parity (n, o, or e) and number of stopbits (1 or 2)\n");
  printf("Example:\n");
  printf("\t%s /dev/ttyS4:38400:8n1\n", cmdline_pgm);

  if (code) exit(code);
  return 0;
}

int setset(fd_set *sr, fd_set *sw, int count, int *fd, struct buf_t *rd, struct buf_t *wr) {
  int nfds = 0;
  int ix;

  FD_ZERO(sr);
  for (ix = 0; ix < count; ++ix) {
    if (buf_space(&rd[ix]) > 0) {
      FD_SET(fd[ix], sr);
      if (nfds < fd[ix]) nfds = fd[ix];
    }
  }

  FD_ZERO(sw);
  for (ix = 0; ix < count; ++ix) {
    if (buf_len( &wr[ix] )) {
      FD_SET(fd[ix], sw);
      if (nfds < fd[ix]) nfds = fd[ix];
    }
  }
  ++nfds;
  return nfds;
}

int run(int count, int *fd, struct buf_t *rd, struct buf_t *wr) {
  fd_set sr;
  fd_set sw;
  int nfds = 0;
  /*struct timespec tv = { 1, 0 }; */
  int ix;
  int cont = 10;

  while (cont--) {
    struct timespec *timeout = NULL;
    /*printf("%d %d\n", count, fd[0]); */

    nfds = setset(&sr, &sw, count, fd, rd, wr);
    LOG_IFERRNO ( (ix = pselect(nfds, &sr, &sw, NULL, timeout, NULL)) < 0 && errno != EINTR, LOG_ERR ) {
      return -1;    /* too bad, let user handle this */
    }
    if (ix < 0) { /* EINTR */
      return 0;
    } else if (ix == 0) { /* timeout */
      printf("timeout\n");
      /* TODO */
    } else { /* data to be read/written */
      int cnt;

      /* check writers */
      for (ix = 0; ix < count; ++ix) {
	if (FD_ISSET(fd[ix], &sw)) {
	  printf("write %d\n", fd[ix]);
	  LOG_IFERRNO( (cnt = buf_write(&wr[ix], fd[ix])) < 0 && errno != EINTR, LOG_ERR) return -1;
	}
      }

      /* check readers */
      for (ix = 0; ix < count; ++ix) {
	if (FD_ISSET(fd[ix], &sr)) {
	  /*printf("read on %d\n", fd[ix]); */
	  LOG_IFERRNO( (cnt = buf_read(&rd[ix], fd[ix])) < 0 && errno != EINTR, LOG_ERR) return -1;
	  if (cnt == 0) {
	    printf("EOF on %d\n", ix);
	    return 0;
	  }

	  if (ix == 0) {	
	    uint8_t cc;
	    buf_peeklast(&rd[ix], &cc);
	    if (cc == '\r') {
	      buf_droplast(&rd[ix]);
	      buf_putlast(&rd[ix], '\n');
	    }
	    printf("%s", rd[ix].buf);
	    buf_flush(&rd[ix]);
	  }
	}
      }
    }
  }
  return 0;
}


int main(int argc, char *argv[]) {
  (void) cmdline_extrpgm(argv);
  (void) log_open("", LOG_PRIO | 2, 0); /* log to stdout */
  argc--;
  argv++;

  if (argc < 1) {
    Usage(0);
    return 0;
  }

  {
    int ix;
    struct openraw_t port[argc+1];
    int fd[argc+1];

    fd[0] = STDIN_FILENO; /* 0 is for comand channel, i.e. the user */
    for (ix = 1; ix < argc+1; ++ix) { /* 1 and up is for the serial ports */
    }

    {
      struct buf_t wr[argc+1];
      struct buf_t rd[argc+1];

      uint8_t rbuf[argc][BUFSZ];
      uint8_t wbuf[argc][BUFSZ];
      char cmdbuf[CMDSZ];

      buf_init(&(rd[0]), cmdbuf, CMDSZ);
      buf_init(&(wr[0]), NULL, 0);
      for (ix = 1; ix < argc+1; ++ix) {
	buf_init(&(rd[ix]), rbuf[ix-1], BUFSZ);
	buf_init(&(wr[ix]), wbuf[ix-1], BUFSZ);
      }
      LOG_IF( setvbuf(stdout, (char *) NULL, _IOLBF, 0), LOG_NOTICE) {
	/* too bad, but we'll continue */
      }
      run(argc+1, fd, rd, wr);
    }

    /* no close for 0, stdin */
    {
      pid_t pid = getpid();
      for (ix = 1; ix < argc+1; ++ix) {
	tty_close(fd[ix], &(port[ix].save), port[ix].dev, pid);
      }
    }
  }
  return 0;
}
