/* imageio: basic image functions*/
/* LibGD Version, fast and optimized*/

#include "defs.h"
#include "gd.h"
#include <stdio.h>
#include <string.h>
#ifdef USE_CLUSTER
#include "cluster.h"
#endif

/*Internal functions:-------------------------------------------------------*/

inline int fileformatim(char* filename) {
/*Returns 1 for png, 2 for jpg, 3 for wbmp, 4 for gif, */
/*5 for gd, 6 for gd2, 7 for xpm and 8 for xbm*/
   char* fileformat;	/*CharPointer to filetype*/

   fileformat  = strrchr(filename,'.');
   if (fileformat==NULL) {
      fprintf (stderr,"Error: Couldnt determine file extension of %s\n",filename);
      return 0;
   }
   /*Check if the filename has a "." to determine filetype*/

   if (!(strcasecmp(fileformat,".png"))) {
      return 1;
   } else if (!(strcasecmp(fileformat,".jpg")) || !(strcasecmp(fileformat,".jpeg"))) {
      return 2;
   } else if (!(strcasecmp(fileformat,".wbmp"))) {
      return 3;
   } else if (!(strcasecmp(fileformat,".gif"))) {
      return 4;
   } else if (!(strcasecmp(fileformat,".gd"))) {
      return 5;
   } else if (!(strcasecmp(fileformat,".gd2"))) {
      return 6;
   } else if (!(strcasecmp(fileformat,".xpm"))) {
      return 7;
   } else if (!(strcasecmp(fileformat,".xbm"))) {
      return 8;
   } else {
      fprintf (stderr,"Error: Couldnt recognize format %s in %s\n",fileformat,filename);
      return 0;
   }
   /*Check what type of image it is, and if we could possibly open it*/
}


/*External functions:-------------------------------------------------------*/

typedef void* ImagePtr;

ImagePtr loadim(char* filename) {
/*Loads an Image*/
/*Returns gdImagePtr to image structure if succesfull and NULL if not*/

   gdImagePtr im=NULL;		/*ImagePointer to the image*/
   FILE *infile;		/*FilePointer to the image file*/
   int ff;			/*Integer that codes the fileformat type*/

#ifdef USE_DEBUG
   fprintf(stderr,"Opening image %s with GD...\n",filename);
#endif

   ff=fileformatim(filename);
   if (ff==0) {
      fprintf(stderr,"Error: Detection of filetype failed\n");
      return NULL;
   }
   /*Check what type of image it is, and if we could possibly open it*/

   if (!(infile  = fopen( filename,"rb"))) {
      fprintf (stderr,"Error: Couldnt open %s\n",filename);
      return NULL;
   }
   /*We have a filename, we know its type, so open it*/
   
   if (ff==1) {
#ifdef USE_PNG_R
      im= gdImageCreateFromPng(infile);
#else
      fprintf (stderr,"Error: Imagetype PNG not supported in this build!");
#endif
   } else if (ff==2) {
#ifdef USE_JPG_R
      im= gdImageCreateFromJpeg(infile);
#else
      fprintf (stderr,"Error: Imagetype JPG not supported in this build!");
#endif
   } else if (ff==3) {
#ifdef USE_WBMP_R
      im= gdImageCreateFromWBMP(infile);
#else
      fprintf (stderr,"Error: Imagetype WBMP not supported in this build!");
#endif
   } else if (ff==4) {
#ifdef USE_GIF_R
      im= gdImageCreateFromGif(infile);
#else
      fprintf (stderr,"Error: Imagetype GIF not supported in this build!");
#endif
   } else if (ff==5) {
#ifdef USE_DGD_R
      im= gdImageCreateFromGd(infile);
#else
      fprintf (stderr,"Error: Imagetype GD not supported in this build!");
#endif
   } else if (ff==6) {
#ifdef USE_DGD2_R
      im= gdImageCreateFromGd2(infile);
#else
      fprintf (stderr,"Error: Imagetype GD2 not supported in this build!");
#endif
   } else if (ff==7) {
#ifdef USE_XPM_R
      im= gdImageCreateFromXpm(filename);
#else
      fprintf (stderr,"Error: Imagetype XPM not supported in this build!");
#endif
   } else if (ff==8) {
#ifdef USE_XBM_R
      im= gdImageCreateFromXbm(infile);
#else
      fprintf (stderr,"Error: Imagetype XBM not supported in this build!");
#endif
   }
   /*Ok now just let libgd create an image out of it*/

   fclose(infile);
   /*Be tidy and close the opened files*/

   return (ImagePtr) im;
   /*Thats all folks*/
}

/*---------------------------------------------------------------------------*/

int saveim(ImagePtr ima, char* filename) {
/*Saves the Image in a file*/
/*Returns null if sucessfull and -1 if not*/

   FILE *outfile;	/*FilePointer to the image file*/
   int ff;		/*Integer that codes the fileformat type*/
   gdImagePtr im;

#ifdef USE_DEBUG
   fprintf(stderr,"Saving as image %s with GD...\n",filename);
#endif

   im=(gdImagePtr) ima;

   ff=fileformatim(filename);
   if (ff==0) {
      fprintf(stderr,"Error: Detection of filetype failed\n");
      return -1;
   }
   /*Check what type of image it is, and if we could possibly open it*/

   if (!(outfile = fopen(filename,"wb"))) {
      fprintf (stderr,"Error: Couldnt open %s\n",filename);
      return -1;
   }
   /*We have a filename, we know its type, so open it*/

   if (ff==1) {
#ifdef USE_PNG_W
      gdImagePng(im,outfile);
#else
      fprintf (stderr,"Error: Imagetype PNG not supported in this build!");
#endif
   } else if (ff==2) {
#ifdef USE_JPG_W
      gdImageJpeg(im,outfile,-1);
#else
      fprintf (stderr,"Error: Imagetype JPG not supported in this build!");
#endif
   } else if (ff==3) {
#ifdef USE_WBMP_W
      gdImageWBMP(im,gdImageColorAllocate(im, 0, 0, 0),outfile);
#else
      fprintf (stderr,"Error: Imagetype WBMP not supported in this build!");
#endif
   } else if (ff==4) {
#ifdef USE_GIF_W
      gdImageGif(im,outfile);
#else
      fprintf (stderr,"Error: Imagetype GIF not supported in this build!");
#endif
   } else if (ff==5) {
#ifdef USE_DGD_W
      gdImageGd(im,outfile);
#else
      fprintf (stderr,"Error: Imagetype GD not supported in this build!");
#endif
   } else if (ff==6) {
#ifdef USE_DGD2_W
      gdImageGd2(im,outfile,0, GD2_FMT_COMPRESSED);
#else
      fprintf (stderr,"Error: Imagetype GD2 not supported in this build!");
#endif
   } else {
      fprintf (stderr,"Error: Imagetype XPM and XBM not writeable with GD!");
   }
   /*Make libgd save the image to disk*/
   /* TODO: libgd could fail, but i just dunno the return value so
            for now i assume it always works.
            If it doesnt its no problem for me, since
            our program is possibly done with that image >;>>>*/

   fclose(outfile);
   /*be tidy again*/

   return 0;

}

/*---------------------------------------------------------------------------*/

inline ImagePtr createim(int width, int height) {
/*Creates an Image*/
/*Returns ImagePtr to image structure if succesfull and NULL if not*/

#ifdef USE_DEBUG
   fprintf(stderr,"Creating empty image %ix%i with GD...\n",width,height);
#endif

return (ImagePtr) gdImageCreate(width,height);

}

/*---------------------------------------------------------------------------*/

inline int destroyim (ImagePtr ima) {
/*Frees image memory*/

#ifdef USE_DEBUG
   fprintf(stderr,"Deallocating GD image structure...\n");
#endif

   gdImageDestroy((gdImagePtr) ima);
   return 0;
}

/*---------------------------------------------------------------------------*/

inline int widthim(ImagePtr ima) {
/*Returns the images X width*/

   return gdImageSX((gdImagePtr)ima);

}

/*---------------------------------------------------------------------------*/

inline int heightim(ImagePtr ima) {
/*Returns the images X width*/

   return gdImageSY((gdImagePtr)ima);

}

/*---------------------------------------------------------------------------*/

inline int collorallocateim(ImagePtr ima,int r,int g,int b) {
/*Allocates a color structure*/
/*Returns an ID integer of this*/

   return gdImageColorAllocate((gdImagePtr) ima,r,g,b);

}

/*---------------------------------------------------------------------------*/

inline int pixelbrightcim(int r, int g, int b) {
/*Gives back the corresponding pixelbrightness*/
   return ((r+g+b)/3);
}

/*---------------------------------------------------------------------------*/

inline int pixelbrightim(ImagePtr ima,int x,int y) {
/*Gives back the pixelbrightness at x,y in im*/
/*Gives back 0 if error (x,y out of range or so)*/

   int t;
   gdImagePtr im;
   im=(gdImagePtr) ima;
   
#ifndef USE_SPEEDHACK
   if (gdImageBoundsSafe(im,x,y)) {
      t = gdImageGetPixel(im,x,y);
   } else {
      return 0;
   }
#else
 #ifdef gdImageTrueColor
   if (im->trueColor) {
      t = im->tpixels[y][x];
   } else {
 #endif
      t = im->pixels[y][x];
 #ifdef gdImageTrueColor
   }
 #endif
#endif
   return pixelbrightcim(gdImageRed(im,t),gdImageGreen(im,t),gdImageBlue(im,t));
}

/*---------------------------------------------------------------------------*/

inline int pixelredim(ImagePtr ima,int x,int y) {
/*Gives back the pixelredness at x,y in im*/
/*Gives back 0 if error (x,y out of range or so)*/

   int t;
   gdImagePtr im;
   im=(gdImagePtr) ima;

#ifndef USE_SPEEDHACK
   if (gdImageBoundsSafe(im,x,y)) {
      t = gdImageGetPixel(im,x,y);
   } else {
      return 0;
   }
#else
 #ifdef gdImageTrueColor
   if (im->trueColor) {
      t = im->tpixels[y][x];
   } else {
 #endif
      t = im->pixels[y][x];
 #ifdef gdImageTrueColor
   }
 #endif
#endif
   return (gdImageRed(im,t));
}

/*---------------------------------------------------------------------------*/
inline int pixelgreenim(ImagePtr ima,int x,int y) {
/*Gives back the pixelgreenness at x,y in im*/
/*Gives back 0 if error (x,y out of range or so)*/

   int t;
   gdImagePtr im;
   im=(gdImagePtr) ima;

#ifndef USE_SPEEDHACK
   if (gdImageBoundsSafe(im,x,y)) {
      t = gdImageGetPixel(im,x,y);
   } else {
      return 0;
   }
#else
 #ifdef gdImageTrueColor
   if (im->trueColor) {
      t = im->tpixels[y][x];
   } else {
 #endif
      t = im->pixels[y][x];
 #ifdef gdImageTrueColor
   }
 #endif
#endif
   return (gdImageGreen(im,t));
}

/*---------------------------------------------------------------------------*/
inline int pixelblueim(ImagePtr ima,int x,int y) {
/*Gives back the pixelblueness at x,y in im*/
/*Gives back 0 if error (x,y out of range or so)*/

   int t;
   gdImagePtr im;
   im=(gdImagePtr) ima;

#ifndef USE_SPEEDHACK
   if (gdImageBoundsSafe(im,x,y)) {
      t = gdImageGetPixel(im,x,y);
   } else {
      return 0;
   }
#else
 #ifdef gdImageTrueColor
   if (im->trueColor) {
      t = im->tpixels[y][x];
   } else {
 #endif
      t = im->pixels[y][x];
 #ifdef gdImageTrueColor
   }
 #endif
#endif
   return (gdImageBlue(im,t));
}

/*---------------------------------------------------------------------------*/

inline int drawpixelim(ImagePtr ima,int x,int y,int color) {
/*Draws a Pixel into im*/
/*0 if OK, -1 if failed*/
   gdImagePtr im;
   im=(gdImagePtr) ima;

#ifndef USE_SPEEDHACK
   if (gdImageBoundsSafe(im,x,y)) {
      gdImageSetPixel(im,x,y,color);
   } else {
      return -1;
   }
#else
 #ifdef gdImageTrueColor
   if (im->trueColor) {
      im->tpixels[y][x] = color;
   } else {
 #endif
      im->pixels[y][x] = color;
 #ifdef gdImageTrueColor
   }
 #endif
#endif
   return 0;

}


/*---------------------------------------------------------------------------*/

#ifdef USE_CLUSTER
#ifdef USE_SPEEDHACK
inline int processsyncim(ImagePtr *ima,int spread) {
/*when working multithreaded, sync image data to pid 0
  spread is the amount used to calculate split_min / max
  when creating the image (important if != image width) */

   int x,y,y2,y3,t;
   unsigned char * ppointer;
   int * tppointer;
   /*from gd.h im->tpixels is (int**) , im->pixels is (unsigned char **) */
   size_t length;
   gdImagePtr im;

   im=(gdImagePtr)ima;

   x=gdImageSX(im);
   y=gdImageSY(im);
   y2=(cluster_split_min(0,spread)*y)/spread;
   y3=(cluster_split_max(0,spread)*y)/spread;

   if (pid()>0) {
      #ifdef USE_DEBUG
         fprintf(stderr,"cluster(%i)_io: writing image part (line %i - %i)to pid 0 ...\n",pid(),y2,y3);
      #endif
      t=y2;
      while (t<y3) {
         #ifdef gdImageTrueColor
         if (im->trueColor) {
	    tppointer=im->tpixels[t];
	    length=&tppointer[x]-&tppointer[0];
	    cluster_sync_data_tomaster(tppointer,length);
	 } else {
	 #endif
	    ppointer=im->pixels[t];
	    length=&ppointer[x]-&ppointer[0];
	    cluster_sync_data_tomaster(ppointer,length);
         #ifdef gdImageTrueColor
	 }
	 #endif
         t++;
      }
      #ifdef USE_DEBUG
         fprintf(stderr,"cluster(%i)_io: done.\n",pid());
      #endif
   } else {
      t=y3;
      while (t<y) {
         #ifdef USE_DEBUG
	    if ((t/y3)!=y2) {
               fprintf(stderr,"cluster(0)_io: reading image parts from pid(%i)...\n",t/y3);
	       y2=(t/y3);
	    }
         #endif
         #ifdef gdImageTrueColor
         if (im->trueColor) {
	    tppointer=im->tpixels[t];
	    length=&tppointer[x]-&tppointer[0];
	    cluster_sync_data_fromslave(tppointer,length,t/y3);
	 } else {
	 #endif
	    ppointer=im->pixels[t];
	    length=&ppointer[x]-&ppointer[0];
	    cluster_sync_data_fromslave(ppointer,length,t/y3);
         #ifdef gdImageTrueColor
	 }
	 #endif
         t++;
      }
      #ifdef USE_DEBUG
         fprintf(stderr,"cluster(0)_io: done.\n");
      #endif
   }

   return 0; 
}

/*---------------------------------------------------------------------------*/

#else
#error you cannot compile cluster support withoud speedhack, sorry
#endif
#endif


