#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
#ifdef OS2
#include "tty_os2.h"
#else
#ifdef DOS
#include "tty_dos.h"
#else
#include "tty.h"
#include <termios.h>
#endif /* DOS */
#endif /* OS2 */
#endif /* WIN32 */
#endif /* X68K */
#if HAVE_UNISTD_H
#include <unistd.h>
#endif

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

extern int verbose;
extern int hasfinemode;

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

/* for VC++1.5 */
#ifdef DOS
void 
sleep(sec)
int sec;
{
	time_t lt;
	time_t lt2;
	time(&lt);

	while(1){
		time(&lt2);		
		if((lt2 - lt) >= sec)
			return;
	}
}
#endif

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

int
QVgetfd()
{
  return QVfd;
}

/*------------------------------------------------------------*/
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);
}

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);
  }
  check_sum = check_sum + (int) c;
}

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;
}

int checksum(u_char u)
{
	u_char s;
	s = 0xff & (~check_sum);
	if(u != s) {
		if(s == rbyte())
			return(1);
		fprintf(stderr,"checksum error.\n");
		return(-1);
	}
	return(1);
}

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);
  }
  check_sum = check_sum + calcsum(p, len);
}

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){
      check_sum = 0;
      return 1;			/*ok*/
    }
  }
  return 0;			/*ng*/
}

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

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

  if(flag)
    wstr("QR", 2);
  else
    wstr("QE", 2);

  s = rbyte();			/*supposed to be 0x5c('Z') or 0x69*/
#if !defined(__FreeBSD__)  /* Why ? */
  if(checksum(s) == -1)
	  return(-1);
#endif
  wbyte(ACK);

  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 */
    if(s == 0x62) break;
  }
  wbyte(ACK);
  n = rbyte();			/*# of pictures*/
  return (int) n;
}

int
QVremain(n)  /* 100 only may be*/
     int	n;
{
  u_char	s;

  if (!QVok())
    return -1;			/*ng*/
  if(n)
    wstr("Eb", 2); /* fine picture remain */
  else
    wstr("EB", 2); /* normal picture remain */
  s = rbyte();			
  if(checksum(s) == -1) return(-1);
  wbyte(ACK);
  s = rbyte();  /* remain picture num */
  return (int) s;
}

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*/
  if(checksum(s) == -1) return(-1);
  wbyte(ACK);
  return 1;			/*ok*/
}

int
QVswstat()
{
  u_char	s;
  int r;

  if (!QVok())
    return -1;			/*ng*/
  wstr("DS", 2);
  wbyte(0x02);
  s = rbyte();			
  if(checksum(s) == -1) return(-1);
  wbyte(ACK);
  s = rbyte();  /* SW status ?*/
  r = s << 8;
  s = rbyte();  /* SW status ?*/
  r = r | s ; 
  return (int) r;
}

long
QVrevision()
{
  u_char	s;
  long r;

  if (!QVok())
    return -1;			/*ng*/
  wstr("SU", 2);
  s = rbyte();			
  if(checksum(s) == -1) return(-1);
  wbyte(ACK);
  s = rbyte();  /* revision ? */
  r = s;
  s = rbyte();  
  r = r << 8;
  r = r | s;
  s = rbyte();  /* revision ? */
  r = r << 8;
  r = r | s;
  s = rbyte();  
  r = r << 8;
  r = r | s;
  return (long) r;
}

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;
#if defined(WIN32) || defined (OS2) || defined(__FreeBSD__) || defined(DOS)
    baud = B115200;
#else
    baud = B38400;
#endif
    break;
  case TOP:			/* 57600 baud */
    n = 7;
#if defined(WIN32) || defined(OS2) || defined(__FreeBSD__) || defined(DOS)
    baud = B57600;
#else
    baud = B38400;
#endif
    break;
  case HIGH:			/* 38400 baud */
#ifdef X68
    if(hasfinemode)
      n = 11;  /* QV-100/300 */
    else
      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);

  s = rbyte();
  if (checksum(s) == -1) return(-1);
  wbyte(ACK);
  sleep(1);			/* ??? */
  changespeed(QVfd, baud);
  current_speed = speed;
  if (!QVok())
    return -1;			/*ng*/
  return 1;			/*ok*/

}

/*------------------------------------------------------------*/
int 
QVsectorsize(n)
     int n;
{
  u_char s;
  u_char t;
  s = (u_char) (n >> 8) & 0xff;
  t = (u_char) n &  0xff;
  if (!QVok())
    return -1;                  /*ng*/
  wstr("PP", 2);
  wbyte(s);
  wbyte(t);
  s = rbyte();
  if(checksum(s) == -1) return(-1);
  wbyte(ACK);
  return 1;
}

#ifdef USEWORKFILE
int
QVblockrecv_file(fp)
     FILE	*fp;
{
  u_char	s;
  u_char	t;
  u_char	*p;
  int i = 0;
  u_int	sectorsize;
  int retrycount = RETRY;
  int sum;
  u_char buf[SECTOR];

  wbyte(DC2);


  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:;
	p = buf;
    /* 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);
    /* 3: finalize sector */
    s = rbyte();		/*sector type?*/
    t = 0xff & ( ~ rbyte());	/*checksum?*/
    sum = 0xff & (sum + s);
    if(sum != t){
      flushtty(QVfd);
      wbyte(NAK);
      goto retry;
    }
	i += sectorsize;
	fwrite(p, sizeof(u_char), sectorsize, fp);
		
    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 i;
}
#endif

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 < SECTOR)
      sectorsize = rest;
    else
      sectorsize = SECTOR;

    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);
  }

  if (verbose)
    fprintf(stderr, "%6d\b\b\b\b\b\b", p - buf);
  /* 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;
}

int
QVbattery()
{
  u_char        s;

  if (!QVok())
    return -1;                  /*ng*/
  wstr("RB", 2);
  wbyte(ENQ);
  wbyte(0xFF);
  wbyte(0xFE);
  wbyte(0xE6);
  s = rbyte();                  /* check sum 0x83 */
  if(checksum(s) == -1) return(-1);
  wbyte(ACK);
  s = rbyte();                  /* battery */
  return (int) s;
}


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

  if (!QVok())
    return -1;                  /*ng*/
  wstr("DV", 2);
  wbyte(n);
  s = rbyte(); 
  if(checksum(s) == -1) return(-1);
  wbyte(ACK);
  return (int) 1;
}



