#include <aspoutil.h>
#include <aspoprotocol.h>
#include <stdlib.h>
#include <time.h>
#include <unistd.h>

#define LOG_BUFSZ 200
const size_t log_bufsz = LOG_BUFSZ;
char log_buffer[LOG_BUFSZ];

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

#define TIME 100000

uint8_t req[S6400_MAXREQLEN];
#define RESSZ 4000
uint8_t res[RESSZ];
struct termios ttysave;
int fd = -1;
char *dev;
static uint32_t reader;
static struct openraw_t opts = { 8, 0, 1, };

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

static void Usage(void) {
  printf("Usage:\n\t%s serial_port\n", cmdline_pgm);
  exit(1);
}

static void Close(void) {
  NOTICE("Stopping %s", cmdline_pgm);
  LOG_VIF( (fd = tty_close(fd, &ttysave, dev, getpid())) < 0, LOG_ERR, " dev = %s fd = %d", dev, fd) { }
}

/*
static void sighdl(int sig, siginfo_t *si, void *context) {
  signal_handlerLogReopen(sig, si, context);
  switch (sig) {
  case SIGINT:
  case SIGTERM:
    exit(Close());
    break;
  case SIGUSR1:
  case SIGUSR2:
    / * theese are only logged for the time being * /
    break;
  }
}
*/

static int doio(const void *obuf, size_t olen, void *ibuf, ssize_t ilen, int check ) {
  ssize_t ret;
  uint16_t code;
  const char *err1;
  const char *err2;

  LOG_IF( tcflush(fd, TCIOFLUSH) < 0, LOG_ERR) { }
  if (obuf && olen > 0) {
    LOG_IF( (ret = tty_write(fd, obuf, olen) != (ssize_t) olen), LOG_NOTICE) return -1;
  }
  /*s6400_prpkg("out ", obuf, olen); */
  /* sending 15 bytes and receiving 22 bytes takes about 1ms, so the delay is for reader tag processing */
  usleep(TIME); /* this don't check for signal interruptions */
  bzero( ibuf, ilen);  
  LOG_IF( (ret = tty_read(fd, ibuf, ilen)) < 0, LOG_ERR) return -1;
  /*s6400_prpkg("in  ", ibuf, ret); */

  if (!ret) {
    /*s6400_prpkg("no response for", obuf, olen); */
    return 0;
  }
  if (check) {
    if (s6400_validate(ibuf, ret)   < 0) goto doio_err;
    if (s6400_check_error_res(ibuf, &code, &err1, &err2) < 0 || code) {
      if (code == 0x0201) return 0; /* no transponder found */
      if (code == 0x0206) return 0; /* for some reasons, sometimes tag crc error is reported */
      (void) s6400_check_error_report(code, err1, err2);
      goto doio_err;
    }
  }

  return ret;

doio_err:
  s6400_prpkg("doio req: ", obuf, olen);
  s6400_prpkg("doio res: ", ibuf, ret);
  return -1;
}

int run(void);
int run(void) {
  long sec;
  fd_set sr;
  struct timespec ts = { 1, 0 }; /* we'll wait 1sec for answers, else we'll consider the conn. broken */
  int byterate = 38400; /* ? */
  ssize_t nread;
  int ix;
  /*const double timeslot = 1000000.0/byterate;   / * us per byte * / */
  /*const int getsize = 30;        / * how many bytes to wait for * / */
  /*const int wait = ( timeslot * getsize * 1 ) / 2; */

  (void) timespec_align_to(1);
  sec = timespec_current.tv_sec;
  while(1) {
    nread = 0;
    FD_ZERO(&sr);
    FD_SET(fd, &sr);
    LOG_IFERRNO ( (ix = pselect(fd+1, &sr, NULL, NULL, &ts, NULL)) < 0 && errno != EINTR, LOG_ERR ) break;
    (void) timespec_update();
    if (ix < 0) {
    } else if (ix == 0) {
      /* timeout */
      ++sec;
      printf("%ld\n", sec); /* this is a timing "pulse" once a second */
    } else {
      usleep(10000);
      nread = tty_read_cont(fd, res, RESSZ, byterate);
      res[nread] = '\0';
      printf("%ld %s", sec, res);
    }
    ts.tv_sec = sec + 1;
    ts.tv_nsec = 0;
    if (timespec_cmp(ts, timespec_current) > 0) {
      ts = timespec_sub(ts, timespec_current);
    } else {
      ts.tv_sec = 0;
      ts.tv_nsec = 100;
    }
  }
  return ix;
}

int main(int argc, char *argv[]) {
  int val;
  int baudrate = B38400;
  /*sigset_t unblock_set; */

  (void) cmdline_extrpgm(argv);
  log_open("", LOG_PRIO | 2, 0); /* log to stdout */
  if (argc != 2) Usage();
  dev = argv[1];

  LOG_VIF( (fd = tty_openraw(&ttysave, dev, baudrate, &opts)) < 0, LOG_ERR, " dev = %s", dev ) Usage();
  LOG_IF( atexit(Close) < 0, LOG_ERR) return -1;
  LOG_IF( setvbuf(stdout, (char *) NULL, _IOLBF, 0), LOG_NOTICE) { } /* too bad, but we'll continue */
  LOG_IF( doio(NULL, (size_t) 0, res, RESSZ, 0) < 0, LOG_ERR) return -1;

  val = s6400_reader_version_req( req, S6400_MAXREQLEN, 0);
  LOG_IF( doio(req, (size_t) val, res, RESSZ, 1) < 0, LOG_ERR) return -1;
  LOG_IF( s6400_reader_version_res(res, NULL, NULL, &reader), LOG_ERR) return -1;
  printf("Reader found:     %07u\n", reader);
  fflush(stdout);

  /* led and busser */
  val = s6400_reader_mode_req( req, S6400_MAXREQLEN, 0, S6400_MODE_LEDAUDIO, S6400_MODE_LED | S6400_MODE_AUDIO);
  LOG_IF( doio(req, (size_t) val, res, RESSZ, 1) < 0, LOG_ERR) return -1;
  LOG_IF( s6400_reader_setup_res(res, NULL, NULL, NULL, NULL, NULL), LOG_ERR) return -1;

  /* set in Wiegang mode */
  val = s6400_reader_mode_wieg_req( req, S6400_MAXREQLEN, 0, S6400_MODE_64BITS, 0);
  LOG_IF( doio(req, (size_t) val, res, RESSZ, 1) < 0, LOG_ERR) return -1;
  LOG_IF( s6400_reader_setup_res(res, NULL, NULL, NULL, NULL, NULL), LOG_ERR) return -1;

  run();

  /*Close(); */
  return 1;
}
