

#include <stdio.h>
#include <math.h>
#include <limits.h>

#include "clarg.h"
#include "imConv.h"
#include "image.h"
#include "misc.h"
#include "string-support.h"
#include "rgb-hsv.h"


static char *USAGE =
  "\n%s:  Convert the P*M input to the specified output type, which defaults to PGM.\n"
  "\n"
  "Arguments are:\n"
  "   -input file:          Specify the input file (PGM, PFM, PPM, PDM, PBM, PLM, PSM)\n"
  "   -output file:         Specify the output file (as above)\n"
  "\n"
  "*   -quantile [low high]: Eliminate outliers outside the quantiles low and high.\n"
  "*   -histeq  [max]:       Histogram-equalize the input before conversion.\n"
  "\n"
  "   -binary thresh:       Specify PBM output, and threshold on input.\n"
  "   -gr[ae]y [max]:       Specify PGM output, optionally give max intensity.\n"
  "   -short   [max]:       Specify PSM output, optionally give max intensity.\n"
  "   -long    [max]:       Specify PLM output, optionally give max intensity.\n"
  "   -float   [max]:       Specify PFM output, optionally give max intensity.\n"
  "   -double  [max]:       Specify PDM output, optionally give max intensity.\n"
  "   -rgb     [max]:       Specify PPM output, optionally give max intensity.\n"
  "   -rgbfloat [max]:      Specify PPFM output, optionally give max intensity.\n"
  "   -hsv    [max]:        Specify HSV output.\n"
  "\n"
  "   -zero                 0 in input maps to zero in output\n"
  "   -positive             all output image values should be positive\n"
  "   -direct               direct mapping, no scaling\n"
  "\n"
  "Doesn't handle some inputs yet.  Description to be added when I finish\n"
  "this program.  Starred options above not yet implemented.\n"
  "Some inputs cause segmentation faults.   Definitely works for\n"
  "float to grey conversion, though.\n\n";


#define INPUT 	 "i.nput"
#define OUTPUT 	 "o.utput"

#define	QUANT    "q.uantile"		     /* kill outliers */
#define HISTEQ	 "h.isteq"

#define ZERO     "z.ero"
#define POSITIVE "p.ositive"
#define DIRECT   "d.irect"

#define BINARY   "binary"
#define GRAY     "gray"
#define GREY     "grey"
#define SHORT    "short"
#define LONG     "long"
#define FLOAT    "float"
#define DOUBLE   "double"
#define RGBIM    "rgb"
#define RGBFIM   "rgbf.loat"
#define HSVIM    "hsv"


static CLINIT clinit[] = {{INPUT,  1, 1,  NULL,    0},
			  {OUTPUT, 1, 1,  NULL,    0},
			    
			  {QUANT,    0, 2, cl_real, 0}, /* low, high quantiles */
			  {HISTEQ,   0, 1, cl_real, 0}, /* max intensity */

                          {ZERO,     0, 0, NULL,    0},
                          {POSITIVE, 0, 0, NULL,    0},
                          {DIRECT,   0, 0, NULL,    0},
			    
			  {BINARY,   1, 1, cl_real, 0}, /* threshold */
			  {GRAY,     0, 1, cl_int,  0}, /* max intensity */
			  {GREY,     0, 1, cl_int,  0}, /* max intensity */
			  {SHORT,    0, 1, cl_int,  0}, /* max intensity */
			  {LONG,     0, 1, cl_int,  0}, /* max intensity */
			  {FLOAT,    0, 1, cl_real, 0}, /* max intensity */
			  {DOUBLE,   0, 1, NULL,    0}, /* max intensity */
			  {RGBIM,    0, 1, cl_int,  0}, /* max intensity */
			  {RGBFIM,   0, 1, cl_real, 0}, /* max intensity */
			  {HSVIM,    0, 0, NULL,    0},

			  {NULL,     1, 1, NULL,    0}};


static char *require[] = {GRAY"+"GREY"+"FLOAT"+"RGBIM"+"DOUBLE"+"BINARY"+"LONG"+"SHORT"+"
                                     RGBFIM"+"HSVIM" <= 1",
			  "!(UNNAMED && "INPUT")",
			  ZERO"+"POSITIVE"+"DIRECT" <= 1",
			  NULL};




static ImageType defaultOutputType(ImageType intype)
{
  switch (intype) {
  case IMAGE_GRAY:    return IMAGE_GRAY;
  case IMAGE_FLOAT:   return IMAGE_GRAY;
  case IMAGE_RGB:     return IMAGE_RGB;
  case IMAGE_DOUBLE:  return IMAGE_GRAY;
  case IMAGE_BINARY:  return IMAGE_BINARY;
  case IMAGE_LONG:    return IMAGE_GRAY;
  case IMAGE_SHORT:   return IMAGE_GRAY;
  case IMAGE_RGBF:    return IMAGE_RGB;
  case IMAGE_HSV:     return IMAGE_RGB;

  default:            return (ImageType) 0;
  }
}



int main(int argc, char **argv, char **envp)
{
  AnyImage img = (AnyImage) NULL, out = (AnyImage) NULL;
  double mxval=0.0, thresh=0.0;
  uchar mode;
  double qlow, qhigh;
  double histmax;
  ImageType type;
  boolean histeq = FALSE, quant = FALSE;
  char *infile;
  int imno;


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

  infile = (cl_opt(INPUT)
	    ? cl_arg1(INPUT)
	    : cl_opt(CL_UNNAMED) ? cl_arg1(CL_UNNAMED) : NULL);

  if (cl_opt(QUANT)) {
    quant = TRUE;
    if (cl_numarg(QUANT) == 2) {
      qlow  = atof(cl_arg1(QUANT));
      qhigh = atof(cl_arg2(QUANT));
    }
    else if (cl_numarg(QUANT) == 1) {
      qlow  = atof(cl_arg1(QUANT));
      qhigh = 1.0 - qlow;
    }
    else {
      qlow  = 0.05;
      qhigh = 0.95;
    }

    if (qlow < 0.0  ||  qhigh > 1.0  ||  qhigh <= qlow) {
      fprintf(stderr, "IMCONV: Invalid quantile parameters. 0.0 <= %f < %f <= 1.0.\n",
	      qlow, qhigh);
      return 0;
    }
  }

  if      (cl_opt(ZERO))      mode = ImConvZero;
  else if (cl_opt(POSITIVE))  mode = ImConvPositive;
  else if (cl_opt(DIRECT))    mode = ImConvDirect;
  else                        mode = ImConvNormal;
  
  if (cl_opt(HISTEQ)) {
    histeq = TRUE;
    if (cl_numarg(HISTEQ) == 1) {
      histmax = atof(cl_arg1(HISTEQ));
    }
    else {
/* NaN */      histmax = 0 /* HISTEQ_DEFAULT */ ;
    }
  }

  imno = 0;                                  /* read in image(s) */
  while ((imno == 0 || infile == NULL) &&
         (img = imLoad(IMAGE_GRAY | IMAGE_FLOAT | IMAGE_RGB  | IMAGE_DOUBLE |
                       IMAGE_BINARY | IMAGE_LONG | IMAGE_SHORT | IMAGE_RGBF |
                       IMAGE_HSV, infile))) {
    imno++;
 
    if (cl_opt(GRAY)) {                      /* figure out output type */
      type  = IMAGE_GRAY;
      mxval = cl_numarg(GRAY) ? atof(cl_arg1(GRAY)) : 255.0;
    }
    else if (cl_opt(GREY)) {                 /* figure out output type */
      type  = IMAGE_GRAY;
      mxval = cl_numarg(GREY) ? atof(cl_arg1(GREY)) : 255.0;
    }
    else if (cl_opt(FLOAT)) {
      type  = IMAGE_FLOAT;
      mxval = cl_numarg(FLOAT) ? atof(cl_arg1(FLOAT)) : 1.0;
    }
    else if (cl_opt(RGBIM)) {
      type  = IMAGE_RGB;
      mxval = cl_numarg(RGBIM) ? atof(cl_arg1(RGBIM)) : 255.0;
    }
    else if (cl_opt(DOUBLE)) {
      type  = IMAGE_DOUBLE;
      mxval = cl_numarg(DOUBLE) ? atof(cl_arg1(DOUBLE)) : 1.0;
    }
    else if (cl_opt(BINARY)) {
      type  = IMAGE_BINARY;
      thresh = atof(cl_arg1(BINARY));
    }
    else if (cl_opt(LONG)) {
      type  = IMAGE_LONG;
      mxval = cl_numarg(LONG) ? atof(cl_arg1(LONG)) : 1000.0;
    }
    else if (cl_opt(SHORT)) {
      type  = IMAGE_SHORT;
      mxval = cl_numarg(SHORT) ? atof(cl_arg1(SHORT)) : 1000.0;
    }
    else if (cl_opt(RGBFIM)) {
      type  = IMAGE_RGBF;
      mxval = cl_numarg(RGBFIM) ? atof(cl_arg1(RGBFIM)) : 1.0;
    }
    else if (cl_opt(HSVIM)) {
      type  = IMAGE_HSV;
      mxval = 1.0;
    }
    else {
      type   = defaultOutputType(imGetType(img));
      mxval  = 255.0;
      thresh = 0.0;
    }
  
  
#if 0                                        /* need to write these */
    if (quantile) {
      if (imKillOutliers(img, qlow, qhigh)) {
        fprintf(stderr, "IMCONV: Outlier elimination not defined for input image type.\n");
        goto done;
      }
    }

    if (histeq) {
      if (imHistEq(img, histmax)) {
        fprintf(stderr, "IMCONV: Histogram eq. not defined for input image type.\n");
        goto done;
      }
    }
#endif

    if ((out = imConv(img, type, mxval: mxval, thresh: thresh, mode: mode)) == imNULL) {
      fprintf(stderr, "IMCONV: Error converting input image\n");
      return 0;
    }

    imDocString(out) = strNewComment(argc, argv, envp, imDocString(img), NULLSTRING);

    if (out != (AnyImage) NULL) {
      if (imSave(out, cl_opt(OUTPUT) ? cl_arg1(OUTPUT) : NULL)) {
        fprintf(stderr, "IMCONV: Error writing output image.\n");
      }
    }

    imFree(img);
    imFree(out);
  }

  if (imno == 0) {
    fprintf(stderr, "IMCONV: Error reading input.\n");
    return 0;
  }



  done:
  if (out != (AnyImage) NULL)
    imFree(out);

  if (img != (AnyImage) NULL)
    imFree(img);
    
  return 0;
}


