/*******************************************************************************
 *                                                                             *
 *  Filename : pic_io.c                                                        *
 *  Author   : Bruno Grossniklaus                                              *
 *  Created  : 12.4.91                                                         *
 *  Modified :                                                                 *
 *             20.6.91 Gro: filling rayshade                                   *
 *                          fast reading for rayshade                          *
 *                          no default extension for input file                *
 *                                                                             *
 ******************************************************************************/

#include <stdio.h>
/* This version is for use with XView only! */
#include <pixrect/pixrect_hs.h>
#include "defaults.h"


/* functions to make programs shorter */
/***************************************
void     set_file_names();
void     close_success();
void     close_error();
int      open_in_file();
int      open_out_file();
int      get_image ();
int      get_mem ();
int      put_image ();
void     print_euclidean_dist();
void     write_map();
int read_resolution (int *X, int *Y, FILE *infile);
****************************************/

/*******************************************************************************
 *   set_file_names                                                            *
 *                                                                             *
 *   Input  : - fn_in   input filename                                         *
 *            - fn_out  output filename                                        *
 *                                                                             *
 *   Output : - fn_in   input filename                                         *
 *            - fn_out  output filename                                        *
 *                                                                             *
 ******************************************************************************/
void set_file_names (fn_in,fn_out)
char fn_in[FNS];   /* input filename        */
char fn_out[FNS];  /* output filename       */

{
  int           with_ext;     /* flag to put extension */
  register int  i;            /* used for loops        */
  int           j;            /* used in loops         */

  /* scan filename if there is an extension                   */
  /* ../dir/picture.pixrect is a name with extension          */
  /* ../dir/image has no extension, so take default extension */
  with_ext = 0;

  for (i = 0; i < strlen (fn_in); i++) {
    if (fn_in[i] == '.') with_ext = 1;
    if (fn_in[i] == '/') with_ext = 0;
  }

  /* if no extension add default extension */

/*if (!with_ext) strcat (fn_in,DEFAULT_IN_EXT); */

  /* now the same for output filename                             */
  /* if no output filename is given take input file name */
  if (strlen (fn_out) == 0) {

    with_ext = 0;
    j = 0;

    for (i = 0; i < strlen (fn_in); i++) {
      if (fn_in[i] == '/' ) { j = -1; with_ext = 0; }
      if (fn_in[i] == '.' ) with_ext = 1;
      if ( (j > -1) && (!with_ext) ) fn_out[j] = fn_in[i];
      if (!with_ext) j++;
    }

    fn_out[j] = '\0';
    strcat (fn_out,DEFAULT_OUT_EXT);
    fprintf (stderr,"There is no output file specified. So i take %s\n",fn_out);
  }

  else {

    if ( strcmp (fn_out,DEFAULT_STDOUT) != 0) {
      /* if there is no extension for output file add default extension */

      with_ext = 0;

      for (i = 0; i < strlen (fn_out); i++) {
        if (fn_out[i] == '.') with_ext = 1;
        if (fn_out[i] == '/') with_ext = 0;
      }

      if (!with_ext) strcat (fn_out,DEFAULT_OUT_EXT);
    }
  }
}


/*******************************************************************************
 *   close_success                                                             *
 *                                                                             *
 *   Input  : - fp_in        input file pointer                                *
 *            - fp_out       output file pointer                               *
 *            - mem_in       pixrect in memory 24 or 32 Bit                    *
 *            - mem_8_out    pixrect in memory 8 Bit                           *
 *                                                                             *
 *   close open files and free allocated memory.                               *
 *                                                                             *
 ******************************************************************************/
void close_success (fp_in,fp_out,mem_in,mem_8_out)
FILE       *fp_in;       /* input file pointer             */
FILE       *fp_out;      /* output file pointer            */
Pixrect    *mem_in;      /* pixrect in memory 24 or 32 Bit */
Pixrect    *mem_8_out;   /* pixrect in memory 8 Bit        */

{
  fprintf (stderr,"Success \n");
  fclose (fp_in);
  fclose (fp_out);
  if (mem_in    != NULL) pr_close (mem_in);
  if (mem_8_out != NULL) pr_close (mem_8_out);
}


/*******************************************************************************
 *   close_error                                                               *
 *                                                                             *
 *   Input  : - fp_in        input file pointer                                *
 *            - fp_out       output file pointer                               *
 *            - mem_in       pixrect in memory 24 or 32 Bit                    *
 *            - mem_8_out    pixrect in memory 8 Bit                           *
 *                                                                             *
 *   close open files and free allocated memory.                               *
 *                                                                             *
 ******************************************************************************/
void close_error (fp_in,fp_out,mem_in,mem_8_out)
FILE       *fp_in;       /* input file pointer             */
FILE       *fp_out;      /* output file pointer            */
Pixrect    *mem_in;      /* pixrect in memory 24 or 32 Bit */
Pixrect    *mem_8_out;   /* pixrect in memory 8 Bit        */

{
 fprintf (stderr,"\nClosing all. No good output because of error! \n");
  fclose (fp_in);
  fclose (fp_out);
  if (mem_in    != NULL) pr_close (mem_in);
  if (mem_8_out != NULL) pr_close (mem_8_out);
}


/*******************************************************************************
 *   open_in_file                                                              *
 *                                                                             *
 *   Input  : - fn_in     input filename                                       *
 *            - fp_in     input file pointer                                   *
 *            - fp_out    output file pointer                                  *
 *            - mem_in    pixrect in memory 24 or 32 Bit                       *
 *            - mem_8_out pixrect in memory 8 Bit                              *
 *                                                                             *
 *   Output : - fp_in     input file pointer                                   *
 *                                                                             *
 *   Return : - NULL if ok, 1 on error.                                        *
 *                                                                             *
 ******************************************************************************/
int open_in_file (fn_in,fp_in,fp_out,mem_in,mem_8_out)
char    fn_in[FNS];   /* input filename                 */
FILE    **fp_in;      /* input file pointer             */
FILE    *fp_out;      /* output file pointer            */
Pixrect *mem_in;      /* pixrect in memory 24 or 32 Bit */
Pixrect *mem_8_out;   /* pixrect in memory 8 Bit        */

{
  if ( (*fp_in = fopen (fn_in,"r")) == NULL ) {
    fprintf (stderr,"Error opening file %s \n",fn_in);
    fprintf (stderr," -maybe no path specified but file not in current dir.\n");
    fprintf (stderr," -maybe wrong path specified.\n");
    fprintf (stderr," -maybe file does not exists.\n");
    fprintf (stderr," -maybe file is defective.\n");
    fprintf (stderr," -> use Esc function of SUNVIEW to complete filename!\n");
    close_error (*fp_in,fp_out,mem_in,mem_8_out);
    return (1);
  }
  else return (NULL);
}


/*******************************************************************************
 *   open_out_file                                                             *
 *                                                                             *
 *   Input  : - fn_out    output filename                                      *
 *            - fp_in     input file pointer                                   *
 *            - fp_out    output file pointer                                  *
 *            - mem_in    pixrect in memory 24 or 32 Bit                       *
 *            - mem_8_out pixrect in memory 8 Bit                              *
 *                                                                             *
 *   Output : - fp_out    output file pointer                                  *
 *                                                                             *
 *   Return : - NULL if ok, 1 on error.                                        *
 *                                                                             *
 ******************************************************************************/
int open_out_file (fn_out,fp_in,fp_out,mem_in,mem_8_out)
char    fn_out[FNS];  /* output filename                */
FILE    *fp_in;       /* input file pointer             */
FILE    **fp_out;     /* output file pointer            */
Pixrect *mem_in;      /* pixrect in memory 24 or 32 Bit */
Pixrect *mem_8_out;   /* pixrect in memory 8 Bit        */

{
  if (strcmp (fn_out,DEFAULT_STDOUT) == 0) {
    /* set pointer to stdout */
    *fp_out = stdout;
    fprintf (stderr,"I`ll write image to stdout. \n");
    return (NULL);
  }

  else {

    /* open for reading to see if output file already exists */
    if ( (*fp_out = fopen (fn_out,"r")) != NULL ) {
      fprintf (stderr,"Output file %s already exists. ",fn_out);
      fprintf (stderr,"Overwrite (y/n) : ");
      if ('y' != getchar()) {
        close_error (fp_in,*fp_out,mem_in,mem_8_out);
        return (1);
      }
      fprintf (stderr,"\n");
    }
    else fclose (*fp_out);

    /* now open for writing ... */
    if ( (*fp_out = fopen (fn_out,"w")) == NULL ) {
      fprintf (stderr,"Error creating file %s \n",fn_out);
      fprintf (stderr," -maybe there are forbidden characters in the ");
      fprintf (stderr,"filename.\n");
      fprintf (stderr," -maybe over disk quota.\n");
      fprintf (stderr," -maybe disk is full.\n");
      fprintf (stderr," -maybe server is defective.\n");
      close_error (fp_in,*fp_out,mem_in,mem_8_out);
      return (1);
    }
    else return (NULL);
  }
}


/*******************************************************************************
 *   get_image                                                                 *
 *                                                                             *
 *   Input  : - fn_in        input filename                                    *
 *            - fp_in        input file pointer                                *
 *            - fp_out       output file pointer                               *
 *            - mem_in       pixrect in memory 24 or 32 Bit                    *
 *            - mem_8_out    pixrect in memory 8 Bit                           *
 *            - flag_prompt  flag if prompt or not                             *
 *            - x            width of image                                    *
 *            - y            heigth of image                                   *
 *            - depth        depth of image                                    *
 *                                                                             *
 *   Output : - x            width of image                                    *
 *            - y            heigth of image                                   *
 *            - depth        depth of image                                    *
 *            - mem_in       points to image data                              *
 *                                                                             *
 *   Return : - NULL if ok, 1 on error.                                        *
 *                                                                             *
 ******************************************************************************/
int get_image (fn_in,fp_in,fp_out,mem_in,mem_8_out,flag_prompt,x,y,depth)
char          fn_in[FNS];   /* input filename                 */
FILE          *fp_in;       /* input file pointer             */
FILE          *fp_out;      /* output file pointer            */
Pixrect       **mem_in;     /* pixrect in memory 24 or 32 Bit */
Pixrect       *mem_8_out;   /* pixrect in memory 8 Bit        */
unsigned char flag_prompt;  /* flag if prompt                 */
int           *x,*y,*depth; /* size and depth of image        */

{
  struct rasterfile  rh;        /* header of input image       */
  int                *ray_ptr;  /* pointer for rayshade image  */
  unsigned char      *buffer;   /* buffer for reading rayshade */
  int                i,j,k;     /* used for loops              */

  /* get the header of the input file */
  if ( pr_load_header (fp_in,&rh) == PIX_ERR ) {

    /* try to load rayshade image */
    fp_in = fopen (fn_in,"r");

/* with some rayshade files this does not work */
/*    if (fscanf(fp_in, "%d%d\n", x, y) == 2) */

      /* read the x and y values */
      if (read_resolution (x, y, fp_in))
      {

	fprintf (stderr,"Rayshade image  x=%d  y=%d \n",*x,*y);
	*depth = 24;

	/* get memory for image */
	if ( (*mem_in = mem_create (*x,*y,32)) == NULL) 
	{
	  fprintf (stderr,"Error in mem_create(). Not enough memory\n");
	  close_error (fp_in,fp_out,*mem_in,mem_8_out);
	  return (1);
	}

	/* now read rayshade image */
	if (flag_prompt) fprintf (stderr,"Reading %s ... ",fn_in);

	ray_ptr = (int *) mpr_d(*mem_in)->md_image;

        /* get memory for buffer */
        if ( (buffer = (unsigned char*) 
                       malloc ( 3 *(*x) * sizeof (unsigned char) )) == NULL) {
 	  fprintf (stderr,"Error in malloc(buffer). Not enough memory\n");       
	  close_error (fp_in,fp_out,*mem_in,mem_8_out);
          return (1);
	}

        /* read image in rows */
	for (i = 0; i < (*y); i++) {
	  if (!fread ( (char *) buffer, 3, (*x), fp_in)) break;
          for (j = 0; j < (*x); j++, ray_ptr++) {
	    *ray_ptr = (int) ((buffer[j*3+2] << 16)+(buffer[j*3+1]<<8)+buffer[j*3+0]);
	  }
	}
        free ( (char *) buffer);

	/* test if there was any error */
	if (i != (*y) ) {
          if (i < (*y) ) {
 
            /* rayshade image was not finished. So fill it up */
            if (flag_prompt) {
              fprintf (stderr,"done.\n");
              fprintf (stderr,"There are %d lines of %d finished.",i-1,(*y));
              fprintf (stderr," Filling the rest ... ");
	    }
            for (j=i; j < (*y); j++) 
              for (k = 0; k < (*x); k++, ray_ptr++) {
                *ray_ptr = (int) ((FILL_B << 16) + (FILL_G << 8) + (FILL_R));
	      }
	  }

          /* more Pixels than x*y */
          else {
	    fprintf (stderr,"\nIllegal formated. There are more Pixels than x*y.\n");
	    close_error (fp_in,fp_out,*mem_in,mem_8_out);
	    return (1);
	  }
	}

      if (flag_prompt) fprintf (stderr,"done. \n");
      return (NULL);
      }
  
    fprintf (stderr,"Error loading header of %s \n",fn_in);
    fprintf (stderr," -maybe this is no pixrect or rayshade file.\n");
    fprintf (stderr," -maybe file is defective.\n");
    fprintf (stderr," -maybe server is defective.\n");
    close_error (fp_in,fp_out,*mem_in,mem_8_out);
    return (1);
  }

  /* set values of return variables */
  *x = rh.ras_width; *y = rh.ras_height; *depth = rh.ras_depth;

  if (flag_prompt)
    fprintf (stderr,"Data of image : x=%d  y=%d  depth=%d Bit \n",*x,*y,*depth);


  /* check if there is no colormap for the input image */
  if (rh.ras_maptype != RMT_NONE) {
    if (rh.ras_maptype == RMT_EQUAL_RGB)
      fprintf (stderr,"Image %s already has RGB colormap. \n",fn_in);
    if (rh.ras_maptype == RMT_RAW)
      fprintf (stderr,"Image %s already has RAW colormap. \n",fn_in);
    close_error (fp_in,fp_out,*mem_in,mem_8_out);
    return (1);
  }

  /* check if depth of input image is 24 or 32 */
  if ( (*depth != 32) && (*depth != 24) ) {
    fprintf (stderr,"Depth of image is not 32 or 24 but %d \n",*depth);
    close_error (fp_in,fp_out,*mem_in,mem_8_out);
    return (1);
  }

  /* now load the image */
  if (flag_prompt) fprintf (stderr,"Reading %s ... ",fn_in);
  if ( (*mem_in = pr_load_image (fp_in, &rh, NULL)) == NULL ) {
    fprintf (stderr,"\nError loading image %s \n",fn_in);
    fprintf (stderr," -maybe not enough memory.\n");
    fprintf (stderr," -maybe file is defective.\n");
    fprintf (stderr," -maybe server is defective.\n");
    close_error (fp_in,fp_out,*mem_in,mem_8_out);
    return (1);
  }
  if (flag_prompt) fprintf (stderr,"done. \n");
  return (NULL);
}


/*******************************************************************************
 *   get_mem                                                                   *
 *                                                                             *
 *   Input  : - fp_in        input file pointer                                *
 *            - fp_out       output file pointer                               *
 *            - mem_in       pixrect in memory 24 or 32 Bit                    *
 *            - mem_8_out    pixrect in memory 8 Bit                           *
 *            - flag_prompt  flag if prompt or not                             *
 *            - x            width of image                                    *
 *            - y            heigth of image                                   *
 *                                                                             *
 *   Output : - mem_8_out    points to the allocated memory for output image   *
 *                                                                             *
 *   Return : - NULL if ok, 1 on error.                                        *
 *                                                                             *
 ******************************************************************************/
int get_mem (fp_in,fp_out,mem_in,mem_8_out,flag_prompt,x,y)
FILE             *fp_in;         /* input file pointer             */
FILE             *fp_out;        /* output file pointer            */
Pixrect          *mem_in;        /* pixrect in memory 24 or 32 Bit */
Pixrect          **mem_8_out;     /* pixrect in memory 8 Bit        */
unsigned char    flag_prompt;    /* flag if prompt                 */
int              x,y;            /* size of image                  */

{
  register unsigned char  *im_ptr_8_out;  /* pointer into 8 Bit image       */
  register int            i;              /* used for loops                 */

  /* allocate memory for output image */
  if (flag_prompt) fprintf (stderr,"Creating memory for image ... ");
  if ( (*mem_8_out = mem_create (x,y,8)) == NULL ) {
    fprintf (stderr,"\nError in mem_create (). Not enough memory!\n");
    close_error (fp_in,fp_out,mem_in,*mem_8_out);
    return (1);
  }

  /* and clear it */
  if (flag_prompt) fprintf (stderr,"done. Now clearing it ... ");
  im_ptr_8_out = (unsigned char *) mpr_d(*mem_8_out)->md_image;
  for (i = 0; i < x*y; i++, im_ptr_8_out++) *im_ptr_8_out = 0;
  if (flag_prompt) fprintf (stderr,"done. \n");
  return (NULL);
}


/*******************************************************************************
 *   put_image                                                                 *
 *                                                                             *
 *   Input  : - fn_out       output filename                                   *
 *            - fp_in        input file pointer                                *
 *            - fp_out       output file pointer                               *
 *            - mem_in       pixrect in memory 24 or 32 Bit                    *
 *            - mem_8_out    pixrect in memory 8 Bit                           *
 *            - flag_prompt  flag if prompt or not                             *
 *            - colormap     colormap of output image                          *
 *            - out_format   writing format                                    *
 *                                                                             *
 *   Return : - NULL if ok, 1 on error.                                        *
 *                                                                             *
 ******************************************************************************/
int put_image (fn_out,fp_in,fp_out,mem_in,mem_8_out,
               flag_prompt,colormap,out_format)
char          fn_out[FNS];  /* output filename                */
FILE          *fp_in;       /* input file pointer             */
FILE          *fp_out;      /* output file pointer            */
Pixrect       *mem_in;      /* pixrect in memory 24 or 32 Bit */
Pixrect       *mem_8_out;   /* pixrect in memory 8 Bit        */
unsigned char flag_prompt;  /* flag if prompt                 */
colormap_t    colormap;     /* colormap of output file        */
int           out_format;   /* output file format             */

{
  /* write the output image */
  if (flag_prompt) fprintf (stderr,"Writing %s ... ",fn_out);
  if ( pr_dump (mem_8_out,fp_out,&colormap,out_format,0) == PIX_ERR) {
    fprintf (stderr,"\nError writing %s \n",fn_out);
    fprintf (stderr," -maybe disk is full.\n");
    fprintf (stderr," -maybe overdisk quota.\n");
    fprintf (stderr," -maybe server is defective.\n");
    close_error (fp_in,fp_out,mem_in,mem_8_out);
    return (1);
  }
  if (flag_prompt) fprintf (stderr,"done. \n");
  return (NULL);
}


/*******************************************************************************
 *   print_euclidean_dist                                                      *
 *                                                                             *
 *   Input  : - fn_out       output filename                                   *
 *            - mem_in       pixrect in memory 24 or 32 Bit                    *
 *            - mem_8_out    pixrect in memory 8 Bit                           *
 *            - flag_prompt  flag if prompt or not                             *
 *            - red          pointer to red values in colormap                 *
 *            - green        pointer to green values in colormap               *
 *            - blue         pointer to blue values in colormap                *
 *            - x            width of image                                    *
 *            - y            heigth of image                                   *
 *                                                                             *
 *   Output :                prints to stdout the sum of the euclidean         *
 *                           distance over all pixels and the average per pixel*
 *                                                                             *
 ******************************************************************************/
void print_euclidean_dist  (fn_out,mem_in,mem_8_out,flag_prompt,red,green,blue,
                            x,y)
char           fn_out[FNS];  /* output filename                     */
Pixrect        *mem_in;      /* pixrect in memory 24 or 32 Bit      */
Pixrect        *mem_8_out;   /* pixrect in memory 8 Bit             */
unsigned char  flag_prompt;  /* flag if prompt                      */
unsigned char  *red;         /* pointer to red values in colormap   */
unsigned char  *green;       /* pointer to green values in colormap */
unsigned char  *blue;        /* pointer to blue values in colormap  */
int            x,y;          /* size of image                       */

{
  register unsigned long int  euclid_tmp;    /* temporary for calculating     */
  register unsigned long int  euclid_dist;   /* sum of euclidean distance     */
  register int                *im_ptr_in;    /* pointer into 24 or 32 Bit imag*/
  register unsigned char      *im_ptr_8_out; /* pointer into 8 Bit image      */
  register int                i;             /* used for loops                */

  /* euclidean distance for one pixel := ( red(input)   - red(output) )^2 +   */
  /*                                     ( green(input) - green(output) )^2 + */
  /*                                     ( blue(input)  - blue(output) )^2    */
  /* it's a simple measure how good the output color fits the input color     */

  if (flag_prompt) fprintf (stderr,"Calculating euclidean distance ... ");
  euclid_dist = 0;
  im_ptr_in = (int *) mpr_d(mem_in)->md_image;
  im_ptr_8_out = (unsigned char *) mpr_d(mem_8_out)->md_image;
  for (i = 0; i < x*y; i++, im_ptr_in++, im_ptr_8_out++) {
    euclid_tmp  = 0;
    euclid_tmp  = (  (*im_ptr_in)        & 0xff) - red[*im_ptr_8_out];
    euclid_dist += euclid_tmp * euclid_tmp;
    euclid_tmp  = (( (*im_ptr_in) >>  8) & 0xff) - green[*im_ptr_8_out];
    euclid_dist += euclid_tmp * euclid_tmp;
    euclid_tmp  = (( (*im_ptr_in) >> 16) & 0xff) - blue[*im_ptr_8_out];
    euclid_dist += euclid_tmp * euclid_tmp;
  }
  if (flag_prompt) fprintf (stderr,"done. \n\n");
  printf ("Euclidean statistics for %s \n",fn_out);
  printf ("Sum of euclidean distance over all pixels = %d \n",
          euclid_dist);
  printf ("Average per color pixel  (sum/x/y/3)      = %4.2f \n\n\n",
          (float) euclid_dist / (x * y * 3) );
}


/*******************************************************************************
 *   write_map                                                                 *
 *                                                                             *
 *   Input  :                                                                  *
 *            - fn_out       output filename                                   *
 *            - red          colormap red values                               *
 *            - green        colormap green values                             *
 *            - blue         colormap blue values                              *
 *                                                                             *
 *  Output :                                                                   *
 *                           prints to stdout the colormap                     *
 *                                                                             *
 ******************************************************************************/
void write_map (fn_out,red,green,blue)
char           fn_out[FNS];  /* output filename                     */
unsigned char  red[256];     /* red values                          */
unsigned char  green[256];   /* green values                        */
unsigned char  blue[256];    /* blue values                         */

{
  unsigned char  i;          /* used for loops */

  printf ("Colormap for %s \n\n",fn_out);
  printf ("nr.  red green blue   red green blue   red green blue   ");
  printf ("red green blue\n");
  for (i = 0; i < 64; i++) {
    printf ("%3d  %3d  %3d  %3d    %3d  %3d  %3d    %3d  %3d  %3d    ",
             i*4,
             red[i*4],   green[i*4],   blue[i*4],
             red[i*4+1], green[i*4+1], blue[i*4+1],
             red[i*4+2], green[i*4+2], blue[i*4+2]);
    printf ("%3d  %3d  %3d\n",
              red[i*4+3], green[i*4+3], blue[i*4+3]);
  }
  printf ("\n\n\n");
}



/*******************************************************************************
 *   read_resolution                                                           *
 *                                                                             *
 *   Input  :                                                                  *
 *            - fp_in        input file pointer                                *
 *                                                                             *
 *  Output :                                                                   *
 *            - x, y         width and heigth of image                         *
 *                                                                             *
 *  Return :                                                                   *
 *            1 ok                                                             *
 *            0 on error                                                       *
 *                                                                             *
 ******************************************************************************/

/*
 * Written by Christoph Streit
 */

int read_resolution (x, y, fp_in)
     int *x, *y;
     FILE *fp_in;
{
  char one_byte;

  *x = 0; *y = 0;

  /* get first value */
  do
  {
    if (fread(&one_byte, 1, 1,fp_in) &&
	!isspace(one_byte)            &&
	isdigit(one_byte))
      *x = *x * 10 + (int) (one_byte - '0');
  } while (isdigit(one_byte));

  while (isspace(one_byte) && fread(&one_byte,1 ,1, fp_in));

  while (one_byte != '\n')
  {
    *y = *y * 10 + (int) (one_byte - '0');
    if (!fread(&one_byte,1 ,1, fp_in))
      return 0;
  }
  return 1;
}
