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

#include <stdlib.h>
#include <stdio.h>
#include <me_c_video.h>
#include <me_c_video_driver_interface.h>
#include <me_c_video_surface.h>
#include <string>
#include <iostream>

namespace SDL
{
#include "SDL.h"
#include "SDL_rwops.h"
}
extern "C"
{
#include <png.h>
}

#define PNG_BYTES_TO_CHECK 4

/* See if an image is contained in a data source */
int IMG_isPNG( SDL::SDL_RWops *src )
{
	unsigned char buf[ PNG_BYTES_TO_CHECK ];

	/* Read in the signature bytes */
	if ( SDL_RWread( src, buf, 1, PNG_BYTES_TO_CHECK ) != PNG_BYTES_TO_CHECK )
		return 0;

	/* Compare the first PNG_BYTES_TO_CHECK bytes of the signature. */
	return ( !png_sig_cmp( buf, ( png_size_t ) 0, PNG_BYTES_TO_CHECK ) );
}

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

	src = ( SDL::SDL_RWops * ) png_get_io_ptr( ctx );
	SDL_RWread( src, area, size, 1 );
}
//SDL::SDL_Surface *IMG_LoadPNG_RW(SDL::SDL_RWops *src)
ME_n_Video::ME_c_Video_Surface *IMG_LoadPNG_RW( std::string fname, std::string grp,std::string name )
{
	SDL::SDL_RWops * src;
	src = SDL::SDL_RWFromFile( fname.c_str(),"rb" );
	//	SDL::SDL_Surface *volatile surface;
	png_structp png_ptr;
	png_infop info_ptr;
	png_uint_32 width, height;
	int bit_depth, color_type, interlace_type;
	ME_n_Video::ME_c_Video_Surface *surface;
	Uint32 Rmask;
	Uint32 Gmask;
	Uint32 Bmask;
	Uint32 Amask;
	SDL::SDL_Palette *palette;
	png_bytep *volatile row_pointers;
	int row, i;
	volatile int ckey = -1;
	png_color_16 *transv;
	ME_n_Video::ME_c_Video video;

//std::cerr<<"Loading PNG"<<std::endl;
//std::cerr<<"Is png?"<<IMG_isPNG(src )<<std::endl;
	if ( !src )
	{
//std::cerr<<"!SRC"<<std::endl;
		/* 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;
// 
	surface =&( *video )._create_surface( ( Sint16 ) width, ( Sint16 ) height, grp,false,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 );
	}
	SDL_FreeRW(src);
	return ( surface );
}
