#include <string>
#include <stdio.h>
#include <png.h>
#include "image.h"

using namespace std;

namespace hm
{

#ifdef IMAGE_BMP_SUPPORT
	void Image::loadBMPFromMem(unsigned char *data)
	{
		printf("BMP data found\n");
		int i,j;

		// header
		BMP_HEADER *header;
		header = (BMP_HEADER*)data;
		data+=sizeof(BMP_HEADER);

		// init/alloc skimage structure
		width = header->biWidth;
		height = header->biHeight;
		pixels = new unsigned char[width*height*(header->biBitCount>>3)]; // 24bpp

		// do we have an alpha channel?
		this->hasAlphaChannel = (header->biBitCount==32);

/*
		// 8bit uncompressed image data
		if ((header->biBitCount==8)&&(header->biCompression==0)) {
			// read palette
			int bmpwidth = img->w;
			bmpwidth+=4;
			int cols = header->biClrUsed;

			char *pal = (char*)data;
			data+=(cols*4);
			char *imgdata = (char*)data;

			int bmp_pos=0;
			for (i=0; i<img->h; i++) {
				for (j=0; j<img->w; j++) {
					char c = imgdata[bmp_pos];
					img->data[(j*img->h)+i] = real2hi2(pal[c*4+2],pal[c*4+1],pal[c*4]);
					bmp_pos++;
				}
				while ((bmp_pos&3)>0) bmp_pos++; // evil bmp shit
			}

		} 

		// 8bit rle-compressed image data
		if ((header->biBitCount==8)&&(header->biCompression==1)) {

			// read palette
			int cols = header->biClrUsed;
			char *pal = (char*)data;

			data+=(cols*4);
			char *imgdata = (char*)data;

			int pos=0;

			// first read the image to a temp buffer
			unsigned short *buffer = gm_malloc(img->w*img->h*2);

			bool eod = false;
			while (!eod) {

				char n = *(imgdata++);
				char c = *(imgdata++);
				if (n!=0) {
					// write n pixels
					for (i=0; i<n; i++) buffer[pos++] = real2hi2(pal[c*4+2],pal[c*4+1],pal[c*4]);
				} else {
					//if (c==0) eol=true; // end of line
					if (c==1) eod=true; // end of data

					if (c>=3) { // c pixels uncompressed
						for (i=0; i<c; i++) {
							char cc = *(imgdata++);
							//buffer[pos++] = real2hi(pal[cc*4]|(pal[cc*4+1]<<8)|(pal[cc*4+2]<<16));
							buffer[pos++] = real2hi2(pal[cc*4+2],pal[cc*4+1],pal[cc*4]);
						}
						if ((c&1)==1) imgdata++; // align to 16bit boundary
					}
				}
			} // read data

			// now copy the buffer to the image data (90 degrees anti clockwise)
			for (i=0; i<img->h; i++)
				for (j=0; j<img->w; j++)
					img->data[(j*img->h)+i] = buffer[i*img->w+j];

			// free our temp buffer;
			gm_free(buffer);

		} 
		*/

		// 24bit uncompressed image data
		if ((header->biBitCount==24)&&(header->biCompression==0))
		{
			char *imgdata = (char*)data;

			int bmp_pos=0;
			for (i=0; i<height; i++)
			{
				for (j=0; j<width; j++)
				{
					pixels[((height-i-1)*width+j)*3+0] = imgdata[bmp_pos+0];
					pixels[((height-i-1)*width+j)*3+1] = imgdata[bmp_pos+1];
					pixels[((height-i-1)*width+j)*3+2] = imgdata[bmp_pos+2];
					bmp_pos+=3;
				}
				while ((bmp_pos&3)>0) bmp_pos++; // evil bmp shit
			}
		}

		// 32bit uncompressed image data
		if ((header->biBitCount==32)&&(header->biCompression==0)) {
			char *imgdata = (char*)data;
			int bmp_pos=0;
			for (i=0; i<height; i++)
			{
				for (j=0; j<width; j++)
				{
					pixels[((height-i-1)*width+j)*4+0] = imgdata[bmp_pos+0];
					pixels[((height-i-1)*width+j)*4+1] = imgdata[bmp_pos+1];
					pixels[((height-i-1)*width+j)*4+2] = imgdata[bmp_pos+2];
					pixels[((height-i-1)*width+j)*4+3] = imgdata[bmp_pos+3];
					bmp_pos+=4;
				}
				while ((bmp_pos&3)>0) bmp_pos++; // evil bmp shit
			}
		}
	}
#endif

	void Image::loadPNGFromMem(FILE *fp)
	{
		png_structp png_ptr = png_create_read_struct
			(PNG_LIBPNG_VER_STRING, (png_voidp) NULL, NULL, NULL);
		if (!png_ptr)
		{
			printf(" - Could not create png_ptr");
			return;
		}
		png_infop info_ptr = png_create_info_struct(png_ptr);
		if (!info_ptr)
		{
			printf(" - Could not create info_ptr");
			png_destroy_read_struct(&png_ptr,(png_infopp)NULL, 
				(png_infopp)NULL);
			return;
		}
		png_infop end_info = png_create_info_struct(png_ptr);
		if (!end_info)
		{
			printf(" - Could not create end_info");
			png_destroy_read_struct(&png_ptr, &info_ptr,(png_infopp)NULL);
			return;
		}
		png_init_io(png_ptr, fp);
		png_read_png(png_ptr, info_ptr, PNG_TRANSFORM_BGR
			| PNG_TRANSFORM_STRIP_16 | PNG_TRANSFORM_PACKING, NULL);
		png_bytepp row_ptr = png_get_rows(png_ptr, info_ptr);
		
		height = info_ptr->height;
		width = info_ptr->width;
		hasAlphaChannel = info_ptr->color_type & PNG_COLOR_MASK_ALPHA;

		char numBytes = hasAlphaChannel?4:3;
		pixels = new unsigned char[width*height*numBytes];
		for (int i=0; i<height; i++)
			memcpy((void *)&pixels[i*width*numBytes], (const void *)row_ptr[i], 
					width*numBytes);
		png_destroy_read_struct(&png_ptr, &info_ptr, (png_infopp)NULL);
	}

	// TODO: add error handling
	Image::Image(string filename)
	{
		/* load bmp into memory */
		hasAlphaChannel = false;

		// open file
		FILE* file;
		file = fopen(filename.c_str(),"rb");
		if (!file) throw string("unable to open file ["+filename+"]");

#ifdef IMAGE_BMP_SUPPORT
		// determine file size
		fseek(file,0,SEEK_END);
		long fileSize = ftell(file);
		fseek(file,0,SEEK_SET);

		// allocate memory for the file
		unsigned char *data = new unsigned char[fileSize];
		fread(data,1,fileSize,file);
		fseek(file,0,SEEK_SET);

		// read BMP file
		if (png_sig_cmp(data, 0, fileSize))
			loadBMPFromMem(data);
		else 
#endif // IMAGE_BMP_SUPPORT
			loadPNGFromMem(file);
		
		fclose(file);
		// free temporary buffer
#ifdef IMAGE_BMP_SUPPORT
		delete(data);
#endif // IMAGE_BMP_SUPPORT
	}

	Image::Image(int w,int h)
	{
		width = w;
		height = h;
		hasAlphaChannel = false;
		pixels = new unsigned char[w*h*3]; // 24bpp
	}

	Image* Image::subImage(int x,int y,int w,int h)
	{
		Image *ret = new Image(w,h);

		int numBytes = hasAlphaChannel?4:3;

		// copy the image subsection
		for (int i=0; i<h; i++)
			for (int j=0; j<w; j++)
				for (int h=0; h<numBytes; h++)
					ret->getPixels()[(i*w+j)*numBytes+h] = getPixels()[((i+y)*width+x+j)*numBytes+h];

		return(ret);
	}

	Image::~Image()
	{
		// free memory
		delete(pixels);
	}

	unsigned char* Image::getPixels()
	{
		return(pixels);
	}

	int Image::getWidth()
	{
		return(width);
	}

	int Image::getHeight()
	{
		return(height);
	}

	bool Image::hasAlpha()
	{
		return(hasAlphaChannel);
	}
}

