#include "config.h"
#include <stdio.h>
#include <sys/types.h>
#include <setjmp.h>
#include "common.h"
#ifdef X68
#include "tty_x68.h"
#else
#ifdef WIN32
#include "tty_w32.h"
#else
#include "tty.h"
#include <termios.h>
#endif
#endif
#if HAVE_UNISTD_H
#include <unistd.h>
#endif

#if 0
# define dprintf(x)	fprintf x
#else
# define dprintf(x)
#endif

extern int verbose;

static	int	QVfd = -1;
static	int	dotrap = 0;
static	jmp_buf	errtrap;

void
QVsetfd(fd)
     int	fd;
{
  dprintf((stderr, "QVfd = %x\n", fd));
  QVfd = fd;
}

int
QVgetfd()
{
  return QVfd;
}

/*------------------------------------------------------------*/

inline void
wbyte(c)
     u_char	c;
{
  dprintf((stderr, "> %02x\n", c));
  if (writetty(QVfd, &c, 1) < 0) {
    perror("writetty");
    if (dotrap)
      longjmp(errtrap, 1);
    else
      Exit(1);
  }
}

inline u_char
rbyte()
{
  u_char	c;

  if (readtty(QVfd, &c, 1) < 0) {
    perror("readtty");
    if (dotrap)
      longjmp(errtrap, 1);
    else
      Exit(1);
  }
  dprintf((stderr, "< %02x\n", c));
  return c;
}

inline void
wstr(p, len)
     u_char	*p;
     int	len;
{
  dprintf((stderr, "> len=%d\n", len));
  if (writetty(QVfd, p, len) < 0) {
    perror("writetty");
    if (dotrap)
      longjmp(errtrap, 1);
    else
      Exit(1);
  }
}

inline void
rstr(p, len)
     u_char	*p;
     int	len;
{

  dprintf((stderr, "< len=%d\n", len));
  if (readtty(QVfd, p, len) < 0) {
    perror("readtty");
    if (dotrap)
      longjmp(errtrap, 1);
    else
      Exit(1);
  }
}

/*------------------------------------------------------------*/

int
QVok()
{
  int retrycount = RETRY;

  while(retrycount--){
    wbyte(ENQ);
    if (rbyte() == ACK)
      return 1;			/*ok*/
  }
  return 0;			/*ng*/
}

static int
calcsum(p, len)
     u_char *p;
     int len;
{
  u_char *q;
  int sum = 0;
  int i;
  q = p;
  for(i = 0 ; i < len ; i++){
    sum = sum + *q;
    q++;
  }
  return(sum);
}


int
QVreset(flag)
     int flag;
{
  u_char	s;

  if (!QVok())
    return -1;			/*ng*/
  if(flag)
    wstr("QR", 2);
  else
    wstr("QE", 2);
  wbyte(ACK);
  s = rbyte();			/*supposed to be 0x5c('Z') or 0x69*/

  return (int) s;		/*ok*/
}

int
QVhowmany()
{
  u_char	s;
  u_char	n;
  int retrycount = RETRY;

  while(retrycount--){
    if (!QVok())
      return -1;		/*ng*/
    wstr("MP", 2);
    s = rbyte();		/*supposed to be 0x62('b')*/
    if(s == 0x62) break;
  }
  wbyte(ACK);
  n = rbyte();			/*# of pictures*/
  return (int) n;
}

int
QVshowpicture(n)
     int	n;
{
  u_char	s;

  if (!QVok())
    return -1;			/*ng*/
  wstr("DA", 2);
  wbyte(n);
  s = rbyte();			/*supposed to be 0x7a - n*/
  wbyte(ACK);
  return 1;			/*ok*/
}

static int current_speed = DEFAULT;
int
QVchangespeed(speed)
     int speed;
{
  int n;
  u_char	s;
  int baud;

  if(current_speed == speed)
    return(1);

  if (!QVok())
    return -1;			/*ng*/

  switch(speed){
  case LIGHT:			/* 115200 baud */
    n = 3;
#ifdef WIN32
    baud = B115200;
#else
    baud = B38400;
#endif
    break;
  case TOP:			/* 57600 baud */
    n = 7;
#ifdef WIN32
    baud = B57600;
#else
    baud = B38400;
#endif
    break;
  case HIGH:			/* 38400 baud */
#ifdef X68
    n = 10;                     /* 39062.5 baud */
#else
    n = 11;
#endif
    baud = B38400;
    break;
  case MID:			/* 19200 baud */
#ifdef X68
    n = 23;
#else
    n = 22;
#endif
    baud = B19200;
    break;
  case DEFAULT:
  default:
    n = 46;
    baud = B9600;
    break;
  }
  wstr("CB", 2);
  wbyte(n);
  wbyte(ACK);
  s = rbyte();
  sleep(1);			/* ??? */
  changespeed(QVfd, baud);

  current_speed = speed;
  if (!QVok())
    return -1;			/*ng*/
  return 1;			/*ok*/

}

/*------------------------------------------------------------*/
int
QVblockrecv(buf)
     u_char	*buf;
{
  u_char	s;
  u_char	t;
  u_char	*p;
  u_int	sectorsize;
  int retrycount = RETRY;
  int sum;

  wbyte(DC2);

  p = buf;
  while (1) {
    if (verbose)
      fprintf(stderr, "%6d\b\b\b\b\b\b", p - buf);

    /* x: fault handlers */
    dotrap = 1;
    if (setjmp(errtrap) != 0) {
      wbyte(NAK);
      dprintf((stderr, "*********retry*********\n"));
    }

  retry:;
    /* 1: obtain sector size */
    if ((s = rbyte()) != STX) {
      dprintf((stderr,"NG sector size(%02x)\n",s ));
      flushtty(QVfd);
      wbyte(NAK);
      retrycount --;
      if(retrycount)
	goto retry;
      return -1;		/*ng*/
    }
    s = rbyte();
    sum = s;
    t = rbyte();
    sum = sum + t;
    sectorsize = ((u_int)s << 8) | t;

    /* 2: drain it */
    rstr(p, sectorsize);
    sum = sum + calcsum(p, sectorsize);
    p += sectorsize;

    /* 3: finalize sector */
    s = rbyte();		/*sector type?*/
    t = 0xff & ( ~ rbyte());	/*checksum?*/
    sum = 0xff & (sum + s);
    if(sum != t){
      flushtty(QVfd);
      wbyte(NAK);
      goto retry;
    }
		
    dotrap = 0;

    if (s == ETX) {
      /* final sector... terminate transfer */
      wbyte(ACK);
      break;
    } else if (s == ETB) {
      /* block cleanup */	
      wbyte(ACK);
    } else {
      /* strange condition... retry this sector */
      flushtty(QVfd);
      wbyte(NAK);
      goto retry;
    }
  }

  if (verbose)
    fprintf(stderr, "\n");

  return p - buf;
}


int
QVblocksend(buf, size)
     u_char	*buf;
     int size;
{
  u_char	s;
  u_char	t;
  u_char	*p;
  u_int	sectorsize;
  int retrycount = RETRY;
  int rest;
  int sum;

  rest = size;

  p = buf;
  while (rest > 0) {
    if (verbose)
      fprintf(stderr, "%6d\b\b\b\b\b\b", p - buf);
    if(rest < 0x80)
      sectorsize = rest;
    else
      sectorsize = 0x80;

    wbyte(STX);
    s = (u_char) (sectorsize >> 8) & 0xff;
    sum = s;
    t = (u_char) sectorsize &  0xff;
    sum += t;
    wbyte(s);
    wbyte(t);

    wstr(p, sectorsize);
    sum = sum + calcsum(p, sectorsize);
    p += sectorsize;
    rest -= sectorsize;

    wbyte(ETB);
    sum += ETB;
    wbyte(0xff & (~ sum));
    s = rbyte();  /* 0x06 */
    if(s != ACK)
      return(-1);
  }

  /* final sector */
  wbyte(STX);
  wbyte(0x00);
  wbyte(0x00);
  wbyte(ETX);
  wbyte(0xfc);
  s = rbyte();  /* 0x06 */
  if(s != ACK)
    return(-1);
  
  if (verbose)
    fprintf(stderr, "\n");

  return p - buf;
}








