#include <config.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>

#include "OutputOptions.h"
#include "GFilter.h"

/* Class used to handle processing of command line parameters passed to
 * kdc2tiff and kdc2jpeg.
 * 
 * Written by:  Chris Studholme
 * Last Update: 6-Dec-2003
 * Copyright:   GPL (http://www.fsf.org/copyleft/gpl.html)
 */


void OutputOptions::SetOutputParameters() {

  image=srcimage;

  // contrast enhancement
  int min=0;
  int max=255;
  if (contrastenhance) {
    // find best min and max
    srcimage->FindBestMinMax(min,max);
    min-=contrastenhance;
    max+=contrastenhance;
    if (min<0) min=0;
    if (max>255) max=255;
    
    if ((min>0)||(max<255)) {
      fprintf(stderr,"Adjusting constrast (%d,%d) -> (0,255).\n",min,max);
      GFilter_ContrastAdjustment* ca = 
	new GFilter_ContrastAdjustment(*image,(min-128)*128,(max+1-128)*128);
      image=ca;
    }
  }

  // white balance
  if (lightwhite) {
    float red=1;
    float blue=1;
    srcimage->FindWhiteBalance(red,blue,min);
    fprintf(stderr,"Light source (%.2f,1.00,%.2f).\n",red,blue);

    // preferred light source
    //    red *= 1.0169;
    //    blue *= 1.003;
    
    // check for sane values
    if (red>1.4) red=1.4;
    if (red<0.7) red=0.7;
    if (blue>1.4) blue=1.4;
    if (blue<0.7) blue=0.7;

    // don't balance images that undergo insane contrast enhancement
    if (max-min<150) {
      float factor = ((float)max-min)/150;
      red = 1 + factor*(red-1);
      blue = 1 + factor*(blue-1);
    }

    if ((red<0.995)||(red>1.005)||(blue<0.995)||(blue>1.005)) {
      fprintf(stderr,"Balancing white (%.2f,1.00,%.2f).\n",red,blue);
      GFilter_WhiteBalance* wb = 
	new GFilter_WhiteBalance(*image,1/red,1/blue);
      image=wb;
    }
  }

  // gamma correction
  if (gammacorrection!=1) {
    fprintf(stderr,"Applying gamma correction of %.2f.\n",gammacorrection);
    GFilter_GammaCorrection* gc = 
      new GFilter_GammaCorrection(*image,gammacorrection);
    image=gc;
  }

  // rotate image
  if (rotate) {
    GFilter_Rotate* rot = new GFilter_Rotate(*image);
    switch (rotate) {
    case 1:
      rot->setClockwiseRotate();
      fprintf(stderr,"Rotating image clockwise.\n");
      break;
    case -1:
      rot->setCounterClockwiseRotate();
      fprintf(stderr,"Rotating image counter-clockwise.\n");
      break;
    default:
      fprintf(stderr,"invalid rotate value (%d)\n", rotate);
    }
    image=rot;
  }

  // default image dimensions
  if ((outputwidth==0)&&(outputheight==0)) {
    outputwidth=image->getPreferredWidth();
    outputheight=image->getPreferredHeight();
  }

  // scale image
  GFilter_Scale* scale = new GFilter_Scale(*image,outputwidth,outputheight);
  if ((outputwidth>0)&&(outputheight>0)) {
    if (!squarepixels)
      scale->setDimensions(outputwidth,outputheight,false);
    else if (nocrop)
      scale->setMaximumDimensions(outputwidth,outputheight);
  }
  image=scale;
  outputwidth=image->getWidth();
  outputheight=image->getHeight();

  fprintf(stderr,"Scaling image to %dx%d%s",outputwidth,outputheight, 
	  squarepixels?"":" (non-square pixels)");
  int uncropwidth=scale->getUncropWidth();
  int uncropheight=scale->getUncropHeight();
  if ((uncropwidth!=outputwidth)||(uncropheight!=outputheight))
    fprintf(stderr," cropping from %dx%d",uncropwidth,uncropheight);
  fprintf(stderr,".\n");
}

void OutputOptions::SetOutputFilename() {
  if (outputname==0) {
    outputname = new char[strlen(inputname)+5];
    strcpy(outputname,inputname);
    char* extension = strrchr(outputname,'.');
    if ((extension!=0)&&(extension>strrchr(outputname,'/')))
      strcpy(extension, outputtiff?".tif":".jpg");
    else
      strcat(outputname,outputtiff?".tif":".jpg");
  }
}

void OutputOptions::CalculateScanLine(unsigned char* dest, int y) {
  int w=image->getWidth();
  short r[w],g[w],b[w];
  image->getScanLineHorz(r,g,b,0,y,w,y+1,w);
  for (int x=0; x<w; ++x) {
    dest[x*3+0] = r[x]<-16383 ? 0 : r[x]>16383 ? 255 : 128+r[x]/128;
    dest[x*3+1] = g[x]<-16383 ? 0 : g[x]>16383 ? 255 : 128+g[x]/128;
    dest[x*3+2] = b[x]<-16383 ? 0 : b[x]>16383 ? 255 : 128+b[x]/128;
  }
}


// returns 0 if error
char** OutputOptions::ParseOptions(int& argc, char*argv[]) {
  
  while (argc>0) {
    
    if (argv[0][0]=='-') {
      
      // stdin or stdout
      if (strcmp(argv[0],"-")==0)
	return argv;
      
      // next argument is a filename
      if (strcmp(argv[0],"--")==0) {
	--argc;
	return ++argv;
      }
      
      const char* switches = argv[0]+1;
      while (switches[0]) {
	switch (switches[0]) {
	  
	  // copyright
	case 'C':
	  ++argv;
	  if (--argc>0)
	    copyright=argv[0];
	  else
	    return 0;
	  break;
	  
	  // compression scheme
	case 'c':
	  ++argv;
	  if (--argc>0)
	    compresstype=argv[0];
	  else
	    return 0;
	  break;

	  // output filename
	case 'o':
	  ++argv;
	  if (--argc>0) {
	    outputname=argv[0];
	    overwrite=true;
	  }
	  else
	    return 0;
	  break;
	  
	  // rows per strip
	case 'r':
	  ++argv;
	  if (--argc>0)
	    rowsperstrip=atoi(argv[0]);
	  else
	    return 0;
	  if (rowsperstrip<=0) {
	    fprintf(stderr,"invalid rows per strip value (%d)\n",rowsperstrip);
	    return 0;
	  }
	  break;
	  
	  // don't create progressive jpeg
	case 'P':
	  progressive=false;
	  break;
	  
	  // jpeg quality setting
	case 'q':
	  ++argv;
	  if (--argc>0)
	    quality=atoi(argv[0]);
	  else 
	    return 0;
	  if ((quality<0)||(quality>100)) {
	    fprintf(stderr,"invalid quality value (%d)\n",quality);
	    return 0;
	  }
	  break;
	  
	  // width
	case 'w':
	  ++argv;
	  if (--argc>0)
	    outputwidth=atoi(argv[0]);
	  else {
	    return 0;
	  }
	  if (outputwidth<=0) {
	    fprintf(stderr,"invalid image width (%d)\n",outputwidth);
	    return 0;
	  }
	  break;
	  
	  // height
	case 'h':
	  ++argv;
	  if (--argc>0)
	    outputheight=atoi(argv[0]);
	  else 
	    return 0;
	  if (outputheight<=0) {
	    fprintf(stderr,"invalid image height (%d)\n",outputheight);
	    return 0;
	  }
	  break;
	  
	  // gamma correction
	case 'g':
	  ++argv;
	  if (--argc>0)
	    gammacorrection=atof(argv[0]);
	  else 
	    return 0;
	  if ((gammacorrection<0.2)||(gammacorrection>5)) {
	    fprintf(stderr,"invalid gamma setting (%.1f)\n",gammacorrection);
	    return 0;
	  }
	  break;

	  // no grain reduction
	case 'G':
	  grainreduction=0;
	  break;
	  
	  // non-square pixels
	case 's':
	  squarepixels=false;
	  break;
	  
	  // non-fast de-bayer (highest quality)
	case 'f':
	  fastdebayer=false;
	  break;
	  
	  // no contrast enhancement
	case 'e':
	  contrastenhance=0;
	  break;
	  
	  // no cropping
	case 'p':
	  nocrop=true;
	  break;
	  
	  // don't rotate
	case 'R':
	  rotate=0;
	  break;

	  // colour of light source (white)
	case 'W':
	  ++argv;
	  if (--argc>0) {
	    if (strcasecmp(argv[0],"none")==0)
	      lightwhite=0;
	    else if (strcasecmp(argv[0],"auto")==0)
	      lightwhite=-1;
	    else if (strcasecmp(argv[0],"sun")==0)
	      lightwhite=-1;
	    else if (strcasecmp(argv[0],"shade")==0)
	      lightwhite=-1;
	    else if (strcasecmp(argv[0],"cloud")==0)
	      lightwhite=-1;
	    else if (strcasecmp(argv[0],"incandecent")==0)
	      lightwhite=-1;
	    else if (strcasecmp(argv[0],"flourescent")==0)
	      lightwhite=-1;
	    else {
	      lightwhite=atoi(argv[0]);
	      if ((lightwhite<500)||(lightwhite>15000)) {
		fprintf(stderr,"invalid light source temperature, assuming ttl mode\n");
		lightwhite=-1;
	      }
	    }
	  }
	  else
	    return 0;
	  break;
	  
	default:
	  return 0;
	}
	++switches;
      }
    }
    
    else if (argv[0][0]=='+') {
      const char* switches = argv[0]+1;
      float tempf;
      while (switches[0]) {
	switch (switches[0]) {
	  
	  // create progressive jpeg
	case 'P':
	  progressive=true;
	  break;
	  
	  // square pixels
	case 's':
	  squarepixels=true;
	  break;
	  
	  // fast de-bayer (sacrifice quality)
	case 'f':
	  fastdebayer=true;
	  break;
	  
	  // gamma correction
	case 'g':
	  ++argv;
	  if (--argc>0)
	    tempf=atof(argv[0]);
	  else 
	    return 0;
	  if ((tempf<0.2)||(tempf>5)) {
	    fprintf(stderr,"invalid gamma setting (%.1f)\n",tempf);
	    return 0;
	  }
	  else
	    gammacorrection*=tempf;
	  break;

	  // grain reduction
	case 'G':
	  if (++grainreduction>2)
	    grainreduction=2;
	  break;
	  
	  // normal contrast enhancement
	case 'e':
	  contrastenhance=2;
	  break;

	case 'E':
	  ++argv;
	  if (--argc>0)
	    contrastenhance=atoi(argv[0]);
	  else 
	    return 0;
	  if ((contrastenhance<=0)||(contrastenhance>255)) {
	    fprintf(stderr,"invalid contrast enhancement (%d)\n",
		    contrastenhance);
	    return 0;
	  }
	  break;
	  
	  // crop if necessary
	case 'p':
	  nocrop=false;
	  break;

	  // rotate clockwise
	case 'R':
	  rotate=1;
	  break;

	  // rotate counter-clockwise
	case 'B':
	  rotate=-1;
	  break;

	default:
	  return 0;
	}
	++switches;
      }
    }

    // must be a filename
    else
      return argv;

    ++argv;
    --argc;
  }

  return argv;
}
