/**************************************************************************
 * File: thresher.c             THRESHER.C
 * Programmer: Justin Miller Cornell id# 356452
 * Project: Dissolve Detection
 *
 * Purpose: Determines the best parameters for event discrimination
 * Limitations: very ugly coding.
 * This File: 
 *   -
 *   -
 * Last Sanity Check: never.
 *
 * ToDo: Make more stable.  Fix interface. 
 *
 * See Also: 
 *
 * Copyright (c) 1995 Cornell University.  All Rights Reserved.
 *************************************************************************/
static char rcsid[] = "@(#)$Header: /usr/u/jmiller/prog/ImDiss/src/RCS/thresher.c,v 1.1 1995/07/01 17:01:34 jmiller Exp jmiller $";
#define DBUG
#include <stdio.h>
#include "math.h"
#include "image.h"
#include "assert.h"
#include "misc.h"
#include "datafile.h"

/* TYPE DEFINITIONS*/

/* MAGIC NUMBERS*/
#define MODE_HELP -1
#define MODE_DEFAULT 1
#define MODE_CATTRANS 3
#define MAGIC_PADDING 10
/* GLOBAL VARIABLES*/


/* PROTOTYPES FOR INTERNAL PROCEDURES*/
analysis_set runTests(FILE *file, comp *arr, int arrsize, 
		      tranStruct *reality, int nreality,
		      FILE *tranfile);
void print_output(analysis_set ans, FILE *file, int mode);
void printThresherHeader(FILE *file, int *arrs, int *tr, tranStruct **trans);

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

/* MAGIC NUMBERS*/

/* GLOBAL VARIABLES */
static char *usage2 = 
"\n%s: Calculates the motion vector between successive image frames.\n"
"
Usage: %s [options] seq.out seq.tran

Options:
 -d float : Threshold for discriminating between dissolves and cuts
 -e int   : radius of event suppression region.
 -n int   : radius for the summation region.
 -t float : Threshold for classifying as a scene transition.

 -D f f f : option d, starting at, ending at, step size.
 -E i i i : option e, starting at, ending at, step size.
 -N i i i : option n, starting at, ending at, step size.
 -T f f f : option t, starting at, ending at, step size.

 -C fname : the reality trans file name. (DOESN'T WORK)
 -M int   : mode of operation, either a one or zero

Output:
 A vector file on stdout.

Notes:
";

typedef struct {
  int    start, stop, step;
} iStruct;

typedef struct {
  float  start, stop, step;
} fStruct;


/* Parameters*/
iStruct suppress, region;
fStruct tranThreshold, dissThreshold, noiseThreshold;
int origargc;
char **origargv, **namesargv;
int glob_nstreams, glob_nframes, glob_ntrans;

/* MAIN PROCEDURE*/
int main(int argc, char **argv, char **envp) {
  char *exec_name = argv[0];
  char *reality_fname = (char *)NULL;
  FILE *input, *realf;
  FILE *tranfile = (FILE *)NULL;
  FILE *rtranfile = (FILE *)NULL;
  int mode, M, N,i;
  tranStruct *reality;
  comp *arr;
  tranStruct **allTrans = (tranStruct**)NULL;
  comp **allarrs;
  int *allTransL, *allarrsL;
  int stepsize = 1;
  int compareTrans = 0;

  mode = MODE_DEFAULT;
  origargc = argc;
  origargv = argv;

  /* PARSE ARGS */
  while ((argc >= 2) && (argv[1][0] == '-')) {
    switch(argv[1][1]) {
    case 'C' : {
      reality_fname = argv[2];
      argc-=2;
      argv+=2;
      break;
    } 
    case 'd' : {
      dissThreshold.start = dissThreshold.stop = (float)atof(argv[2]);
      dissThreshold.step = 1;
      argc-=2;
      argv+=2;      
      break;
    } 
    case 'D' : {
      dissThreshold.start = (float)atof(argv[2]);
      dissThreshold.stop  = (float)atof(argv[3]);
      dissThreshold.step  = (float)atof(argv[4]);
      argc-=4;
      argv+=4;      
      break;
    } 
    case 't' : {
      tranThreshold.start = tranThreshold.stop = (float)atof(argv[2]);
      tranThreshold.step = 1;
      argc-=2;
      argv+=2;      
      break;
    } 
    case 'T' : {
      tranThreshold.start = (float)atof(argv[2]);
      tranThreshold.stop  = (float)atof(argv[3]);
      tranThreshold.step  = (float)atof(argv[4]);
      argc-=4;
      argv+=4;      
      break;
    } 
    case 'n' : {
      region.start = region.stop = atoi(argv[2]);
      region.step = 1;
      argc-=2;
      argv+=2;
      break;
    } 
    case 'N' : {
      region.start = (int)atoi(argv[2]);
      region.stop  = (int)atoi(argv[3]);
      region.step  = (int)atoi(argv[4]);
      argc-=4;
      argv+=4;      
      break;
    } 
    case 'e' : {
      suppress.start = suppress.stop = atoi(argv[2]);
      suppress.step = 1;
      argc-=2;
      argv+=2;
      break;
    } 
    case 'E' : {
      suppress.start = (int)atoi(argv[2]);
      suppress.stop  = (int)atoi(argv[3]);
      suppress.step  = (int)atoi(argv[4]);
      argc-=4;
      argv+=4;      
      break;
    } 
    case 'M' : {
      mode = (int) atoi(argv[2]);
      argc-=2;
      argv+=2;
      break;
    } 
    case 'R' : {
      compareTrans = 1;
      argc-=1;
      argv+=1;
      break;
    } 
    case 'S' : {
      stepsize = (int) atoi(argv[2]);
      argc-=2;
      argv+=2;
      break;
    } 
    case 'w' : {
      noiseThreshold.start = noiseThreshold.stop = (float)atof(argv[2]);
      noiseThreshold.step = 1;
      argc-=2;
      argv+=2;      
      break;
    } 
    case 'W' : {
      noiseThreshold.start = (float)atof(argv[2]);
      noiseThreshold.stop  = (float)atof(argv[3]);
      noiseThreshold.step  = (float)atof(argv[4]);
      argc-=4;
      argv+=4;      
      break;
    } 
    case 'o' : {
      tranfile = (FILE *)fopen(argv[2], "w");
      if(tranfile == (FILE *)NULL)
	fprintf(stderr,"Error could not load output file: %s",argv[2]);
      argc-=2;
      argv+=2;
      break;
    }
    case 'c' : {
      rtranfile = (FILE *)fopen(argv[2], "w");
      if(rtranfile == (FILE *)NULL)
	fprintf(stderr,"Error could not load output file: %s",argv[2]);
      argc-=2;
      argv+=2;
      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 && compareTrans) || argc < 2 || mode == MODE_HELP){
    fprintf(stderr, usage2, exec_name, exec_name);
    exit(1);
  }
  assert(stepsize >= 1);

  if(compareTrans)
      glob_nstreams = (argc-1)/2;
  else
      glob_nstreams = (argc-1);

  namesargv = argv + 1;
  allTrans = (tranStruct **)malloc(glob_nstreams*sizeof(tranStruct *));
  allarrs = (comp **)malloc(glob_nstreams*sizeof(comp *));
  allTransL = (int *)malloc(glob_nstreams*sizeof(int));
  allarrsL = (int *)malloc(glob_nstreams*sizeof(int));
    
  for(i = 0; i < glob_nstreams; i++){
    input = (FILE *)fopen(argv[1], "r");
    if(input == (FILE *)NULL)
	fprintf(stderr,"Error could not load input file: %s",argv[1]);
    skipComments(input,'#');
    allarrsL[i] = N = initArrBinary(allarrs+i,input);
    loadArrBinary(allarrs[i],N,input);
    fclose(input);

    if(compareTrans){
      realf = (FILE *)fopen(argv[2], "r");
      if(realf == (FILE *)NULL)
	  fprintf(stderr,"Error could not load input file: %s",argv[2]);
      skipComments(realf,'#');
      allTransL[i] = M = initTrans(allTrans+i,realf);
      scanTrans(allTrans[i],M,realf);
      fclose(realf);
      argc -=2;
      argv +=2;
    }
    else{
      argc -=1;
      argv +=1;
    }
  }
  if(compareTrans){
    printThresherHeader(stdout,allarrsL,allTransL,allTrans);
    arr = catArr(allarrs, allarrsL, glob_nstreams, &glob_nframes, 
		 MAGIC_PADDING);
    reality = catTrans(allTrans, allarrsL, allTransL, glob_nstreams, 
		       &glob_ntrans, MAGIC_PADDING);
    divTrans(reality,glob_ntrans,stepsize);
  }
  else {
    arr = catArr(allarrs, allarrsL, glob_nstreams, &glob_nframes, 
		 MAGIC_PADDING);
    reality = (tranStruct *)NULL;
    glob_ntrans = 0;
  }
  
  /*  print_stats(arr,glob_nframes,stdout);*/
  
  if(mode != MODE_CATTRANS){
    if(rtranfile != (FILE *)NULL){
      printThresherHeader(rtranfile,allarrsL,allTransL,allTrans);
      fprintf(rtranfile,"# Input trans\n");
      fprintf(rtranfile,"%d\n",glob_ntrans);
      fprintTransN(rtranfile,reality,glob_ntrans);
      fclose(rtranfile);
    }
  }
  print_output(runTests(stdout,arr,glob_nframes,reality,
			glob_ntrans,tranfile),
	       stdout,mode);
  
  if(rtranfile != (FILE *)NULL){
    fclose(rtranfile);
  }
  
  if(tranfile != (FILE *)NULL){
    fclose(tranfile);
  }
  
  if(input != stdin){
    fclose(input);
  }
  free(arr);
  
  exit(1);
}  

#define PRINT_INT_AN(a) for(i = 0; i < TYPE_CLASSES; i++) fprintf(file,"%3d ",(a)[i])


void print_short_analysis(analysis val, FILE *file){
  int i,j;
  
  fprintf(file,"%2d %2d %1.3f %1.3f %1.3f ",val.supp,val.regn,val.noise,
	  val.diss,val.tran);
  fprintf(file,"|%3d|",val.nbad);
  PRINT_INT_AN(val.outOccur);
  fprintf(file,"::");
  PRINT_INT_AN(val.correct);
  fprintf(file,":");
  PRINT_INT_AN(val.fpositive);
  fprintf(file,":");
  PRINT_INT_AN(val.fnegative);
  fprintf(file,"**");
  fprintf(file,"%3d ",val.diffused);
  fprintf(file,"|");
  for(j = 0; j < TYPE_CLASSES; j++){
    PRINT_INT_AN(val.matrix[j]);
  fprintf(file,";");
  }
  fprintf(file,"\n");
}



void printThresherHeader(FILE *file, int *arrs, int *tr, tranStruct **trans){
  int x,i,j;
  int framenum = 0;
  int sum[TYPE_CLASSES];
  int tempsum[TYPE_CLASSES];

  fprintf(file, "# ");
  for(x = 1; x < origargc; x++){
    fprintf(file, "%s ",origargv[x]);
  }
  fprintf(file, "\n#\n");
  fprintf(file,"# nstreams = %d, nframes = %d, ntrans = %d \n",
	  glob_nstreams,glob_nframes, glob_ntrans);
  
  for(j = 0; j < TYPE_CLASSES; j++)
      sum[j] = 0;
  
  for(x = 0; x < glob_nstreams; x++){
    fprintf(file,"# %25s  %4d %4d %4d ",namesargv[x*2], arrs[x],
	    framenum, framenum+arrs[x]-1+MAGIC_PADDING);
    framenum+= arrs[x] + MAGIC_PADDING;
    for(j = 0; j < TYPE_CLASSES; j++)
	tempsum[j] = 0;
    for(j = 0; j < tr[x]; j++)
	(tempsum[(trans[x])[j].type])++;
    for(j = 0; j < TYPE_CLASSES; j++)
	sum[j] += tempsum[j];
    PRINT_INT_AN(tempsum);
    fprintf(file,"\n");
  }
  
  fprintf(file,"#real type = ");
  PRINT_INT_AN(sum);
  fprintf(file,"\n");
  fprintf(file,"#sup reg nois dis tran |bad| out occur  ::  correct   \
: fpositive  : fnegative  **diff|    mistake matrix \n");
}

  

analysis_set runTests(FILE *file, comp *arr, int arrsize, 
		      tranStruct *reality, int nreality,
		      FILE *tranfile){
  int supp, regn;
  float tran, diss, noise;
  analysis ans;
  analysis_set rset;
  tranStruct *result;
  int nresult,i,sum;

  ans = new_analysis();

  sum = 0;
  for(supp= suppress.start; supp <= suppress.stop; supp+=suppress.step){
    for(regn= region.start; regn <= region.stop; regn+=region.step){
      for(noise= noiseThreshold.start; noise <= noiseThreshold.stop; 
	  noise+=noiseThreshold.step){
	for(diss= dissThreshold.start; diss <= dissThreshold.stop; 
	    diss+=dissThreshold.step){
	  for(tran= tranThreshold.start; tran <= tranThreshold.stop; 
	      tran+=tranThreshold.step){
	    sum++;
	  }
	}
      }
    }
  }

  rset.data = (analysis *)malloc(sizeof(analysis)*sum);
  rset.n = sum;
  i = 0;
  
  for(supp= suppress.start; supp <= suppress.stop; supp+=suppress.step){
    for(regn= region.start; regn <= region.stop; regn+=region.step){
      for(noise= noiseThreshold.start; noise <= noiseThreshold.stop; 
	  noise+=noiseThreshold.step){
	for(diss= dissThreshold.start; diss <= dissThreshold.stop; 
	    diss+=dissThreshold.step){
	  for(tran= tranThreshold.start; tran <= tranThreshold.stop; 
	      tran+=tranThreshold.step){
	    nresult = threshArr(arr,arrsize, supp,regn,tran,
				diss,&result);
	    subdivide_dissolves(arr,arrsize,(double)noise,result,nresult);
	    find_wipes(arr,result,nresult,0.2);

	    ans = compareTrans(reality, nreality,result,nresult);

	    if(tranfile != (FILE *)NULL){
	      fprintf(tranfile,"%d\n",nresult);
	      fprintTrans(tranfile,result,nresult);
	    }

	    ans.supp = supp;
	    ans.regn = regn;
	    ans.noise = noise;
	    ans.diss = diss;
	    ans.tran = tran;
	    nbad_analysis(&ans);
	    rset.data[i++] = ans;
	  }
	}
      }
    }
  }
  return(rset);
}

void print_output(analysis_set ans, FILE *file, int mode){
  int i, min, sum, temp;

  switch(mode){
  case 1 : {
    for(i = 1, min = ans.data[0].nbad, sum = 1; i < ans.n; i++){
      temp = ans.data[i].nbad;
      if(temp < min){
	min = temp;
	sum = 1;
      }
      else if(temp == min){
	sum++;
      }
    }
    fprintf(file,"%d\n",sum);
    for(i = 0; i < ans.n ; i++){
      if(ans.data[i].nbad == min)
	  print_short_analysis(ans.data[i],file);
    }
    break;
  }
  case 0 :/* NULL*/;
  default : {
    fprintf(file,"%d\n",ans.n);
    
    for(i = 0; i < ans.n; i++){
      print_short_analysis(ans.data[i],file);
    }
  }
  }

  return;
}
