/**************************************************************************
 * File: maindiss.c             MAINDISS.C
 * Programmer: Justin Miller Cornell 
 * Project: Dissolve Detection
 *
 * Purpose: 
 * Limitations: 
 * This File: 
 *   - command line version of dissolvem, most people will probably want to
 *     use the mpeg_browse version (and all its braindamage).
 *   -
 * Last Sanity Check: never.
 *
 * ToDo: Make more stable.  Fix interface. 
 * 
 * Compile Time directives:
 *  - MULTIPROC - the multiple process version, uses shared memory to buffer
 *       results.
 *  - USING_GDB - turns off multiple process mode.
 *
 * See Also: 
 *
 * Copyright (c) 1995 Cornell University.  All Rights Reserved.
 *************************************************************************/
static char rcsid[] = "@(#)$Header: /usr/u/jmiller/prog/ImDiss/src/RCS/dissolvem.c,v 1.2 1995/07/11 00:11:06 jmiller Exp jmiller $";
static char versionid[] = "Test Version 1.0.1.  Version date : 951009.";

#include <stdio.h>
#include <sys/types.h>
#include <sys/mman.h>
#include <sys/wait.h>
#include "misc.h"
#include "math.h"
#include "image.h"
#include "assert.h"
#include "solaris.h"
#include "routines.h"      /* pgm crop*/
#include "boxGaussian.h"   /* smoothing*/
#include "cannyEdge.h"     /* edge detection*/
#include "hausd_corr.h"    /* binary hausd*/
#include "utility.h"
#include "datafile.h"
#include "dissolvem.h"

#ifdef USING_GDB
#undef MULTIPROC
#endif
static int    dispX        = HORZ_CORR;
static int    dispY        = VERT_CORR;
static int maxXglob=0;
static int maxYglob=0;
comp *SMaddr = (comp *)NULL;
int SMsize = 10*sizeof(comp);


/* PROCEDURES*/
/*
 *
 *  main()
 * 
 *
 * ToDo: there needs to be range checking on the inputs, and a graceful
 *   exit.  The -h info is currently wrong.
 *
 */

/* GLOBAL VARIABLE */
static char *usage2 = 
"\n%s: Calculates the motion vector between successive image frames.\n"
"
Usage: %s [options] frame1.pgm frame2.pgm ...

Options:
 -a fract : try to maintain (fract) precentage of edge pixels.
 -b fract : bi-directional -a
 -c thresh frac : hybrid of -a -t
 -d       : calculate the differential pgm
 -D range : Disparity range (horz and vert).
 -e       : output edge images to current directory using .e and .f
            extensions.
 -E dest  : output edge images to destination directory.
 -h dist. : Manhattan distance for dialation of edge image.
 -L (0,1) : 0 for canny edges, 1 for gaussian edges
 -P num   : split the work between num forked processes.
 -q frac  : quick mode
 -s sigma : sigma for the smoothing image.
 -S step  : number of frames to step by.  (-P compatibility iffy)
 -t thres : threshold for canny edge detection.
 -T       : use two passes
 -v       : verbal mode
 -w       : use text output
 -?       : this message
 
Output:
 A vector file on stdout.

Notes:
";

/* MAIN PROCEDURE*/
int main(int argc, char **argv, char **envp){
  char *exec_name = argv[0];
  int origargc = argc;
  char **origargv = argv;
  GrayImage gimage, gimagePrev;
  int mode = MODE_DEFAULT;
  int useText = 0;
  int nFrames, j, i, x;
  int pid, startAT, stopAT, chunk;
  int skip = 1;
  int nProcesses = 1;
#ifdef MULTIPROC 
  int fdSM;
#endif

  /* PARSE ARGS */
  while ((argc >= 2) && (argv[1][0] == '-')) {
    switch(argv[1][1]) {
/*    case 'A' : {
      minThreshold = atoi(argv[2]);
      maxThreshold = atoi(argv[3]);
      argc-=3;
      argv+=3;
      break;
    } */
    case 'a' : {
      threshMode = 1;
      threshold = 0;   /* IMPORTANT */
      desiredP = atof(argv[2]);
      argc-=2;
      argv+=2;
      break;
    } 
    case 'b' : {
      threshMode = 2;
      threshold = 0;   /* IMPORTANT */
      desiredP = atof(argv[2]);
      argc-=2;
      argv+=2;
      break;
    } 
    case 'c' : {
      threshMode = 3;
      threshold = 0;   /* IMPORTANT */
      threshModeCval = atoi(argv[2]);
      threshModeCval = threshModeCval * threshModeCval;
      desiredP = atof(argv[3]);
      argc-=3;
      argv+=3;
      break;
    }
#ifndef NO_CENSUS
    case 'C' : {
      useCensus = 1;
      argc-=1;
      argv+=1;
      break;
    }
#endif
#ifndef NO_DIFFPGM
    case 'd' : {
      calcDiffPGM = 1;
      argc-=1;
      argv+=1;
      break;
    } 
#endif
    case 'D' : {
      dispY = dispX = atoi(argv[2]);
      argc-=2;
      argv+=2;
      break;
    }
    case 'e' : {
      edgeOutput = 1;
      edgeDirName = ".";
      argc-=1;
      argv+=1;
      break;
    } 
    case 'E' : {
      edgeOutput = 1;
      edgeDirName = argv[2];
      argc-=2;
      argv+=2;
      break;
    }  
    case 'h' : {
      dilateDist = atoi(argv[2]);
      argc-=2;
      argv+=2;
      break;
    } 
    case 'I' : {
      inOut = 1;
      argc-=1;
      argv+=1;
      break;
    } 
    case 'L' : {
      edgeDetectMode = atoi(argv[2]);
      argc-=2;
      argv+=2;
      break;
    } 
#ifdef MULTIPROC
    case 'P' : {
      nProcesses = atoi(argv[2]);
      argc-=2;
      argv+=2;
      break;
    } 
#endif
#ifndef MULTIPROC
    case 'P' : {
      fprintf(stderr,"Ignoring -P argument\n");
      argc-=2;
      argv+=2;
      break;
    } 
#endif
    case 'q' : {
      quickMode = 1;
      quickModeCutoff = atof(argv[2]);
      argc-=2;
      argv+=2;
      break;
    } 
    case 's' : {
      sigma = atof(argv[2]);
      argc-=2;
      argv+=2;
      break;
    } 
    case 'S' : {
      skip = atoi(argv[2]);
      argc-=2;
      argv+=2;
      break;
    } 
    case 't' : {
      threshold = atoi(argv[2]);
      argc-=2;
      argv+=2;
      break;
    } 
#ifndef NO_TWOPASS
    case 'T' : {
      useTwoPasses = 1;
      inOut = 1;
      argc-=1;
      argv+=1;
      break;
    } 
#endif
#ifndef NO_RESOLVE_EDGES
    case 'R' : {
      useResolveEdges = 1;
      argc-=1;
      argv+=1;
      break;
    } 
#endif
    case 'v' : {
      verbal=1;
      argc-=1;
      argv+=1;
      break;
    }
    case 'w' : {
      useText=1;
      argc-=1;
      argv+=1;
      break;
    }
    case '?' : {         
      mode = MODE_HELP;        /* help mode*/
      argc-=1;
      argv+=1;
      break;
    } 
    default : {
      fprintf(stderr, "Bad command line argument: %s\n", argv[1]);
      exit(1);
    }
    }
  }
  
  if (argc < 3 || mode == MODE_HELP){
    fprintf(stderr, usage2, exec_name, exec_name);
    exit(1);
  }
  if(edgeDetectMode == EDGE_MODE_CANNY)  /* IMPORTANT*/
      threshold = threshold * threshold;
  
  /* END PARSE ARGS */
  nFrames = argc-1;
  SMsize = nFrames*sizeof(comp);
  /* Initialize shared memory*/
#ifdef MULTIPROC
  fdSM = open("/dev/zero", O_RDWR);
  assert(fdSM !=-1);  
  SMaddr = (comp *)mmap(0, (unsigned)SMsize, 
			PROT_READ|PROT_WRITE, MAP_SHARED, fdSM, 0);
  assert(SMaddr !=(comp *)-1);
#else
  SMaddr = (comp *)malloc(sizeof(comp)*SMsize);
#endif
  for(j=0; i < nFrames; i++){
    SMaddr[j].frame_num = -1;
  }
#ifdef USING_GDB
  nProcesses = 1;
#endif
  argv++;
 /* split the frame sequence between processes*/
  chunk = (nFrames / (nProcesses*skip) + 1) * skip;
  for(j = 0; j < nProcesses; j++){
    startAT = j * chunk;
    (j == nProcesses - 1) ? (stopAT = nFrames-1) : (stopAT = (j+1)*chunk);
    
#ifndef MULTIPROC
    switch(pid = 0){
#else
      switch(pid = fork()){
#endif
      case -1 : {
	printf("fork was unsuccessful \n");
	exit(-1);
	break;
      }
      case 0: {
	sanity(startAT != stopAT, "Each process must get more than 1 frame");
	if(verbal)
	    fprintf(stderr,"Process %d STARTS (frames %d -> %d).\n",j,
		    startAT,stopAT);
	/* INITIALIZE SEQUENCE*/
	/* Load the image from stdin*/
	gimage = (GrayImage)imLoad(IMAGE_GRAY, argv[startAT]);
	if(gimage == (GrayImage)NULL){
	  fprintf(stderr,"Image load error: %s\n",argv[startAT]);
	  exit(-1); 
	}
	/* INITIALIZE GLOBAL VARIABLES*/
	maxXglob = (int)imGetWidth(gimage);
	maxYglob = (int)imGetHeight(gimage);
	diss_storage_init(dispX,dispY,maxXglob,maxYglob);
	
	/* Initialize first frame*/
	diss_first_frame(gimage);

	/* MAIN SEQUENCE LOOP*/
	for(i = startAT+skip; i <= stopAT; i += skip){
	  if(verbal)
	      fprintf(stderr,"(%d:%d,%d)",j,i,stopAT);
	  gimagePrev = gimage;
	  gimage = (GrayImage)imLoad(IMAGE_GRAY, argv[i]);
	  if(gimage == (GrayImage)NULL){
	    fprintf(stderr,"Image load error: %s\n",argv[i]);
	    exit(-1); 
	  }
	  sanity((maxXglob == imGetWidth(gimage) &&
		  maxYglob == imGetHeight(gimage)),
		 "image size changed");
	  SMaddr[i].frame_num = i;
	  diss_next_frame(gimage, SMaddr+i, argv[i]);
	  imFree(gimagePrev);
	}  
	imFree(gimage);
	if(verbal){
	  fprintf(stderr,"Process %d EXITS.\n",j);
	}
#ifdef MULTIPROC
	exit(0);
#endif
	break;
      }
      default : {
	continue;
	break;
      }
#ifndef MULTIPROC
      }
#else
    }
#endif
  }
  /* wait for children, dump output, end*/
  for(i = 0; i < nProcesses; i++){
    wait((int *)NULL);
  }
  
  /* OUTPUT OUTPUT OUTPUT */
  /* first output the version information*/
  fprintf(stdout, "#%s\n",versionid);
  fprintf(stdout, "# Compiled %s %s\n",__TIME__,__DATE__);
  /* then the command-line*/
  fprintf(stdout, "# ");
    for(x = 1; x < origargc-nFrames; x++){
      fprintf(stdout, "%s ",origargv[x]);
    }
  fprintf(stdout, "\n#\n");
  fprintf(stdout,"# disparity %d, skip %d, dilate %d, sigma %f\n",
	  dispX, skip, dilateDist, sigma);
  /* then the actual data*/
  fprintf(stdout,"%d %d\n", (nFrames - 1)/skip, sizeof(comp));

  if(useText){
    for(i = 1+skip; i <= nFrames; i+=skip){
      fprintArrEntry(stdout,SMaddr,i);
    }
    fprintf(stderr,"\n");
  }
  else {
    fprintArrBinary(stdout,SMaddr,compressArr(SMaddr,nFrames));
  }
  exit(0);
}
