#include "config.h"
#include <stdio.h>
#include <string.h>
#ifdef BINARYFILEMODE
#include <fcntl.h>  /* for setmode() */
#endif
#include <stdlib.h>
#include <time.h>
#if HAVE_UNISTD_H
# include <sys/types.h>
# include <unistd.h>
#endif
#if HAVE_SYS_PARAM_H
# include <sys/param.h>
#else
#define MAXPATHLEN 256
#endif
#include "common.h"
#include "command.h"
#include "command1.h"
#ifdef X68
#include "tty_x68.h"
#else
#ifdef WIN32
#include "tty_w32.h"
#include "getopt.h"
#else
#include "tty.h"
#endif
#endif
#include "jpeg.h"
#include "ppm.h"
#include "bmp.h"
#include "common.h"

#ifdef BINARYFILEMODE
#define WMODE "wb"
#define RMODE "rb"
#else
#define WMODE "w"
#define RMODE "r"
#endif
extern int	optind, opterr;
extern char	*optarg;

#define OUTFILENAME_JPG "%s_%03d.jpg"
#define OUTFILENAME_JPG0 "qv_%03d.jpg"

#define OUTFILENAME_PPM "%s_%03d.ppm"
#define OUTFILENAME_PPM0 "qv_%03d.ppm"

#define OUTFILENAME_CAM "%s_%03d.cam"
#define OUTFILENAME_CAM0 "qv_%03d.cam"

#define OUTFILENAME_RGB "%s_%03d.rgb"
#define OUTFILENAME_RGB0 "qv_%03d.rgb"

#define OUTFILENAME_BMP "%s_%03d.bmp"
#define OUTFILENAME_BMP0 "qv_%03d.bmp"

static int     format = JPEG;
int	verbose = 0;
static int	raw_data = 0;
static int     speed = 0;

static	int	errflg = 0;
static	int	all_pic_num = -1;
#ifndef DONTCAREUID
static	uid_t	uid, euid;
static	gid_t	gid, egid;
static	int	uidswapped = 0;
#endif

void usage()
{
  static	char	*usagestr[] =  {
    "qvplay (Ver 0.10) (c)1996 ken-ichi HAYASHI, itojun\n",
    "\t -h           : show this usage.\n",
    "\t -n           : print how many pictures in QV10.\n",
    "\t -p num       : show picture on LCD.\n",
    "\t -o filename  : set output filename.\n",
    "\t -g num       : get a picture data in QV10.\n",
    "\t -a           : get all picture data in QV10.\n",
    "\t -F format    : picture format.[jpeg ppm PPM rgb RGB bmp BMP cam]\n",
    "                  (use with -a or -g).\n",
    "\t -s num       : start picture number.(use with -a)\n",
    "\t -e num       : end picture number.(use with -a)\n",
    "\t -v           : verbose mode(use with -a or -g)\n",
    "\t -r           : reset QV10.\n",
    "\t -d num       : delete picture in QV10.\n",
    "\t -t           : take a picture.\n",
#if defined(__linux__) || defined(WIN32)
    "\t -S speed     : serial speed. [normal middle high top light]\n",
#else
    "\t -S speed     : serial speed. [normal middle high]\n",
#endif
#ifndef X68
    "\t -D ttydevice : set tty(cua) device.\n",
#endif
    (char *)NULL,
  };
  char	**p;

  p = usagestr;
  while (*p)
    fprintf(stderr, *p++);
}

void Exit(code)
     int code;
{
  QVchangespeed(DEFAULT);
  if (!(QVgetfd() < 0))
    closetty(QVgetfd());
  exit(code);
} 

void
get_picture(n, outfilename, format)
     int	n;
     char *outfilename;
     int  format;
{
  int	len;
  u_char	*buf;
  FILE	*outfp;

  if (all_pic_num < n) {
    fprintf(stderr, "picture number is too large.\n");
    errflg ++;
    return;
  }

  switch(format){
  case JPEG:
    buf = (u_char *)malloc(JPEG_MAXSIZ);
    break;
  case PPM_T:
  case RGB_T:
  case BMP_T:
    buf = (u_char *)malloc(THUMBNAIL_MAXSIZ);
    break;
  case PPM_P:
  case RGB_P:
  case BMP_P:
  default:
    buf = (u_char *)malloc(YCC_MAXSIZ);
    break;
  }

  if (buf == (u_char *)NULL) {
    fprintf(stderr, "can't alloc\n");
    errflg ++;
    return;
  }

  outfp = stdout;
  if (outfilename) {
    outfp = fopen(outfilename, WMODE);
    if (outfp == NULL){
      fprintf(stderr, "can't open outfile(%s).\n, outfilename");
      errflg ++;
      goto cleanup0;
    }
  }
#ifdef BINARYFILEMODE
  if(outfp == stdout){
#ifdef WIN32
  	_setmode(_fileno(stdout), _O_BINARY);
#else
  	setmode(fileno(stdout), O_BINARY);
#endif
  }
#endif  
  

  len = QVgetpicture(n, buf, format);
  if (len < 0) {
    errflg ++;
    goto cleanup;
  }

  if (raw_data)
    write_file(buf, len, outfp);
  else 
    switch(format){
    case PPM_T:
      write_ppm(buf, outfp,
		THUMBNAIL_WIDTH, THUMBNAIL_HEIGHT,
		2, 2,
		1, 0);
      break;
    case PPM_P:
      write_ppm(buf, outfp, 
		PICTURE_WIDTH, PICTURE_HEIGHT,
		3, 2,
		1, 0);
      break;
    case RGB_T:
      write_ppm(buf, outfp,
		THUMBNAIL_WIDTH, THUMBNAIL_HEIGHT,
		2, 2,
		0, 0);
      break;
    case RGB_P:
      write_ppm(buf, outfp, 
		PICTURE_WIDTH, PICTURE_HEIGHT,
		3, 2,
		0, 0);
      break;
    case BMP_T:
      write_bmp(buf, outfp,
		THUMBNAIL_WIDTH, THUMBNAIL_HEIGHT,
		2, 2);
      break;
    case BMP_P:
      write_bmp(buf, outfp, 
		PICTURE_WIDTH, PICTURE_HEIGHT,
		3, 2);
      break;
    case JPEG:
    default:
      write_jpeg(buf, outfp);
      break;
    }
 cleanup:;
  if (outfp != stdout)
    fclose(outfp);
 cleanup0:;
  free(buf);
}

void
get_camfile(n, outfilename)
     int	n;
     char	*outfilename;
{
  long	len;
  long    lenj;
  char buf1[64];
  char buf5[64];
  u_char	*bufj;
  u_char	*bufp;
  FILE	*outfp;
  int i;
  time_t tt;

  if (all_pic_num < n) {
    fprintf(stderr, "picture number is too large.\n");
    errflg ++;
    return;
  }

  bufp = (u_char *)malloc(THUMBNAIL_MAXSIZ);
  if (bufp == (u_char *)NULL) {
    fprintf(stderr, "can't alloc\n");
    errflg ++;
    return;
  }
  bufj = (u_char *)malloc(JPEG_MAXSIZ);
  if (bufj == (u_char *)NULL) {
    fprintf(stderr, "can't alloc\n");
    errflg ++;
    return;
  }

  outfp = stdout;
  if (outfilename) {
    outfp = fopen(outfilename, WMODE);
    if (outfp == NULL){
      fprintf(stderr, "can't open outfile(%s).\n, outfilename");
      errflg ++;
      goto cleanup0;
    }
  }

  fputc(0x07, outfp);		/* CAM header */
  fputc(0x20, outfp);
  fputc(0x4d, outfp);
  fputc(0x4d, outfp);

  fputc(0x00, outfp);
  fputc(0x03, outfp);

  fputc(0x00, outfp);		/* comment area  size */
  fputc(0x01, outfp);
  sprintf(buf1,"Generated by qvplay-0.10");
  tt = time(0);
  sprintf(buf5,"%s", asctime(localtime(&tt)));
  len = strlen(buf5) - 1;
  buf5[len] = '\0';
  len = len + strlen(buf1);

  len = len + 14 +1 ;		/* (ID + 00) * 7  + 00 */
  fputc((len >> 24) & 0xff , outfp);
  fputc((len >> 16) & 0xff , outfp);
  fputc((len >> 8) & 0xff , outfp);
  fputc(len & 0xff , outfp);
  for(i = 0 ; i < 10 ; i ++)
    fputc(0x00, outfp);		/* dummy */

  fputc(0x00, outfp);		/* thumbnail area  size */
  fputc(0x02, outfp);
  fputc(0x00, outfp);
  fputc(0x00, outfp);
  fputc(0x15, outfp);
  fputc(0xf0, outfp);		/* 5616 = 3 * 52 * 36 */
  for(i = 0 ; i < 10 ; i ++)
    fputc(0x00, outfp);		/* dummy */
	
  fputc(0x00, outfp);		/* jpeg area  size */
  fputc(0x03, outfp);
  lenj = QVgetpicture(n, bufj, JPEG);
  if (lenj < 0) {
    errflg ++;
    goto cleanup;
  }
  fputc((lenj >> 24) & 0xff , outfp);
  fputc((lenj >> 16) & 0xff , outfp);
  fputc((lenj >> 8) & 0xff , outfp);
  fputc(lenj & 0xff , outfp);
  for(i = 0 ; i < 10 ; i ++)
    fputc(0x00, outfp);		/* dummy */
	
  /* comment  */
  fputc(0x01, outfp);
  write_file((u_char *)buf1, strlen(buf1) +1 , outfp);
  fputc(0x02, outfp); fputc(0x00, outfp);
  fputc(0x03, outfp); fputc(0x00, outfp);
  fputc(0x04, outfp); fputc(0x00, outfp);
  fputc(0x05, outfp);
  write_file((u_char *)buf5, strlen(buf5) +1 , outfp);

  fputc(0x06, outfp); fputc(0x00, outfp);
  fputc(0x07, outfp); fputc(0x00, outfp);

  fputc(0x00, outfp);

  len = QVgetpicture(n, bufp, PPM_T);
  if (len < 0) {
    errflg ++;
    goto cleanup;
  }
  write_ppm(bufp, outfp,
	    THUMBNAIL_WIDTH, THUMBNAIL_HEIGHT,
	    2, 2,
	    0, 0);
  write_file(bufj, lenj, outfp);

 cleanup:;
  if (outfp != stdout)
    fclose(outfp);
 cleanup0:;
  free(bufp);
  free(bufj);
}

void
show_picture(n)
     int	n;
{
  if (all_pic_num < n) {
    fprintf(stderr, "picture number is too large.\n");
    errflg ++;
    return;
  }

  if (QVshowpicture(n) < 0)
    errflg ++;
}

void
get_all_pictures(start, end, outfilename, format)
     int	start;
     int	end;
     char	*outfilename;
     int format;
{
  int	i;
  char	fname[MAXPATHLEN];

  if (start == 0)
    start = 1;
  if (end == 0)
    end = all_pic_num;
  if (all_pic_num < start || all_pic_num < end) {
    fprintf(stderr, "picture number is too large.\n");
    errflg ++;
    return;
  }
  if (start > end) {
    int	tmp;
    tmp = end;
    end = start;
    start = tmp;
  }

  for (i = start; i <= end; i++) {
    switch(format){
    case PPM_P:
    case PPM_T:
      if (outfilename)
	sprintf(fname, OUTFILENAME_PPM, outfilename, i);
      else
	sprintf(fname, OUTFILENAME_PPM0, i);
      break;
    case RGB_P:
    case RGB_T:
      if (outfilename)
	sprintf(fname, OUTFILENAME_RGB, outfilename, i);
      else
	sprintf(fname, OUTFILENAME_RGB0, i);
      break;
    case BMP_P:
    case BMP_T:
      if (outfilename)
	sprintf(fname, OUTFILENAME_BMP, outfilename, i);
      else
	sprintf(fname, OUTFILENAME_BMP0, i);
      break;
    case CAM:
      if (outfilename)
	sprintf(fname, OUTFILENAME_CAM, outfilename, i);
      else
	sprintf(fname, OUTFILENAME_CAM0, i);
      break;
    case JPEG:
    default:
      if (outfilename)
	sprintf(fname, OUTFILENAME_JPG, outfilename, i);
      else
	sprintf(fname, OUTFILENAME_JPG0, i);
      break;
    }
    if(format == CAM)
      get_camfile(i, fname);
    else
      get_picture(i, fname, format);
  }
}

void
delete_picture(n)
     int	n;
{
  if (all_pic_num < n) {
    fprintf(stderr, "picture number is too large.\n");
    errflg ++;
    return;
  }

  if (QVdeletepicture(n) < 0)
    errflg ++;
  all_pic_num = -1;		/*need update*/
}

void
take_picture()
{
  if (96 <= all_pic_num) {
    fprintf(stderr, "picture full.\n");
    errflg ++;
    return;
  }

  if (QVtakepicture() < 0)
    errflg ++;
  all_pic_num = -1;		/*need update*/
}

#ifdef X68
void
show_on_X68k(n)
     int n;
{
  int	len;
  u_char	*buf;
  if (all_pic_num < n) {
    fprintf(stderr, "picture number is too large.\n");
    errflg ++;
    return;
  }
  buf = (u_char *)malloc(YCC_MAXSIZ);
  if (buf == (u_char *)NULL) {
    fprintf(stderr, "can't alloc\n");
    errflg ++;
    return;
  }
  len = QVgetpicture(n, buf, PPM_P);
  if (len < 0) {
    errflg ++;
    goto cleanup0;
  }
  write_x68k(buf, PICTURE_WIDTH, PICTURE_HEIGHT, 3, 2);
  
 cleanup0:;
  free(buf);

}
#endif
#ifndef DONTCAREUID
void
daemonuid()
{
  if (uidswapped) {
#ifdef HAVE_SETREUID
    setreuid(uid, euid);
    setregid(gid, egid);
#else
    setuid(uid);
    seteuid(euid);
    setgid(gid);
    setegid(egid);
#endif
    uidswapped = 0;
  }
}

void
useruid()
{
  if (!uidswapped) {
#ifdef HAVE_SETREUID
    setregid(egid, gid);
    setreuid(euid, uid);
#else
    setgid(egid);
    setegid(gid);
    setuid(euid);
    seteuid(uid);
#endif
    uidswapped = 1;
  }
}
#endif

void
main(argc, argv)
     int	argc;
     char	**argv;
{
  char	*devpath = NULL;
  char	*outfilename = NULL;
  int	start_picture = 0;
  int	end_picture = 0;
  int     move_from = 0 ;
  int     move_to = 0 ;
  char	c;

#ifndef DONTCAREUID
  uid = getuid();
  euid = geteuid();
  gid = getgid();
  egid = getegid();
  useruid();
#endif

  devpath = getenv("QVPLAYTTY");

  if(devpath == NULL)
    devpath = strdup(RSPORT);

  if (devpath) {
#ifndef DONTCAREUID
    daemonuid();
#endif
    QVsetfd(opentty(devpath));
#ifndef DONTCAREUID
    useruid();
#endif
  }

  while ((c = getopt( argc, argv, "D:p:o:g:rRnas:e:d:tvF:S:X:h")) != EOF) {
    if(c == 'h'){
      usage();
      Exit(-1);
    }
    if(!(QVgetfd() < 0)){
      all_pic_num = QVhowmany();
    }

    switch(c) {
    case 'D':
      if (!(QVgetfd() < 0))
	closetty(QVgetfd());

      devpath = optarg;

#ifndef DONTCAREUID
      daemonuid();
#endif
      QVsetfd(opentty(devpath));
#ifndef DONTCAREUID
      useruid();
#endif
      if (QVgetfd() < 0)
	Exit(1);
      if (all_pic_num < 0) 
	all_pic_num = QVhowmany();
      if (all_pic_num < 0)
	Exit(1);
      break;
    case 'p':
      show_picture(atoi(optarg));
      break;
    case 'o':
      outfilename = optarg;
      break;
    case 'g':
      if(format == CAM)
	get_camfile(atoi(optarg), outfilename);
      else
	get_picture(atoi(optarg), outfilename, format);
      break;
    case 'r':
      QVchangespeed(DEFAULT);
      QVreset(1);
      break;
    case 'R':
      QVchangespeed(DEFAULT);
      QVreset(0);
      break;
    case 'n':
      printf("pictures = %d\n", all_pic_num);
      break;
    case 'a':
      get_all_pictures(start_picture, end_picture, outfilename, format);
      break;
    case 's':
      start_picture = atoi(optarg);
      break;
    case 'e':
      end_picture = atoi(optarg);
      break;
    case 'v':
      verbose = 1;
      break;
    case 'd':
      delete_picture(atoi(optarg));
      break;
    case 't':
      take_picture();
      break;
    case 'm':			/* don't use */
      /* QVmovepicture is very dangerous */
      if(sscanf(optarg, "%d,%d", &move_from, &move_to)
	 != 2)
	break;
      if(move_from > all_pic_num) break;
      if(move_from < 1) break;
      if(move_to > all_pic_num) break;
      if(move_to < 1) break;
      QVmovepicture(move_from, move_to);
      break;
    case 'F':
      {
	char *p;
	if(optarg[0] == '+'){
	  raw_data = 1;
	  p = &optarg[1];
	} else
	  p = &optarg[0];
	switch(*p){
	case 'j':
	  format = JPEG;
	  break;
	case 'p':
	  format = PPM_T;
	  break;
	case 'P':
	  format = PPM_P;
	  break;
	case 'r':
	  format = RGB_T;
	  break;
	case 'R':
	  format = RGB_P;
	  break;
	case 'b':
	  format = BMP_T;
	  break;
	case 'B':
	  format = BMP_P;
	  break;
	case 'c':
	  format = CAM;
	  break;
	default:
	  format = JPEG;
	  break;
	}
      }
      break;
#ifdef X68
    case 'X':
      show_on_X68k(atoi(optarg));
      break;
#endif
    case 'S':
      switch(optarg[0]){
#if defined(__linux__) || defined(WIN32)
      case 'l':
      case '5':
	speed = LIGHT;
	break;
      case 't':
      case '4':
	speed = TOP;
	break;
#endif
      case 'h':
      case '3':
	speed = HIGH;
	break;
      case 'm':
      case '2':
	speed = MID;
	break;
      default:
	speed = DEFAULT;
	break;
      }
      QVchangespeed(speed);
      break;
    default:
      usage();
      Exit(-1);
    }
  }

  Exit (errflg ? 1 : 0);
}
