/*
 *  Copyright (C) 2005-2008  Maarten de Boer <maarten@resorama.com>
 * 
 *  This program is free software; you can redistribute it and/or
 *  modify it under the terms of the GNU General Public License
 *  as published by the Free Software Foundation; either version 2
 *  of the License, or (at your option) any later version.
 * 
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 * 
 *  You should have received a copy of the GNU General Public License
 *  along with this program; if not, write to the Free Software
 *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
 *  02110-1301, USA.
 * 
 */

#include "Loader.hxx"

extern "C"
{
#include <png.h>
#include <jpeglib.h>
}

void JpgLoader::Load(const std::string& filename,RGB& target,int x,int y)
{
	FILE *infile = fopen(filename.c_str(), "rb");
	if (!infile)
		throw LoaderException(std::string("failed to open ")+filename);

	struct jpeg_decompress_struct cinfo;
	struct jpeg_error_mgr jerr;
	cinfo.err = jpeg_std_error(&jerr);
	jpeg_create_decompress(&cinfo);
	jpeg_stdio_src(&cinfo, infile);
	jpeg_read_header(&cinfo, TRUE);
	jpeg_start_decompress(&cinfo);

	width = cinfo.output_width;
	height = cinfo.output_height;

	unsigned char *line =
		(unsigned char *) malloc(width *
										 cinfo.output_components);
	int k = 0;
	while (cinfo.output_scanline < height) {
		unsigned char* ptr = target.Data() + (y+k)*target.Width()*3 + x*3;
		jpeg_read_scanlines(&cinfo, &line, 1);
		unsigned char *jpeg_ptr = line;
		for (unsigned int i = 0; i < width; i++) {
			*ptr++ = *jpeg_ptr++;
			*ptr++ = *jpeg_ptr++;
			*ptr++ = *jpeg_ptr++;
		}
		k++;
	}
	free(line);
	jpeg_finish_decompress(&cinfo);
	jpeg_destroy_decompress(&cinfo);

	fclose(infile);
}

void JpgLoader::Load(const std::string& filename,RGBA& target,int x,int y)
{
	FILE *infile = fopen(filename.c_str(), "rb");
	if (!infile)
		throw LoaderException(std::string("failed to open ")+filename);

	struct jpeg_decompress_struct cinfo;
	struct jpeg_error_mgr jerr;
	cinfo.err = jpeg_std_error(&jerr);
	jpeg_create_decompress(&cinfo);
	jpeg_stdio_src(&cinfo, infile);
	jpeg_read_header(&cinfo, TRUE);
	jpeg_start_decompress(&cinfo);

	width = cinfo.output_width;
	height = cinfo.output_height;

	unsigned char *line =
		(unsigned char *) malloc(width *
										 cinfo.output_components);
	int k = 0;
	while (cinfo.output_scanline < height) {
		unsigned char* ptr = target.Data() + (y+k)*target.Width()*4 + x*4;
		jpeg_read_scanlines(&cinfo, &line, 1);
		unsigned char *jpeg_ptr = line;
		for (unsigned int i = 0; i < width; i++) {
			*ptr++ = *jpeg_ptr++;
			*ptr++ = *jpeg_ptr++;
			*ptr++ = *jpeg_ptr++;
			*ptr++ = 255;
		}
		k++;
	}
	free(line);
	jpeg_finish_decompress(&cinfo);
	jpeg_destroy_decompress(&cinfo);

	fclose(infile);
}

void JpgLoader::Merge(const std::string& filename,RGBA& target,int x,int y)
{
	FILE* infile = fopen(filename.c_str(), "rb");
	if (!infile)
		throw LoaderException(std::string("failed to open ")+filename);

	struct jpeg_decompress_struct cinfo;
	struct jpeg_error_mgr jerr;
	cinfo.err = jpeg_std_error(&jerr);
	jpeg_create_decompress(&cinfo);
	jpeg_stdio_src(&cinfo, infile);
	jpeg_read_header(&cinfo, TRUE);
	jpeg_start_decompress(&cinfo);

	unsigned char *line =
		(unsigned char *) malloc(cinfo.output_width *
										 cinfo.output_components);
	int k = 0;
	while (cinfo.output_scanline < cinfo.output_height) {
		unsigned char* ptr = target.Data() + (y+k)*target.Width()*4 + x*4;
		jpeg_read_scanlines(&cinfo, &line, 1);
		unsigned char *jpeg_ptr = line;
		for (unsigned int i = 0; i < cinfo.output_width; i++) {
			if (ptr[0] == 255 &&
					ptr[1] == 255 && ptr[2] == 255 && ptr[3] == 255) {
				*ptr++ = *jpeg_ptr++;
				*ptr++ = *jpeg_ptr++;
				*ptr++ = *jpeg_ptr++;
				ptr++;
			} else {
				ptr += 4;
				jpeg_ptr += 3;
			}
		}
		k++;
	}
	free(line);
	jpeg_finish_decompress(&cinfo);
	jpeg_destroy_decompress(&cinfo);

	fclose(infile);
}
void PngLoader::Load(const std::string& filename,RGBA& target)
{
	png_byte header[8];
	int number = 8;

	FILE *fp = fopen(filename.c_str(), "rb");
	if (!fp) {
		throw LoaderException(std::string("cannot open "+filename));
	}
	fread(header, 1, number, fp);
	int is_png = !png_sig_cmp(header, 0, number);
	if (!is_png) {
		fclose(fp);
		throw LoaderException(filename+" is not a png");
	}

	png_structp png_ptr = png_create_read_struct
								 (PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
	if (!png_ptr)
	{
		fclose(fp);
		throw LoaderException("png_create_read_struct failed");
	}
	png_infop info_ptr = png_create_info_struct(png_ptr);
	if (!info_ptr) {
		png_destroy_read_struct(&png_ptr,
										(png_infopp) NULL, (png_infopp) NULL);
		fclose(fp);
		throw LoaderException("png_create_info_struct failed");
	}

	png_infop end_info = png_create_info_struct(png_ptr);
	if (!end_info) {
		png_destroy_read_struct(&png_ptr, &info_ptr, (png_infopp) NULL);
		throw LoaderException("png_create_info_struct (end_info) failed");
	}
	if (setjmp(png_jmpbuf(png_ptr))) {
		png_destroy_read_struct(&png_ptr, &info_ptr, &end_info);
		fclose(fp);
		throw LoaderException("png_create_info_struct (end_info) failed");
	}
	png_init_io(png_ptr, fp);
	png_set_sig_bytes(png_ptr, number);
	png_uint_32 width, height;
	int bit_depth, color_type, interlace_type;
	png_read_info(png_ptr, info_ptr);
	png_get_IHDR(png_ptr, info_ptr, &width, &height, &bit_depth,
					 &color_type, &interlace_type, int_p_NULL, int_p_NULL);

	int row_bytes = png_get_rowbytes(png_ptr, info_ptr);

	png_bytep *row_pointers =
		(png_bytep *) png_malloc(png_ptr, height * sizeof(png_bytep));

	target.Alloc(width,height);

	png_bytep ptr = target.Data();
	for (unsigned int i = 0; i < height; i++) {
		row_pointers[i] = ptr;
		ptr += row_bytes;
	}
	png_read_image(png_ptr, row_pointers);
	png_read_end(png_ptr, NULL);
	png_free(png_ptr, row_pointers);
	png_destroy_read_struct(&png_ptr, &info_ptr, &end_info);
	fclose(fp);
}

