/*** TODO ***/
// Need to do alot of cleaning up...this is temporary code
// No garanties about it are made.
// This is as it is.
/*** TODO ***/

#include "ReadImage.H"
#include "file.H"
#include "world.H"
#include "xrml.H"
#include <string.h>
#include <math.h>
#include <stdlib.h>

namespace xrml {

void skipComment(class file *file)
{
  char b = fgetc(file->fp);

  while(b == '#')
    {
      while (b != '\n')
        b = fgetc(file->fp);

      b = fgetc(file->fp);
    }
  ungetc(b, file->fp);
}

float *loadPPM(class file *file, long& width, long& height, long& channels)
{
  int  colorsize;

  // read header (P6)
  fscanf(file->fp, "P6\n");


  // skip comment (#)
  skipComment(file);

  // read dimensions
  int r = fscanf(file->fp, "%ld %ld\n%d\n", &width, &height, &colorsize);
  if (r != 3) Error(NULL, "PhBImageTextureBase::load() : not a PPM (raw) file");


  // calc amount of memory needed and allocate it
  long amount = width * height * 3;
  float *map = new float[amount];


  // load image
  for(int i = 0; i < amount; i++)
      map[i] = (float)(fgetc(file->fp)) / colorsize;


  // set nr of channels
  channels = 3;


  // return map
  return map;
}


#define FORMAT "FORMAT"
#define FORMAT_TYPE "32-bit_rle_rgbe"
#define EXPOSURE "EXPOSURE"

float RealPixel2RGB(int c, int e)
{
  if(e == 0) return 0;
  else {
    float v = ldexp(1./256, e - 128);
    return (c + .5)*v;
  }
}

void PICscanline(class file *file, float *row, long width)
{
  long pos = 0;
  int r, g, b, e, i;

  while(pos < (3*width))
    {
      // read 4 bytes
      r = fgetc(file->fp);
      g = fgetc(file->fp);
      b = fgetc(file->fp);
      e = fgetc(file->fp);

      // check compresion method
      if((r == 1) && (g == 1) && (b == 1))           // Old Run-Length encoding
	{
	  long prev = pos - 3;
	  long l = e, t = 8;
	  bool done = false;

	  // stupidity check
	  if(pos < 3) Error(NULL, "PIC: Illegal Old RLE compression");

	  // check l
	  while(!done) 
	    {
	      if(t > 32) Error(NULL, "PIC: Old RLE overflow");
	      
	      r = fgetc(file->fp);
	      g = fgetc(file->fp);
	      b = fgetc(file->fp);
	      e = fgetc(file->fp);
	    
	      if(e == EOF) done = true;
	      else if((r == 1) && (g == 1) && (b ==1))
		{
		  l += (e << t);
		  t += 8;
		}
	      else done = true;
	    }

	  if(e != EOF)
	    {
	      ungetc(e, file->fp);
	      ungetc(b, file->fp);
	      ungetc(g, file->fp);
	      ungetc(r, file->fp);
	    }
	
	  // repeat
	  for(i=0; i < l; i++)
	    {
	      row[pos++] = row[prev + 0];
	      row[pos++] = row[prev + 1];
	      row[pos++] = row[prev + 2];
	    }
	}
      else if((r == 2) && (g == 2))           // Run-Length encoding
	{
	  if((g << 8 | e) != width) Error(NULL, "PIC: RLE length mismatch");
	  
	  int *R = new int[width];
	  int *G = new int[width];
	  int *B = new int[width];
	  int *E = new int[width];
	  int *T = NULL;

	  // repeat per channel
	  for(i=0; i < 4; i++)
	    {
	      int p = 0;
	      if(i==0) T = R;
	      if(i==1) T = G;
	      if(i==2) T = B;
	      if(i==3) T = E;

	      while(p < width)
		{
		  e = fgetc(file->fp);
		  if(p >= width) Error(NULL, "PIC: RLE out of bounds");
		  if(e == EOF) Error(NULL, "PIC: RLE unexpected end");
		  if(e > 128)
		    {
		      e &= 127;
		      r = fgetc(file->fp);

		      for(int j=0; j < e; j++) T[p++] = r;
		    }
		  else for(int j=0; j < e; j++) T[p++] = fgetc(file->fp);
		}
	    }

	  // copy & convert
	  for(i=0; i < width; i++)
	    {	  
	      r = R[i];
	      g = G[i];
	      b = B[i];
	      e = E[i];

	      row[pos++] = RealPixel2RGB(r, e);
	      row[pos++] = RealPixel2RGB(g, e);
	      row[pos++] = RealPixel2RGB(b, e);
	    }

	  // free mem
	  if(R) delete[] R;
	  if(G) delete[] G;
	  if(B) delete[] B;
	  if(E) delete[] E;
	}
      else                           // Uncompressed
	{
	  row[pos++] = RealPixel2RGB(r, e);
	  row[pos++] = RealPixel2RGB(g, e);
	  row[pos++] = RealPixel2RGB(b, e);
	}
    }
}

float *loadPIC(class file *file, long& width, long &height, long &channels)
{
  // Based on the original code for PIC readers.
  float exposure = 1.;
  bool done = false;
  char buf[80];

  // read header
  fscanf(file->fp, "#?RADIANCE\n");

  while(!done)
    {
      skipComment(file);

      // read line
      fgets(buf, 80, file->fp);

      // check format (only support rgbe format...NOT xyze)
      if(!strncasecmp(buf, FORMAT, strlen(FORMAT)))
	{
	  char *string = strchr(buf, '=') + 1;
	  if(strncasecmp(string, FORMAT_TYPE, strlen(FORMAT_TYPE))) Error(NULL, "Unsupported PIC format in %s", file->url);
	}

      // read exposure
      else if(!strncasecmp(buf, EXPOSURE, strlen(EXPOSURE)))
	{
	  char *value = strchr(buf, '=') + 1;
	  exposure = 1. / atof(value);
	}

      // end?
      else if(!strncasecmp(buf, "\n", strlen("\n"))) done = true;

      /* else skip line */
    }

  // get resolution (ignore orientation & sign)
  char sx, sy, X, Y;
  if( fscanf(file->fp, "%c%c %ld %c%c %ld\n", &sy, &Y, &height, &sx, &X, &width) != 6)
    Error(NULL, "PIC resolution error. (%c%c %ld %c%c %ld)", sy, Y, height, sx, X, width);

  channels = 3;
  
  // calc amount of memory needed and allocate it
  long amount = width * height * 3;
  float *map = new float[amount];

  // read image
  float *row = new float[width * 3];
  int pos = 0;

  for(int y = 0; y < height; y++)
    {
      PICscanline(file, row, width);

      for(int x = 0; x < width; x++)
	{
	  map[((pos + x) * 3) + 0] = row[(x * 3) + 0] * exposure;
	  map[((pos + x) * 3) + 1] = row[(x * 3) + 1] * exposure;
	  map[((pos + x) * 3) + 2] = row[(x * 3) + 2] * exposure;
	}

      pos += width;
    }

  if(row) delete[] row;

  // return pointer to image
  return map;
}

Image& Image::operator=(const Image& src)
  {
    if (this != &src)
    {
      unload();

      // copy complete map
      long amount = src.width * src.height * src.channels;
      map = new float[amount];
      memcpy(map, src.map, amount);

      // copy data
      channels = src.channels;
      width = src.width;
      height = src.height;
    }

  return *this;
}


void Image::load(MFString url, class world *world)
{
  // load image texture into buffer
  if (url.size == 0) Error(NULL, "PhBImageTextureBase::load() : no url given");

  class file* file = world->openFileFromURLs(url);

  if (!file) {
    Error("PhBImageTextureBase::load()", "couldn't open texture image file");
    return;
  }

  // determine file type
  char *name = (char *)url[0];
  if(strstr(name, ".ppm") || strstr(name, ".pnm"))
  {
    map = loadPPM(file, width, height, channels);
    fprintf(stderr, "PPM ");
  }
  else if(strstr(name, ".pic"))
  {
    map = loadPIC(file, width, height, channels);
    fprintf(stderr, "PIC ");
  }
  else
  {
    Error("PhBImageTextureBase::load()", "unknown image format for '%s'", name);
  }    

  // close file
  file->close();

  // Display nice message
  fprintf(stderr, "read: %s: %ld KBytes, Size=%ldx%ld, Channels=%ld\n",
	  file->url, width * height * 3 * sizeof(float) / 1024, width, height, channels);
}

}
