

#include "clarg.h"
#include "image.h"
#include "grab.h"
#include "vector.h"
#include "string-support.h"
#include "x-stuff.h"

#include <signal.h>
#include <sys/termios.h>
#include <sys/types.h>
#include <sys/time.h>

extern int setitimer(int which, struct itimerval *value, struct itimerval *ovalue);


static char *USAGE =
  "\n%s:  grab a gray image from a SparcStation VideoPix card.\n"
  "\n"
  "Arguments are:\n"
  "   -output file:         Specify the output file name(s) (PGM)\n"
  "   -downsample Dx [Dy]:  Specify the downsampling in x and y.  With only\n"
  "                         one argument, specifies both x and y factors.\n"
  "   -n N:                 Grab N frames, instead of a single frame.\n"
  "                         If specified, an output filename must be given\n"
  "                         containing a single printf() integer conversion,\n"
  "                         eg. \"filename%%03d.pgm\".\n"
  "   -t delay              Specify (minimum) time between grabbed frames, in sec.\n"
  "   -i                    Use (text) interactive mode.\n"
  "   -x [D]                Use X-windows interactive preview mode, and optionally.\n"
  "                           specify a downsampling factor. The default is 4.\n"
  "\n"
  "Grabs an image from the VideoPix in a SparcStation.  If downsampling\n"
  "is requested, it is done, otherwise a full size image is grabbed.\n"
  "The image is output to the file specified, or stdout if no file name is\n"
  "given.  Argument names may be abreviated.\n\n";


#define OUTPUT 	  "o.utput"
#define DOWN	  "d.ownsample"
#define FRAMES    "n"
#define DELAY     "t"
#define INTERACT  "i"
#define XWIN      "x"
#define  PAUSE "p"

CLINIT clinit[] = {{OUTPUT,   1, 1,  NULL,    0},
			  {DOWN,     1, 2,  cl_int,  0},
			  {FRAMES,   1, 1,  cl_int,  0},
			  {DELAY,    1, 1,  cl_real, 0},
			  {INTERACT, 0, 0,  NULL,    0},
			  {XWIN,     0, 1,  cl_int,  0},
                          {PAUSE, 0, 0, NULL, 0},   
			  {NULL,     0, 0,  NULL,    0}};


static char *depend[] = {FRAMES"=>"OUTPUT, NULL};


static volatile boolean done_waiting = FALSE;

static void handle_timer_interrupt(int sig)
{
  done_waiting = TRUE;
}


int main(int argc, char **argv, char **envp)
{
  PtrVector images;
  GrayImage img, prev_img=NULL;
  int dx, dy;
  int n, i;
  char *output;
  String number = NULLSTRING;
  boolean mult = FALSE;
  char outfile[200];
  boolean delay;
  boolean xwin;
  boolean interactive;
  int x_down = 0;
  struct itimerval timer;
  struct timeval delay_time;
  xWindow prev_win;
  boolean timer_warning = FALSE;
  boolean pause;
  

  if (cl_init(argc, argv, envp, clinit, depend, USAGE, CL_NORESTRICT))  /* parse args */
    return 0;

  output = cl_opt(OUTPUT) ? cl_arg1(OUTPUT) : NULL;
  
  if (cl_opt(DOWN)) {
    if (cl_numarg(DOWN) == 1) {
      dx = dy = atol(cl_arg1(DOWN));
    }
    else {
      dx = atol(cl_arg1(DOWN));
      dy = atol(cl_arg2(DOWN));
    }
  }
  else {
    dx = dy = 1;
  }

  if (dx < 1  ||  dy < 1) {
    fprintf(stderr, "GRAB: Downsampling factors must be positive\n");
    return 0;
  }

  if (cl_opt(FRAMES)) {
    n = atol(cl_arg1(FRAMES));
    mult = TRUE;

    if (n <= 0) {
      fprintf(stderr, "GRAB: Number of frames to grab must be positive\n");
      return 0;
    }
  }
  else {
    n = 1;
  }

  pause = cl_opt(PAUSE);
  
  if ((delay = cl_opt(DELAY))) {
    float delayf = atof(cl_arg1(DELAY));

    if (delayf <= 0.0) {
      delay = FALSE;
    }
    else {
      delay_time.tv_sec  = (int) delayf;
      delay_time.tv_usec = (int) ((delayf - (int) delayf) * 1.0E6);
    }
  }

  if ((xwin = cl_opt(XWIN))) {
    xOpenDisplay();
    xAllocateGreyColorMap();

    x_down = cl_numarg(XWIN) ? atol(cl_arg1(XWIN)) : 4;
    x_down = (x_down <= 0) ? 4 : x_down;
  }
  
  interactive = xwin || cl_opt(INTERACT);
  
  images = (PtrVector) vecNew(VECTOR_PTR, (unsigned) n);
  if (images == (PtrVector) NULL) {
    fprintf(stderr, "GRAB: Error allocating image vector\n");
    return 0;
  }

  /* pre-allocate the images now */

  { int xsize = grabVideoPixWidth(dx);
    int ysize = grabVideoPixHeight(dy);

    if (interactive) {
      fprintf(stderr, "\n\nAllocating %d image%s ... ", n, n == 1 ? "" : "s");
    }
    
    for (i = 0; i < n; i++) {
      if ((vecRef(images, i) = (void *) imNew(IMAGE_GRAY, xsize, ysize)) == NULL) {
	fprintf(stderr, "GRAB: Error allocating images.\n");
	return 0;
      }
    }
  }
    
  /* now pre-connect to video pix before interactive ... this is really slow. */

  if (interactive) {
    fprintf(stderr, "Done.\n\nConnecting to the VideoPix ... ");
    
    if (grabConnectToVideoPix(0) == 0) {
      fprintf(stderr, "Sorry, unable to connect.\n\n");
      return 0;
    }

    fprintf(stderr, "OK\n\n");      

    if (xwin) {				     /* grab first preview and display */
      prev_img = grabVideoPix(downsample: x_down);
      prev_win = xShowImage(prev_img, "Grab preview", 20, 20);
    }
    

    if (xwin) {
      boolean pause_preview = FALSE;
      boolean done_preview  = FALSE;
      int button;
      
      
      fprintf(stderr, "Press button 1 to begin grabbing, button 2 to pause: ");
     
      while (!done_preview) {
	while (!(button = xGetButton(prev_win))) {
	  if (!pause_preview) {
	    grabVideoPix(into: prev_img, downsample: x_down);
	    xShowImageIn(prev_img, prev_win);
	  }
	}

	if (button == 1) {
	  done_preview = TRUE;
	}
	else {
	  pause_preview = !pause_preview;
	}
      }
    }
    else {
      fprintf(stderr, "Press return to begin grabbing: ");
      getc(stdin);
    }

    fprintf(stderr, "\nGrabbing %d frame%s\n\n     ", n, n==1 ? "" : "s");
  }

  if (delay) {				     /* install handler */
    signal(SIGALRM, handle_timer_interrupt);
  }
  
  for (i = 0; i < n; i++) {		     /* grab frames */
    if (delay) {			     /* start timer */
      done_waiting = FALSE;

      timerclear(&(timer.it_interval));
      timer.it_value = delay_time;
      setitimer(ITIMER_REAL, &timer, NULL);
    }

    img = (GrayImage) vecRef(images, i);
    if (grabVideoPix(into: img, down_x: dx, down_y: dy) == NULL) {
      fprintf(stderr, "GRAB: Error grabbing from the VideoPix\n");
      return 0;
    }
    
    number = strPrintF(number, "(%4d of %4d)", i+1, n);
    imDocString(img) = strNewComment(argc, argv, envp, NULLSTRING, number);

    if (interactive) {			     /* print some dots */
      if (i > 0) {
	if (i % 50 == 0)
	  fprintf(stderr, "\n     ");
	else if (i % 10 == 0)
	  putc(' ', stderr);
      }
      
      putc('.', stderr);
    }

    if (delay && done_waiting) {
      timer_warning = TRUE;
    }


    if (pause && i+1 < n) {
      boolean pause_preview = FALSE;
      boolean done_preview  = FALSE;
      int button;

      while (!done_preview) {
	while (!(button = xGetButton(prev_win))) {
	  if (!pause_preview) {
	    grabVideoPix(into: prev_img, downsample: x_down);
	    xShowImageIn(prev_img, prev_win);
	  }
	}

	if (button == 1) {
	  done_preview = TRUE;
	}
	else {
	  pause_preview = !pause_preview;
	}
      }
    }
    
    while (delay && !done_waiting)	     /* wait for timer */
      ;
  }

  if (interactive) {
    if (timer_warning) {
      fprintf(stderr, "\nWarning: grabbing rate requested may not have been achieved.");
    }
    
    fprintf(stderr, "\n\nDone grabbing.  Saving %d image%s ... ", n, n==1 ? "" : "s");
  }

  for (i = 0; i < n; i++) {		     /* write 'em out */
    if (mult)
      sprintf(outfile, output, i);

    if (imSave(((GrayImage) vecRef(images, i)), mult ? outfile : output)) {
      fprintf(stderr, "GRAB: Error writing file \"%s\"\n", mult ? outfile : outfile);
      return 0;
    }
  }

  if (xwin) {
    xKillWindow(prev_win);
  }
  
  if (interactive) {
    xCloseDisplay();
    fprintf(stderr, "\n\n");
  }

  grabDisconnectVideoPix();
  
  return 0;
}
