/**************************************************************************
 * File: datafile.c           DATAFILE.C
 * Programmer: Justin Miller Cornell id# 356452
 * Project: Dissolve Detection
 *
 * Purpose: 
 * Limitations: 
 * This File: 
 *   - contains the heart of most datatypes, i/o routines
 *   - contains thresholding code for determining scene break location
 *
 * Last Sanity Check: Are you kidding, thresholding is extremely shakey.
 *
 * 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/datafile.c,v 1.1 1995/07/01 17:01:34 jmiller Exp jmiller $";

#include <stdio.h>
#include "datafile.h"
#include "misc.h"

/* TYPE DEFINITIONS*/

/* MAGIC NUMBERS*/
#define MIN_PERCENT 0.00 /* Minimum percentage of edge pixels for a 
			    comparison measure to be considered valid */
#define MAX_EFFECT_RADIUS 30

int initArrBinary(comp **arr, FILE *file){
  int nlines;
  int width;

  fscanf(file,"%d %d\n",&nlines,&width);
  assert(width == sizeof(comp));
  *arr = (comp *)malloc(sizeof(comp)*nlines);
  return(nlines);
}

void fprintArrBinary(FILE *file, comp *arr, int nlines){
  fwrite(arr,sizeof(comp),nlines,file);
  return;
}

void loadArrBinary(comp *arr, int n, FILE *file){
  fread(arr,sizeof(comp),n,file);
  return;
}

int compressArr(comp *arr, int n){
  int i,j;
  
  for(i = 0, j= 0; j < n; j++){
    if(arr[j].frame_num > 0){
      arr[i++] = arr[j];
    }
  }
  return(i);
}


comp *catArr(comp **Arrays, int *lArrays, int nArrays, int *nArrs, 
	      int padding){
  comp *out;
  int i,j,k,sum;
  
  for(i=0, sum=0; i < nArrays; i++){
    sum+= lArrays[i];
  }
  *nArrs = sum + (nArrays-1)*padding;
  out = (comp *)malloc(sizeof(comp)*(sum+(nArrays-1)*padding));

  for(i=0,j=0; i < nArrays; i++){
    if(i != 0){
      for(k = 0; k < padding; j++,k++)
	  out[j].frac = 0.0;  /* justin, need a zero comp*/
    }
    for(k = 0; k < lArrays[i]; j++,k++){
      out[j] = Arrays[i][k];
    }
  }
  return(out);
}


/* void calcThreshValues(comp *arr, int N, float *values, float *outvals,
 *                       int suppress, int region, float trans)
 *
 *   values = sum over the region, except for areas in which the 
 *     edge percentage is very low, here we ignore the sum when calculating
 *     the total.
 *   outvals = values when values represents a local maximum, 0 otherwise.
 */
void calcThreshValues(comp *arr, int N, float *values, float *outvals,
		      int suppress, int region, float trans){
  int i,j,k;
  float sum;
  
  for(i = 0; i < N; i++){
    sum = 0;
    if(arr[i].frac >= trans){
      for(j = -region + i; j <= region+i; j++){
	k = (j < N) ? ABS(j) : N - (1 + j % N);
	/* NOTE, don't count areas in which there were no edges 
	   when the measure was calculated*/
	sum += (arr[k].frac < 0) ? 0 : arr[k].frac; 
      }
    }
    values[i] = sum;
  }

  for(i = 0; i < N; i++){
    outvals[i] = values[i];
    for(j = -suppress + i; j <= suppress+i; j++){
      k = j < N ? ABS(j) : N - (1 + j % N);
      if(arr[k].frac > arr[i].frac && arr[i].percent > MIN_PERCENT){
	outvals[i] = 0.0;
	break;
      }
    }
  }
  return;
}


/* converts all the values for which the fraction is greater than 1, 
   indicating that there were no edges in the image to values less than
   zero.
   */
void preThreshParseArr(comp *arr, int N){
  int i;
  float prev;

  prev = arr[0].frac;
  if(arr[1].frac > 1.0 && arr[0].frac > 1.0)
      arr[0].frac = -0.001;
  for(i = 1; i < N-1; i++){
    if(arr[i].frac > 1.0 && prev > 1.0 && arr[i+1].frac > 1.0){
	prev = arr[i].frac;
        arr[i].frac = -0.001;
      }
    else
	prev = arr[i].frac;
  }
  if(prev > 1.0 && arr[N-1].frac > 1.0){
    arr[N-1].frac = -0.001;
  }
  return;
}

#define TYPE_TOGGLE_ON 1
#define TYPE_TOGGLE_OFF -1
#define TYPE_TOGGLE_NODECISION 0

int threshArr(comp *arr, int N, int suppress, int region, 
	      float trans, float diss, tranStruct **transforms){
  int i,j, maxloc;
  float *outvals,*values;
  float bestthreshMin;
  float descending;
  tranStruct *tempptr;
  int isCut, isDissolve, isFade, isWipe, isFalsePeak;

  preThreshParseArr(arr,N);

  values = (float *)malloc(2*sizeof(float)*N);
  outvals = values + N;

  calcThreshValues(arr,N,values,outvals,suppress,region,trans);

  for(maxloc=0, i=0; i < N; i++){
    if(outvals[i] > 0.0)
	maxloc++;
  }
  tempptr =  *transforms = 
      (tranStruct *)malloc(sizeof(tranStruct)*maxloc);

  isCut = isDissolve = isFade = 
      isWipe = isFalsePeak = TYPE_TOGGLE_NODECISION;
  for(i=0; i < N; i++){
    if(outvals[i] > 0.0){
      tempptr->middle = tempptr->start = tempptr->end = i;
      tempptr->strength = (arr[i].frac)/outvals[i];
      tempptr->neighbors = outvals[i];
      tempptr->type = TYPE_NOMATCH;
    
      if(tempptr->strength > diss){
	tempptr->type = TYPE_CUT;
      }
      else{
	if(arr[i].frac > 1.0)
	    tempptr->type = TYPE_FADE;
	else
	    tempptr->type = TYPE_DISS;
      }
      tempptr++;
    }
  }
  /* given an event*/
  /* we check to the left and right of it to see if it is near 
     a no-edge region.  
     -if(there is edges on both sides) then
         check the outvals ratio > trans
	 yes, TYPE_CUT
	 no, */
  /* look for no edge areas, they are either bad regions, 
       or fades*/
  /* a fade should always have a region in which the best 
     threshold is 0, an an gain event will not*/
  /*    else if(arr[i].percent == 0){
	if(i != 0 && outvals[i-1] > 0.0*/
  /* probably shouldn't happen during a dissolve, unless
     the threshold is relatively high*/
  /* at the beginning of a region with no used edges*/
  /* find the end of the region */
  /*      for(j = i+1, bestthreshMin = arr[i].bestthresh; 
	   j < N && arr[j].percent == 0 ; j++){
	  bestthreshMin = MIN(bestthreshMin,arr[j].bestthresh);
	  }
	  if(bestthreshMin == 0)
	   isDissolve = TYPE_TOGGLE_OFF;
	  else
	  isFade = TYPE_TOGGLE_OFF;*/
  /* the region had no actual edges, then we must have had a 
     transition to get to here.  It by definition is not a dissolve,
	   but could be a wipe, cut, or fade.*/
  /* the region did have edges, but they fell below the threshold.
     Therefore it is not a fade.*/  
  /* check to see if it is a FADE     */
  /* Look for fades first, because they cause problems with 
     everything else. Check to see if we have an edge->no edge
     or no edge->edge event*/
  /* First case : edge -> no edge*/
  
      /* check to see if it is a CUT*/
/* check to see if it is a DISSOLVE */

/* check to see if it is a WIPE     */

  free(values);
  return(maxloc);
  
}	





void fprintTrans(FILE *file, tranStruct *trans, int N){
  int i;

  for(i = 0; i < N; i++){
    fprintf(file, "%4d %4d %4d %2d %2d %3d %f %f\n",
	    trans[i].middle,trans[i].start,trans[i].end,
	    trans[i].type,trans[i].corrtype,
	    trans[i].corr,trans[i].strength,trans[i].neighbors);
  }
  return;
}

void fprintTransN(FILE *file, tranStruct *trans, int N){
  int i;

  for(i = 0; i < N; i++){
    fprintf(file, "%3d %4d %4d %1d \n",i,
	    trans[i].start,trans[i].end,trans[i].type);
  }
  return;
}


void scanTrans(tranStruct *trans, int N, FILE *file){
  int i;

  for(i = 0; i < N; i++){
    fscanf(file, "%d %d %d %f\n",
	   &(trans[i].start),&(trans[i].end),&(trans[i].type),
	   &(trans[i].strength));
    trans[i].middle = trans[i].start;
  }
  return;
}

int initTrans(tranStruct **trans, FILE *file){
  int nlines;

  fscanf(file,"%d\n",&nlines);
  *trans = (tranStruct *)malloc(sizeof(float)*sizeof(tranStruct)*nlines);
  return(nlines);
}

void loadTrans(tranStruct *trans, int nlines, FILE *file){
  int i;

  for(i = 0; i < nlines; i++){
    scanTrans(trans, i, file);
  }
}

void divTrans(tranStruct *trans, int nlines, int stepsize){
  int i;

  for(i = 0; i < nlines; i++){
    trans[i].middle /= stepsize;
    trans[i].start /= stepsize;
    trans[i].end /= stepsize;
  }

  return;
}

tranStruct *catTrans(tranStruct **Arrays, int *lArrays, int *dArrays,
		     int nArrays, int *nTrans, int padding){
  tranStruct *out;
  int i,j,k,sum;
  
  for(i=0, sum=0; i < nArrays; i++){
    sum+= dArrays[i];
  }
  *nTrans = sum;
  out = (tranStruct *)malloc(sizeof(tranStruct)*sum);
  for(i=0,j=0, sum=0; i < nArrays; i++){
    for(k = 0; k < dArrays[i]; j++,k++){
      out[j] = Arrays[i][k];
      out[j].middle += sum;
      out[j].start += sum;
      out[j].end += sum;
    }
    sum += lArrays[i] + padding;
  }
  return(out);
}



/* skips comments of the form where the comment delimiter is the first 
   character on the line, and the comment extends to the end of the next 
   line. 

   Returns -1 if EOF reached, 0 if the first character was not the comment
   delimiter, otherwise n, the number of comment lines skipped.
 */
int skipComments(FILE *file, char delimiter){
  int state = 0;
  int nlines = 0;
  int c;

  while(state != -1 && (c = getc(file)) != EOF){
    switch(state){
    case 0 : {
      if(c != (int)delimiter)
	  state = -1;
      else
	  state = 1;
      break;
    }
    case 1 : {
      if(c == (int)'\n'){
	state = 0;
	nlines++;
      }
      break;
    }
    }
  }
  if(c == EOF)
      nlines = -1;
  else{
    ungetc(c,file);
  }
  return(nlines);
}
      

char TYPEdiffuse[] = {1,0,0};
#define TYPE_DIFFUSE(a) (TYPEdiffuse[a] == 1)

analysis compareTrans(tranStruct *reality, int nreality,
		      tranStruct *result, int nresult){
  int i,j,k, start, end, found;
  tranStruct temp;
  analysis rval;
  
  rval = new_analysis();

  /* count the numbers of each transition type*/
  for(i = 0; i < nresult; i++){
    result[i].corr = CORR_MISSED;
    (rval.outOccur[result[i].type])++;
  }
  for(i = 0; i < nreality; i++){
    reality[i].corr = CORR_MISSED;
    (rval.inOccur[reality[i].type])++;
  }


  /* looping over the matches in reality*/
  for(i = 0, j = 0; i < nreality; i++){
    temp = reality[i];

    /* skip to the first in the interval*/
    while(j < nresult && temp.start > result[j].middle){
      result[j].corr = CORR_NONE;
      (rval.fpositive[result[j].type])++; 
      j++;
    }
    /* to far? */
    if(j >= nresult ||temp.end < result[j].middle){
      (rval.fnegative[temp.type])++; 
      continue;
    }
    /* otherwise at least one of the results is within the interval*/
    start = j++;
    for(; result[j].middle <= temp.end && j < nresult; j++)/*NULL*/;
    end = j - 1;

    if(TYPE_DIFFUSE(temp.type)){
      /* want to mark all of the reality matches*/
      for(k = start; k <= end; k++){
	result[k].corrtype = temp.type;
	result[k].corr = i;
	rval.diffused++;
      }
    }
    else{
      /* look for a match*/
      for(found = 0, k = start; k <= end; k++){
	result[k].corr = i;
	result[k].corrtype = temp.type;
	if(result[k].type == temp.type){
	  found = 1;
	}
	(rval.matrix[temp.type][result[k].type])++;
      }
      if(found){
	(rval.correct[temp.type])++;
	(rval.matrix[temp.type][temp.type])--;
      }
    }
  }
  for(; j < nresult; j++){
    (rval.fpositive[result[j].type])++;
  }
  return(rval);
}  

    
analysis new_analysis(void){
  analysis rval;
  int i;

  rval.nstreams = rval.nframes = rval.diffused = rval.nbad = 0;
  for(i = 0; i < TYPE_CLASSES; i++){
    rval.inOccur[i] = rval.outOccur[i] = rval.fpositive[i] = 
	rval.fnegative[i] = rval.correct[i] = 0;
  }
  for(i = 0; i < TYPE_CLASSES*TYPE_CLASSES; i++){
    rval.matrix[0][i] = 0;
  }
  return(rval);
}

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


void print_analysis(analysis val, FILE *file){
  int i,j;

  fprintf(file,"# nstreams = %d, nframes = %d\n", val.nstreams,
	  val.nframes);
  fprintf(file,"# diffuse type = ");
  for(i = 0; i < TYPE_CLASSES; i++){
    if(TYPE_DIFFUSE(i))
	fprintf(file,"y ");
    else
	fprintf(file,"n ");
  }
  fprintf(file,"\ndiffused %d",val.diffused);
  fprintf(file,"\nreal occur : ");
  PRINT_INT_AN(val.inOccur);
  fprintf(file,"\nalg  occur : ");
  PRINT_INT_AN(val.outOccur);
  fprintf(file,"\ncorrect    : ");
  PRINT_INT_AN(val.correct);
  fprintf(file,"\nfpostive   : ");
  PRINT_INT_AN(val.fpositive);
  fprintf(file,"\nfnegative  : ");
  PRINT_INT_AN(val.fnegative);
  fprintf(file,"\n");
  for(j = 0; j < TYPE_CLASSES; j++){
    PRINT_INT_AN(val.matrix[j]);
    fprintf(file,"\n");
  }
  fprintf(file,"\n");
}

#define SUM_INT(a) for(i = 0; i < TYPE_CLASSES; i++) sum += (a)[i]

int nbad_analysis(analysis *valp){
  int i,j,sum;

  sum = 0;
  SUM_INT(valp->fpositive);
  SUM_INT(valp->fnegative);
  for(j = 0; j < TYPE_CLASSES; j++){
    SUM_INT(valp->matrix[j]);
  }
/*  sum += valp->diffused;
  valp->nbad = sum;*/

  valp->nbad = sum;
  sum += valp->diffused;

  SUM_INT(valp->correct);
  sum = -sum;
  SUM_INT(valp->outOccur);
  SUM_INT(valp->fnegative);
  assert(sum == 0);

  sum = 0;
  SUM_INT(valp->inOccur);
  sum = -sum;
  SUM_INT(valp->correct);
  SUM_INT(valp->fnegative);
  if(sum != 0){
    sum=0;
  }

  assert(sum == 0);

  return(sum);
}





void find_wipes(comp *arr, tranStruct *results, int nresults, 
		double wipeThresh){    
  tranStruct *curTranPtr;
  int i,j, stopAt, startAt, length;
  unsigned char wipeFound;
  float sumHist;
  
  for(curTranPtr = results; curTranPtr < results + nresults ; 
      curTranPtr++){
    if(curTranPtr->type == TYPE_CUT)
	continue;
    wipeFound = 0;
    /* for each point over the region, skip ahead to first zero crossing*/
    for(i = startAt = curTranPtr->start, stopAt = curTranPtr->end, 
	length = stopAt-i;
	i < stopAt && !wipeFound ; i++){
      /* Vertical down*/
      if(arr[i].histVert >= 0.5 && arr[i+1].histVert <= 0.5){
	sumHist = 0;
	for(j = startAt; j <= i; j++)
	    sumHist += 1.0 - arr[j].histVert;
	for(; j <= stopAt; j++)
	    sumHist += arr[j].histVert;
	if((sumHist / length) < wipeThresh)
	    wipeFound = 1;
      }
      /* Vertical up*/
      if(arr[i].histVert <= 0.5 && arr[i+1].histVert >= 0.5){
	sumHist = 0;
	for(j = startAt; j <= i; j++)
	    sumHist += arr[j].histVert;
	for(; j <= stopAt; j++)
	    sumHist += 1.0-arr[j].histVert;
	if((sumHist / length) < wipeThresh)
	    wipeFound = 1;
      }
      /* HORIZONTAL*/
      if(arr[i].histHorz >= 0.5 && arr[i+1].histHorz <= 0.5){
	sumHist = 0;
	for(j = startAt; j <= i; j++)
	    sumHist += 1.0 - arr[j].histHorz;
	for(; j <= stopAt; j++)
	    sumHist += arr[j].histHorz;
	if((sumHist / length) < wipeThresh)
	    wipeFound = 1;
      }
      if(arr[i].histHorz <= 0.5 && arr[i+1].histHorz >= 0.5){
	sumHist = 0;
	for(j = startAt; j <= i; j++)
	    sumHist += arr[j].histHorz;
	for(; j <= stopAt; j++)
	    sumHist += 1.0-arr[j].histHorz;
	if((sumHist / length) < wipeThresh)
	    wipeFound = 1;
      }
    }
    if(wipeFound)
	curTranPtr->type = TYPE_WIPE;
  }
  return;
}




void subdivide_dissolves(comp *arr,int arrsize, double noise,
			 tranStruct *results, int nresults){
  int absStop, middle;
  int i,j;
  int prevBar, nextBar;
  tranStruct *curTranPtr;
  
  prevBar = -1;
  for(i = 0,curTranPtr = results; i < nresults; i++, curTranPtr++){
    nextBar = (i == nresults - 1) ? arrsize : curTranPtr[1].middle;
    middle = curTranPtr->middle;

    /* adjust for max effect duration */
    prevBar = MAX(prevBar, middle-MAX_EFFECT_RADIUS);
    nextBar = MIN(nextBar, middle+MAX_EFFECT_RADIUS);

    /* backup till we find the start */
    for(j = middle; (j > prevBar) && (arr[j].frac > noise) ; j--)/*NULL*/;
    curTranPtr->start = ++j;
    for(j = middle; (j < nextBar) && (arr[j].frac > noise) ; j++)/*NULL*/;
    curTranPtr->end = --j;
    prevBar = middle;
  }

/* What was I doing??  fprintf(stderr, "JUSTIN %d %d %d\n",prevBar, nextBar, middle);*/
  return;
}






/* should not be used in the future*/
void fprintArrEntry(FILE *file,comp *arr, int n){
  fprintf(file,
	  "(%4d:%f,%f,%f,%f,%f,%f,%f,%f,%f,%f,%f,%f,%f,%f,%f,%f,%f,%f,%f,%f)\n",
#ifndef NO_FRAME_NUMS
	  arr[n].frame_num,
#endif
#ifdef NO_FRAME_NUMS
	  (int)0,
#endif
	  arr[n].frac,
	  arr[n].forward,
	  arr[n].reverse,
	  arr[n].max_forward,
	  arr[n].max_reverse,
	  arr[n].percent,
	  arr[n].threshold,
	  arr[n].bestthresh,
	  arr[n].x,
	  arr[n].y,
	  arr[n].histHorz,
	  arr[n].histVert,
#ifndef NO_CENSUS
	  arr[n].census_first,
	  arr[n].census_fx,
	  arr[n].census_fy,
	  arr[n].census_second,
	  arr[n].census_sx,
	  arr[n].census_sy,
#endif
#ifdef NO_CENSUS
	  0,0,0,0,0,0,
#endif
#ifndef NO_RESOLVE_EDGES
	  arr[n].resolveFAvg,
	  arr[n].resolveRAvg
#endif
#ifdef NO_RESOLVE_EDGES
	  0,0
#endif
	  );
  return;
}

/* should not be used in the future*/
void fprintArr(FILE *file, comp *arr, int nlines){
  int i;

  for(i = 0; i < nlines; i++){
    fprintArrEntry(file,arr, i);
  }
  return;
}

/* should not be used in the future*/
void scanArr(comp *arr, int n, FILE *file){
  fscanf(file,"(%f,%f,%f,%f,%f,%f,%f,%f,%f,%f,%f,%f)\n",
	 &(arr[n].frac),
	 &(arr[n].forward),
	 &(arr[n].reverse),
	 &(arr[n].max_forward),
	 &(arr[n].max_reverse),
	 &(arr[n].percent),
	 &(arr[n].threshold),
	 &(arr[n].bestthresh),
	 &(arr[n].x),
	 &(arr[n].y),
	 &(arr[n].histHorz),
	 &(arr[n].histVert));
  return;
}

/* should not be used in the future*/
int initArr(comp **arr, FILE *file){
  int nlines;
  int width;

  fscanf(file,"%d %d\n",&nlines,&width);
  assert(width == sizeof(comp));
  *arr = (comp *)malloc(sizeof(comp)*nlines);
  return(nlines);
}

/* should not be used in the future*/
void loadArr(comp *arr, int nlines, FILE *file){
  int i;

  for(i = 0; i < nlines; i++){
    scanArr(arr, i, file);
  }
}



