/*  DS
 *  Copyright (C) Joakim Kolsjö and Anders Asplund 2005
 *	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., 59 Temple Place, Suite 330, Boston, MA  02111-1307 
 */ 
 
#include "load_png.h"

using namespace std;

namespace DS
{
	// Callback for reading png data from ZZIP_FILE handles
	#define NEAR_BUF_SIZE 1024
	#define MIN(a,b) (a <= b ? a : b)
	
	static void cbLoadData(png_structp png_ptr, png_bytep data, png_size_t length)
	{
		((MemFile*)png_ptr->io_ptr)->Read(data, length);
	}
	
	Image::Image()
	{
		pData = 0;
	}
	
	Image::~Image()
	{
		if(pData)
			free(pData);
	}
	
	void Image::Load(const char* pFile, MemFile* pMemFile)
	{
		static char* fn = "[Image::Load]";
		m_mipmap = false;

		// Read data
		unsigned char header[8];
		png_structp png;
		png_infop   info, endinfo;
		png_bytep   *row_p;
		int color;

		pMemFile->Read(header, 8);
		if(!png_check_sig(header, 8))
			throw Exception(fn, (string)"Image is not png: " + pFile + ".");
	
		png = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
		info = png_create_info_struct(png);
		endinfo = png_create_info_struct(png);
	
		if(setjmp(png->jmpbuf)) {
			png_destroy_read_struct(&png, &info, &endinfo);
			throw Exception(fn, (string)"Undefined error, setjmp: " + pFile + ".");
		}
		
		png_set_read_fn(png, (png_voidp)pMemFile, cbLoadData);
		png_set_sig_bytes(png, 8);
		png_read_info(png, info);
		png_get_IHDR(png, info, &width, &height, &depth, &color, NULL, NULL, NULL);

		if(color == PNG_COLOR_TYPE_GRAY || color == PNG_COLOR_TYPE_GRAY_ALPHA)
			png_set_gray_to_rgb(png);
		
		if(color == PNG_COLOR_TYPE_PALETTE)
			png_set_expand(png);
			
		png_read_update_info(png, info);
	
		pData = (png_bytep) malloc(png_get_rowbytes(png, info)*height);
		row_p = (png_bytep *) malloc(sizeof(png_bytep)*height);
	
		for(png_uint_32 i = 0; i < height; i++) {
			row_p[height - 1 - i] = &pData[png_get_rowbytes(png, info)*i];
		}
	
		png_read_image(png, row_p);
		free(row_p);
	
		// RGB or RGBA?
		if(color == PNG_COLOR_TYPE_RGB || color == PNG_COLOR_TYPE_GRAY
			|| color == PNG_COLOR_TYPE_PALETTE)
		{
			m_colorflag = GL_RGB;
			m_components = 3;
			light = 0;
		}
		else if(color == PNG_COLOR_TYPE_GRAY_ALPHA || color == PNG_COLOR_TYPE_RGB_ALPHA)
		{
			m_colorflag = GL_RGBA;
			m_components = 4;
			light = 8;
		}
		else
			throw Exception(fn, "Format flag missing?!");
		
		png_read_end(png, endinfo);
		png_destroy_read_struct(&png, &info, &endinfo);
	}
	
	GLuint Image::MakeGL()
	{
		GLuint id;
		glGenTextures(1, &id);
		glBindTexture(GL_TEXTURE_2D, id);

		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
		if(!m_mipmap)
			glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
		else
			glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_NEAREST);	

		if(!m_mipmap)
			glTexImage2D(GL_TEXTURE_2D, 0, m_components, width,
				height, 0, m_colorflag, GL_UNSIGNED_BYTE, pData);
		else
			gluBuild2DMipmaps(GL_TEXTURE_2D, m_components, width,
				height, m_colorflag, GL_UNSIGNED_BYTE, pData);
		
		return id;		
	}
}
