/* This is a PNG image file loading framework */

#include <stdlib.h>
#include <stdio.h>
#include <video.h>
#include <video_int.h>
#include <string>
#include <iostream>
extern "C"
{
#include <png.h>
}

#define PNG_BYTES_TO_CHECK 4

/* Load a PNG type image from an SDL datasource */
static void png_read_data( png_structp ctx, png_bytep area, png_size_t size )
{
	FILE * src;

	src = ( FILE * ) png_get_io_ptr( ctx );
	int read_size = fread( area, size, 1, src);
	if (read_size != 1 ) 
	{	
		std::cerr<<"Read less than wanted in "<<__FILE__<<std::endl;
	}
}
//SDL::SDL_Surface *IMG_LoadPNG_RW(SDL::SDL_RWops *src)

Video_Surface *IMG_LoadPNG ( Video_Interface *video, std::string fname, std::string grp_name, std::string surf_name, bool overwrite )
{
	FILE * src;
	src = fopen( fname.c_str(),"rb" );
	if(!src)return 0;
	png_structp png_ptr;
	png_infop info_ptr;
	png_uint_32 width, height;
	int bit_depth, color_type, interlace_type;
	Video_Surface *surface;
	Uint32 Rmask;
	Uint32 Gmask;
	Uint32 Bmask;
	Uint32 Amask;
	png_bytep *volatile row_pointers;
	int row;
	volatile int ckey = -1;
	png_color_16 *transv;

	if ( !src )
	{
		/* The error message has been set in SDL::SDL_RWFromFile */
		return NULL;
	}

	/* Initialize the data we will clean up when we're done */
	png_ptr = NULL; info_ptr = NULL; row_pointers = NULL; surface = NULL;

	/* Create the PNG loading context structure */
	png_ptr = png_create_read_struct( PNG_LIBPNG_VER_STRING,
	                                  NULL, NULL, NULL );
//std::cerr<<PNG_LIBPNG_VER_STRING<<" : png_ptr="<<png_ptr<<std::endl;
	
	if ( png_ptr == NULL )
	{
//std::cerr<<"!png_ptr"<<std::endl;
		goto done;
	}

	/* Allocate/initialize the memory for image information.  REQUIRED. */
	info_ptr = png_create_info_struct( png_ptr );
	if ( info_ptr == NULL )
	{
//std::cerr<<"!info_ptr"<<std::endl;
		goto done;
	}

	/* Set error handling if you are using setjmp/longjmp method (this is
	 * the normal method of doing things with libpng).  REQUIRED unless you
	 * set up your own error handlers in png_create_read_struct() earlier.
	 */
	if ( setjmp( png_ptr->jmpbuf ) )
	{
//std::cerr<<"!set_jmp"<<std::endl;
		goto done;
	}

	/* Set up the input control */
	png_set_read_fn( png_ptr, src, png_read_data );

	/* Read PNG header info */
	png_read_info( png_ptr, info_ptr );
	png_get_IHDR( png_ptr, info_ptr, &width, &height, &bit_depth,
	              &color_type, &interlace_type, NULL, NULL );

	/* tell libpng to strip 16 bit/color files down to 8 bits/color */
	png_set_strip_16( png_ptr ) ;

	/* Extract multiple pixels with bit depths of 1, 2, and 4 from a single
	 * byte into separate bytes (useful for paletted and grayscale images).
	 */
	png_set_packing( png_ptr );

	/* scale greyscale values to the range 0..255 */
	if ( color_type == PNG_COLOR_TYPE_GRAY )
		png_set_expand( png_ptr );
	/* For images with a single "transparent colour", set colour key;
	   if more than one index has transparency, or if partially transparent
	   entries exist, use full alpha channel */
	if ( png_get_valid( png_ptr, info_ptr, PNG_INFO_tRNS ) )
	{
		int num_trans;
		Uint8 *trans;
		png_get_tRNS( png_ptr, info_ptr, &trans, &num_trans,
		              &transv );
		if ( color_type == PNG_COLOR_TYPE_PALETTE )
		{
			/* Check if all tRNS entries are opaque except one */
			int i, t = -1;
			for ( i = 0; i < num_trans; i++ )
				if ( trans[ i ] == 0 )
				{
					if ( t >= 0 )
						break;
					t = i;
				}
				else if ( trans[ i ] != 255 )
					break;
			if ( i == num_trans )
			{
				/* exactly one transparent index */
				ckey = t;
			}
			else
			{
				/* more than one transparent index, or translucency */
				png_set_expand( png_ptr );
			}
		}
		else
			ckey = 0; /* actual value will be set later */
	}

	if ( color_type == PNG_COLOR_TYPE_GRAY_ALPHA )
		png_set_gray_to_rgb( png_ptr );

	png_read_update_info( png_ptr, info_ptr );

	png_get_IHDR( png_ptr, info_ptr, &width, &height, &bit_depth,
	              &color_type, &interlace_type, NULL, NULL );

	/* Allocate the SDL surface to hold the image */
	Rmask = 0xFF000000;
	Gmask = 0x00FF0000;
	Bmask = 0x0000FF00;
	Amask = 0x000000FF;
//std::cerr<<"Creating surface"<<std::endl;
// 
	if(!video->_create_surface ( ( Sint16 ) width, ( Sint16 ) height, surf_name, grp_name, overwrite ))return 0;
	surface = video->_get_surface(surf_name, grp_name);

	if ( surface == NULL )
	{	
		goto done;
	}

	/* Create the array of pointers to image data */
	row_pointers = ( png_bytep* ) malloc( sizeof( png_bytep ) * height );
	if ( ( row_pointers == NULL ) )
	{
		surface = NULL;
		goto done;
	}
	for ( row = 1; row <= ( int ) height; row++ )
	{
		row_pointers[ row-1 ] = ( png_bytep )
		                      ( Uint8 * ) surface->_get_surface_data() + (height-row) * surface->_get_line_size();
	}

	/* Read the entire image in one go */
	png_read_image( png_ptr, row_pointers );

	/* and we're done!  (png_read_end() can be omitted if no processing of
	 * post-IDAT text/time/etc. is desired)
	 * In some cases it can't read PNG's created by some popular programs (ACDSEE),
	 * we do not want to process comments, so we omit png_read_end
*/
	png_read_end(png_ptr, info_ptr);
	

	/* Load the palette, if any */
/*	palette = surface->format->palette;
	if ( palette )
	{
		if ( color_type == PNG_COLOR_TYPE_GRAY )
		{
			palette->ncolors = 256;
			for ( i = 0; i < 256; i++ )
			{
				palette->colors[ i ].r = i;
				palette->colors[ i ].g = i;
				palette->colors[ i ].b = i;
			}
		}
		else if ( info_ptr->num_palette > 0 )
		{
			palette->ncolors = info_ptr->num_palette;
			for ( i = 0; i < info_ptr->num_palette; ++i )
			{
				palette->colors[ i ].b = info_ptr->palette[ i ].blue;
				palette->colors[ i ].g = info_ptr->palette[ i ].green;
				palette->colors[ i ].r = info_ptr->palette[ i ].red;
			}
		}
	}*/

done: 	/* Clean up and return */
	png_destroy_read_struct( &png_ptr, info_ptr ? &info_ptr : ( png_infopp ) 0,
	                         ( png_infopp ) 0 );
	if ( row_pointers )
	{
		free( row_pointers );
	}
	fclose(src);
//	mprotect(surface->_get_surface_data(), surface->_get_line_size()*surface->_get_height(), PROT_READ);
	return ( surface );
}
