#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
#define READERSZ 5

uint8_t req[S6400_MAXREQLEN];
#define RESSZ 4000
uint8_t res[RESSZ];
struct termios ttysave;
int fd = -1;
char *dev;
static uint32_t reader[READERSZ];
size_t reader_cnt = 0;

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

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

static void Close(void) {
  NOTICE("Stopping %s", cmdline_pgm);
  LOG_VIF( (fd = tty_close(fd, &ttysave, dev)) < 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 ) {
  ssize_t ret;
  uint16_t code;
  const char *err1;
  const char *err2;

  LOG_IF( tcflush(fd, TCIOFLUSH) < 0, LOG_ERR) { }
  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 (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;
}

static void run(void) {
  static long sec;
  const size_t req_len = s6400_inventory_req( req, S6400_MAXREQLEN, reader_cnt - 1, 0, 0);
  /*const size_t req_len = s6400_inventory_req( req, S6400_MAXREQLEN, 1, 0, 0); */
  const size_t uid_sz = 10;
  {
    uint8_t request[reader_cnt][req_len];
    size_t ix;
    uint64_t uid[uid_sz];
    int val;
    struct timespec wnxt   = timespec_dbl2ts( 0.2 / reader_cnt );
    uint8_t uid_cnt;
    struct timespec deadline[uid_sz];

    (void) timespec_update();
    for (ix = 0; ix < reader_cnt; ++ix) {
      /*(void) s6400_inventory_req( request[ix], req_len, reader[ix], 0, 0); */
      (void) s6400_inventory_req( request[ix], req_len, reader_cnt > 1 ? reader[ix] : 0, 0, 0);
      deadline[ix] = timespec_zero;
    }
    sec = timespec_current.tv_sec;
    /*NOTICE(" %d", reader_cnt); */

    ix = 0;
    while(1) {
      int flg = 0;

      (void) timespec_update();
      if (sec != timespec_current.tv_sec) {
	flg = 1;
	sec = timespec_current.tv_sec;
      }

      (void) nanosleep(&wnxt, NULL);

      if (timespec_cmp(deadline[ix], timespec_current) < 0) {
	val = doio(request[ix], req_len, res, RESSZ);
	if ( val > 0 && (s6400_inventory_res(res, NULL, &uid_cnt, uid_sz, uid) > 0) ) {
	  int iu;
	  /*s6400_prpkg(NULL, request[ix], req_len); */
	  /*s6400_prpkg(NULL, res, val); */
	  flg = 0;
	  printf("%ld %07d %2d", sec, reader[ix], uid_cnt);
	  for (iu = 0; iu < uid_cnt; ++iu) {
	    printf(" %llX", uid[iu]);
	  }
	  printf("\n");
	  deadline[ix] = timespec_add(timespec_current, timespec_dbl2ts( 3.0 ));
	}
      }

      if (flg) printf("%ld\n", sec); /* this is a timing "pulse" once a second */

      ++ix;
      if (ix == reader_cnt) ix = 0;
    }
  }
}

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

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

  val = argc - 2;
  LOG_VIF( READERSZ < val, LOG_NOTICE, " READERSZ = %d argc-2 = %d", READERSZ, val) {
    val = READERSZ;
  }

  /*
  {
    const int sigs[] = { SIGHUP, SIGINT, SIGUSR1, SIGUSR2, SIGTERM, 0 };
    LOG_IF( signal_set_multi(sighdl, sigs, &unblock_set), LOG_CRIT) return -1;
  }
  */

  LOG_VIF( (fd = tty_openraw(&ttysave, dev, baudrate, 0, 1, 1)) < 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 */

  if (argc == 2) {
    val = s6400_reader_version_req( req, S6400_MAXREQLEN, 0);
    LOG_IF( doio(req, (size_t) val, res, RESSZ) < 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[reader_cnt]);
    fflush(stdout);
    reader_cnt = 1;
  }


  for (ix = 2; ix < argc; ++ix) {
    uint32_t t1;
    uint32_t t2;
    int flag = 0;

    LOG_VIF( s6400_str2serial(argv[ix], &t1), LOG_ERR, " argv[%d] = %s", ix, argv[ix]) {
      Usage();
    }
    /* check for presence */
    val = s6400_reader_version_req( req, S6400_MAXREQLEN, t1);
    LOG_IF( doio(req, (size_t) val, res, RESSZ) < 0, LOG_ERR) flag = 1;
    else LOG_IF( s6400_reader_version_res(res, NULL, NULL, &t2), LOG_ERR) flag = 1;

    /* this might help, who knows? */
    val = s6400_reader_reset_req( req, S6400_MAXREQLEN, t1);
    LOG_IF( doio(req, (size_t) val, res, RESSZ) < 0, LOG_ERR) flag = 1;

    /* led and busser */
    val = s6400_reader_mode_req( req, S6400_MAXREQLEN, t1, S6400_MODE_LEDAUDIO, S6400_MODE_AUDIO);
    LOG_IF( doio(req, (size_t) val, res, RESSZ) < 0, LOG_ERR) {
      /* TODO response ignore for time beeing */
    }

    if (!flag && t1 == t2) {
      reader[reader_cnt] = t1;
      printf("Reader  verified: %07u\n", reader[reader_cnt]);
      fflush(stdout);
      ++reader_cnt;
    } else {
      printf("Reader not found: %07u\n", t1);
      flag_not_found = 1;
    }
  }
  if (flag_not_found) Usage();


  /*(void) setlogmask(LOG_UPTO(LOG_ERR)); */
  /*NOTICE("Starting %s", cmdline_pgm); */

  run();

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