#pragma once

// this file will be empty if we have no support of PNG
#ifdef PNG_SAVE_FORMAT

#include "savepng.h"

#include "png.h"
#include "SDL.h"

#define IMG_SetError(a) SDL_SetError(a)


/* Save a PNG type image to an SDL datasource */
static void png_write_data(png_structp ctx, png_bytep area, png_size_t size)
{
	SDL_RWops *src;

	src = (SDL_RWops *)png_get_io_ptr(ctx);
	SDL_RWwrite(src, area, size, 1);
}

static void png_io_flush(png_structp ctx)
{
	SDL_RWops *src;

	src = (SDL_RWops *)png_get_io_ptr(ctx);
	/* how do I flush src? */
}

static int png_colortype_from_surface(SDL_Surface *surface)
{
	int colortype = PNG_COLOR_MASK_COLOR; /* grayscale not supported */

	if (surface->format->palette)
		colortype |= PNG_COLOR_MASK_PALETTE;
	else if (surface->format->Amask)
		colortype |= PNG_COLOR_MASK_ALPHA;

	return colortype;
}

static void png_user_warn(png_structp ctx, png_const_charp str)
{
	fprintf(stderr, "libpng: warning: %s\n", str);
}

static void png_user_error(png_structp ctx, png_const_charp str)
{
	fprintf(stderr, "libpng: error: %s\n", str);
}


int IMG_SavePNG_RW(SDL_Surface *face, SDL_RWops *src)
{
	png_structp png_ptr = 0;
	png_infop info_ptr = 0;
	png_bytep *row_pointers = 0;
	int i;
	int colortype;
	int result = -1;

#if SDL_BYTEORDER == SDL_BIG_ENDIAN
	int rmask = 0x00ff0000;
	int gmask = 0x0000ff00;
	int bmask = 0x000000ff;
	int amask = 0x00000000;
#else
	int rmask = 0x000000ff;
	int gmask = 0x0000ff00;
	int bmask = 0x00ff0000;
	int amask = 0x00000000;
#endif

	SDL_Surface *surface = SDL_CreateRGBSurface(SDL_SWSURFACE, face->w, face->h, 24,
								rmask, gmask, bmask, amask);

	SDL_BlitSurface(face, NULL, surface, NULL);

	SDL_LockSurface(surface);

	png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, png_user_error, png_user_warn);

    if (png_ptr == NULL) {
		IMG_SetError("Couldn't allocate memory for PNG file");
		return -1;
	}

	/* Allocate/initialize the image information data.  REQUIRED */
	info_ptr = png_create_info_struct(png_ptr);
    if (info_ptr == NULL) {
		IMG_SetError("Couldn't create image information for PNG file");
		goto done;
	}

	/* Set error handling. */
    if (setjmp(png_ptr->jmpbuf)) {
		/* If we get here, we had a problem reading the file */
		IMG_SetError("Error writing the PNG file");
		goto done;
	}

	png_set_write_fn(png_ptr, src, png_write_data, png_io_flush);

	/* Set the image information here.  Width and height are up to 2^31,
		* bit_depth is one of 1, 2, 4, 8, or 16, but valid values also depend on
		* the color_type selected. color_type is one of PNG_COLOR_TYPE_GRAY,
		* PNG_COLOR_TYPE_GRAY_ALPHA, PNG_COLOR_TYPE_PALETTE, PNG_COLOR_TYPE_RGB,
		* or PNG_COLOR_TYPE_RGB_ALPHA.  interlace is either PNG_INTERLACE_NONE or
		* PNG_INTERLACE_ADAM7, and the compression_type and filter_type MUST
		* currently be PNG_COMPRESSION_TYPE_BASE and PNG_FILTER_TYPE_BASE. REQUIRED
		*/
	colortype = png_colortype_from_surface(surface);
	png_set_IHDR(png_ptr, info_ptr, surface->w, surface->h, 8,
			  colortype, PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE);

	/* Write the file header information.  REQUIRED */
	png_write_info(png_ptr, info_ptr);

	/* pack pixels into bytes */
	//png_set_packing(png_ptr);

	/* Create the array of pointers to image data */
	//row_pointers = (png_bytep*) malloc(sizeof(png_bytep)*surface->h);
	row_pointers = new png_bytep[surface->h];

    if ( (row_pointers == NULL) ) {
		IMG_SetError("Couldn't allocate PNG row pointers");
		goto done;
	}

	for (i = 0; i < surface->h; i++)
		row_pointers[i] = (png_bytep)(Uint8 *)surface->pixels + i*surface->pitch;

	/* write out the entire image data in one call */
	png_write_image(png_ptr, row_pointers);
	png_write_end(png_ptr, info_ptr);
	result = 0;  /* success! */

done:
        if (row_pointers)
			delete [] row_pointers;

	if (info_ptr->palette)
		delete info_ptr->palette;

	png_destroy_write_struct(&png_ptr, (png_infopp)NULL);


	SDL_UnlockSurface(surface);
	SDL_FreeSurface(surface);

	return result;
}

int IMG_SavePNG(SDL_Surface *surface, const char *file)
{
    SDL_RWops *out = SDL_RWFromFile(file, "wb");
    int ret;
    if (!out)
		return -1;
    ret = IMG_SavePNG_RW(surface, out);
    SDL_RWclose(out);
    return ret;
}

#endif
