#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include "pgm_helper.h"


int setSize(struct image *im, int rows, int columns)
     /*  sets the size of the image to the given 
         height (# of rows) and width (# of columns);
         returns 0 if OK or -1 if fails;*/
{
  im->rows=rows;
  im->columns=columns;
  im->data = (uchar *) malloc(rows * columns * sizeof(char));
  if(im->data == NULL) {
       fprintf(stderr, "Insufficient memory for new image\n");
        exit(1); 
        } 

  return 0;
}

int setSizeColor(struct imageColor *im, int rows, int columns)
     /*  sets the size of the image to the given 
	 height (# of rows) and width (# of columns);	
	 returns 0 if OK or -1 if fails;*/
{
  im->rows=rows;
  im->columns=columns;
  im->dataR = (uchar *) malloc(rows * columns * sizeof(char));
  if(im->dataR == NULL) {
       fprintf(stderr, "Insufficient memory for new image\n");
	exit(1); 
        }
  
  im->dataG = (uchar *) malloc(rows * columns * sizeof(char));
  if(im->dataG == NULL) {
       fprintf(stderr, "Insufficient memory for new image\n");
	exit(1); 
        }
  
  im->dataB = (uchar *) malloc(rows * columns * sizeof(char));
  if(im->dataB == NULL) {
       fprintf(stderr, "Insufficient memory for new image\n");
	exit(1); 
        } 

  return 0;
}

int getNCols(struct image *im)
     /*     returns the number of columns in the image;*/
{
  return im->columns;
}


int getNColsColor(struct imageColor *im)
     /*     returns the number of columns in the image;*/
{
  return im->columns;
}

int getNRows(struct image *im)
     /*     returns the number of rows in the image;*/
{
  return im->rows;
}

int getNRowsColor(struct imageColor *im)
     /*     returns the number of rows in the image;*/
{
  return im->rows;
}

int setColors(struct image *im, int colors)
     /*     sets the number of gray-level colors in the image
            (not counting 0);
            returns 0 if OK or -1 if fails;*/
{
  im->colors=colors;
  return 0;
}

int setColorsColor(struct imageColor *im, int colors)
     /*     sets the number of gray-level colors in the image
	    (not counting 0);
	    returns 0 if OK or -1 if fails;*/
{
  im->colors=colors;
  return 0;
}
int getColors(struct image *im)
     /*     returns the number of gray-level colors in the image; */
{
  return im->colors;
}

int getColorsColor(struct imageColor *im)
     /*     returns the number of gray-level colors in the image; */
{
  return im->colors;
}
int setPixel(struct image *im, int i, int j, int color)
     /*    sets the pixel in the image at row i and column j 
           to a particular color; */
{
  i=CLAMP(i, 0, im->rows-1);
  j=CLAMP(j, 0, im->columns-1);
  im->data[i*(im->columns)+j]=color;
}


int setPixelColor(struct imageColor *im, int i, int j, int colorR, int colorG, int colorB)
     /*    sets the pixel in the image at row i and column j 
	   to a particular color; */
{
  i=CLAMP(i, 0, im->rows-1);
  j=CLAMP(j, 0, im->columns-1);
  im->dataR[i*(im->columns)+j]=colorR;
  im->dataG[i*(im->columns)+j]=colorG;
  im->dataB[i*(im->columns)+j]=colorB;
}
int getPixel(struct image *im, int i, int j)
     /*     returns the color of the pixel in the image at row i and column j;*/

{
  i=CLAMP(i, 0, im->rows-1);
  j=CLAMP(j, 0, im->columns-1);
  return (int) im->data[i*(im->columns)+j];
}


int getPixelColor(struct imageColor *im, int i, int j, int colorNum)
     /*     returns the color of the pixel in the image at row i and column j;*/	
{
  i=CLAMP(i, 0, im->rows-1);
  j=CLAMP(j, 0, im->columns-1);
  if (colorNum==1)  return (int) im->dataR[i*(im->columns)+j];
  if (colorNum==2)  return (int) im->dataG[i*(im->columns)+j];
  if (colorNum==3)  return (int) im->dataB[i*(im->columns)+j];
}


/* Copyright: Anton Nikolaev, 1995 */
/* NO WARRANTIES: use at your own risk */

/* 
  These functions read/write Portable Grey Map (PGM) image files 
  from/to files with given names;

  for the format specification refer to e.g.
  J.D.Murray, W.vanRyper "Encyclopedia of Graphics File Formats", 1994;
  
  The user has to provide the image structure definition and 
  the following "hook" functions:

  int setSize(struct image *im, int rows, int columns);
    sets the size of the image to the given 
    height (# of rows) and width (# of columns);	
    returns 0 if OK or -1 if fails;


  int getNCols(struct image *im);
    returns the number of columns in the image;


  int getNRows(struct image *im);
    returns the number of rows in the image;


  int setColors(struct image *, int colors);
    sets the number of gray-level colors in the image
    (not counting 0);
    returns 0 if OK or -1 if fails;


  int getColors(struct image *);
    returns the number of gray-level colors in the image;
   

  int setPixel(struct image *im, int i, int j, int color);
    sets the pixel in the image at row i and column j 
    to a particular color;


  int getPixel(struct image *im, int i, int j);
    returns the color of the pixel in the image at row i and column j;	
*/ 

int readImage(struct image *im, const char *fname)
/*
  reads image from fname;

  returns 0 if OK or -1 if something goes wrong.
*/
{  
  FILE *input;
  char line[1024];     
  int nCols,nRows;  
  int levels;
  int i, j;

  /* open it */
  if (!fname || (input=fopen(fname,"rb"))==0) {
    printf( "couldn't open input file %s\n", fname );
    return-1;
    }

  /* check for the right "magic number" */
  if ( fread(line,1,3,input)!=3 ||strncmp(line,"P5\n",3) ) {
    printf( "magic number was %s, expected P5\n", line );
    fclose(input);
    return -1;
  }
  
  /* skip the comments */
  do
    fgets(line,sizeof line,input);
  while(*line=='#');

  /* read the width and height */
  sscanf(line,"%d %d\n",&nCols,&nRows);
  setSize(im, nRows, nCols);

  /* read # of gray levels */
  fgets(line,sizeof line,input);
  sscanf(line,"%d\n",&levels);
  setColors(im, levels);
  
  /* read pixel row by row */
  for(i=0;i<nRows;i++)
  {
    for(j=0;j<nCols;j++)
    {
      int byte=fgetc(input);

      /*if (byte==EOF) /* short file */
      if( feof( input ) != 0)
      {
        printf("file was short: %d (%d,%d;%d,%d)\n", byte, i, j, nRows, nCols );
        fclose(input);
        return -1;
      }
      else
        setPixel(im,i,j,byte);	
    }
  }

  /* close the file */
  fclose(input);
  return 0; /* OK */
}

int readImageColor(struct imageColor *im, const char *fname)
/*
  reads image from fname;

  returns 0 if OK or -1 if something goes wrong.
*/
{  
  FILE *input;
  char line[1024];     
  int nCols,nRows;  
  int levels;
  int i, j;

  /* open it */
  if (!fname || (input=fopen(fname,"rb"))==0)
    return-1;

  /* check for the right "magic number" */
  if (
        fread(line,1,3,input)!=3
      ||strncmp(line,"P6\n",3)
     )
  {
    fclose(input);
    return -1;
  }
  
  /* skip the comments */
  do
    fgets(line,sizeof line,input);
  while(*line=='#');

  /* read the width and height */
  sscanf(line,"%d %d\n",&nCols,&nRows);
  setSizeColor(im, nRows, nCols);
 
  /* read # of gray levels */
  fgets(line,sizeof line,input);
  sscanf(line,"%d\n",&levels);
  setColorsColor(im, levels);
  
  /* read pixel row by row */
  for(i=0;i<nRows;i++)
  {
    for(j=0;j<nCols;j++)
    {
      int byteR, byteG, byteB;
      byteR=fgetc(input);
      if (byteR==EOF) /* short file */
      {
        fclose(input);
        return -1;
      }
      byteG=fgetc(input);
      if (byteG==EOF) /* short file */
      {
        fclose(input);
        return -1;
      }
      byteB=fgetc(input);
      if (byteB==EOF) /* short file */
      {
        fclose(input);
        return -1;
      }
      else
        setPixelColor(im,i,j, byteR, byteG, byteB);	
    }
  }

  /* close the file */
  fclose(input);
  return 0; /* OK */
}

int writeImage(struct image *im, const char *fname)
/*
  writes the image into fname;

  returns 0 if OK or -1 if something goes wrong.
*/
{
  FILE *output; 
  int nRows;
  int nCols;
  int colors;
  int i, j;
    
  /* open the file */
  if (!fname || (output=fopen(fname,"wb"))==0)
    return(-1);

  nRows=getNRows(im);
  nCols=getNCols(im);
  colors=getColors(im);

  /* write the header */
  fprintf(output,"P5\n"); /* magic number */
  fprintf(output,"#\n");  /* empty comment */
  fprintf(output,"%d %d\n%03d\n",nCols,nRows,colors); /* image info */

  /* write pixels row by row */
  for(i=0;i<nRows;i++)
  {
    for(j=0;j<nCols;j++)
    {
      int byte=getPixel(im,i,j);

      if (fputc(byte,output)==EOF) /* couldn't write */
      {
	    fclose(output);
	    return -1;
      }      
    }
  }
  
  /* close the file */
  fclose(output);
  return 0; /* OK */
}

int writeImageColor(struct imageColor *im, const char *fname)
/*
  writes the image into fname;

  returns 0 if OK or -1 if something goes wrong.
*/
{
  FILE *output; 
  int nRows;
  int nCols;
  int colors;
  int i, j;
  
  /* open the file */
  if (!fname || (output=fopen(fname, "wb"))==0)
    return(-1);

  nRows=getNRowsColor(im);
  nCols=getNColsColor(im);
  colors=getColorsColor(im);

  /* write the header */
  fprintf(output,"P6\n"); /* magic number */
  fprintf(output,"#\n");  /* empty comment */
  fprintf(output,"%d %d\n%03d\n",nCols,nRows,colors); /* image info */

  /* write pixels row by row */
  for(i=0;i<nRows;i++)
    {
      for(j=0;j<nCols;j++)
        {
          int byteR, byteG, byteB;
          byteR=getPixelColor(im,i,j, 1);
          if (fputc(byteR,output)==EOF) /* couldn't write */
            {
              fclose(output);
              return -1;
            }      
          byteG=getPixelColor(im,i,j, 2);
          if (fputc(byteG,output)==EOF) /* couldn't write */
            {
              fclose(output);
              return -1;
            }      
          byteB=getPixelColor(im,i,j, 3);
          if (fputc(byteB,output)==EOF) /* couldn't write */
            {
              fclose(output);
              return -1;
            }      
        }
    }

  /* close the file */
  if (fclose(output)==EOF) return -1;
  return 0; /* OK */
}



int line(struct image *im, int x0, int y0, int x1, int y1, int color)
/*  
  draws a line of given gray-level color from (x0,y0) to (x1,y1);
  im is the pointer to the user defined image structure - 
  whatever it is;

  returns 0;    

  uses the function setPixel(struct image *im, int x, int y, int color)
  to actually set pixels in the image to the given color; 

  IMPORTANT: (x0,y0) and (x1,y1) can lie outside the image 
  boundaries, so setPixel() should check the coordinates passed to it;

  implements the Bresenham's incremental midpoint algorithm;
  (adapted from J.D.Foley, A. van Dam, S.K.Feiner, J.F.Hughes
  "Computer Graphics. Principles and practice", 
  2nd ed., 1990, section 3.2.2);  
*/
{  
  int xmin,xmax; /* line coordinates */
  int ymin,ymax;
  int dir;       /* scan direction */
  int dx;        /* distance along X */
  int dy;        /* distance along Y */

  /* increments: East, North-East, South, South-East, North */
  int incrE,
      incrNE,
      incrS,
      incrSE,
      incrN;     
  int d;         /* the D */
  int x,y;       /* running coordinates */
  int mpCase;    /* midpoint algorithm's case */
  int done;      /* set to 1 when done */

  xmin=x0;
  xmax=x1;
  ymin=y0;
  ymax=y1;
  
  dx=xmax-xmin;
  dy=ymax-ymin;

  if (dx*dx>dy*dy) /* horizontal scan */
  {
    dir=DIR_X;
    if (xmax<xmin)
    {
      SWAP(xmin,xmax);
      SWAP(ymin,ymax);
    }
    dx=xmax-xmin;
    dy=ymax-ymin;

    if (dy>=0)
    {
      mpCase=1;
      d=2*dy-dx;      
    }
    else
    {
      mpCase=2;
      d=2*dy+dx;      
    }

    incrNE=2*(dy-dx);
    incrE=2*dy;
    incrSE=2*(dy+dx);
  }
  else /* vertical scan */
  {
    dir=DIR_Y;
    if (ymax<ymin)
    {
      SWAP(xmin,xmax);
      SWAP(ymin,ymax);
    }
    dx=xmax-xmin;
    dy=ymax-ymin;    

    if (dx>=0)
    {
      mpCase=1;
      d=2*dx-dy;      
    }
    else
    {
      mpCase=2;
      d=2*dx+dy;      
    }

    incrNE=2*(dx-dy);
    incrE=2*dx;
    incrSE=2*(dx+dy);
  }
  
  /* start the scan */
  x=xmin;
  y=ymin;
  done=0;

  while(!done)
  {
    setPixel(im,x,y,color);
  
    /* move to the next point */
    switch(dir)
    {
      case DIR_X: /* horizontal */
      {
        if (x<xmax)
        {
	      switch(mpCase)
          {
            case 1:
	          if (d<=0)
	          {
	            d+=incrE;  x++;
	          }
              else
              {
                d+=incrNE; x++; y++;
              }
            break;
  
            case 2:
              if (d<=0)
              {
                d+=incrSE; x++; y--;
              }
              else
              {
                d+=incrE;  x++;
              }
            break;
	      } /* mpCase */
        } /* x<xmax */
        else
	      done=1;
      }  
      break;

      case DIR_Y: /* vertical */
      {
        if (y<ymax)
        {
          switch(mpCase)
          {
            case 1:
              if (d<=0)
              {
                d+=incrE;  y++;
              }
              else
              {
                d+=incrNE; y++; x++;
              }
            break;
  
            case 2:
              if (d<=0)
              {
                d+=incrSE; y++; x--;
              }
              else
              {
                d+=incrE;  y++;
              }
            break;
	      } /* mpCase */
        } /* y<ymin */
        else
	      done=1;
      }
      break;    
    }
  }

  return 0; /* no error */
}
