/*  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 "texture.h"
#include <png.h>

using namespace std;

namespace DS
{
	Texture::Texture()
	{
		
	}
	
	Texture::~Texture()
	{
		free(pData);
	}

	int Texture::Load(const char* pFile, bool makegl, bool mipmap)
	{
		m_mipmap = mipmap;
				
		// Open .png file
		FILE* pFileHandle = fopen(pFile, "rb");
		if(!pFileHandle) {
			cout << "[Texture::Load] Can't open file: " << pFile << endl;
			return 1;
		}
		
		// Read data
		// Should work with RGB/Gray/RGBA/PALETTE
		unsigned char header[8];
		png_structp png;
		png_infop   info, endinfo;
		png_bytep   *row_p;
		int color;
		
		fread(header, 1, 8, pFileHandle);
		if(!png_check_sig(header, 8))
		{
			cout << "[Texture::Load] Image is not png: " << pFile << endl;
			return 1;
		}
	
		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);
			cout << "[Texture::Load] Undefined error, setjmp: " << pFile << endl;
			return 1;
		}
	
		png_init_io(png, pFileHandle);
		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);

		// Find usable size
		/*int nwidth, nheight;
		bool wset = false, hset = false;
		for(int i = 1; i < 13; i++)
		{
			int n = (int)pow(2, (double)i);
			if((int)width <= n && !wset) {
				nwidth = n;
				wset = true;
			}
			if((int)height <= n && !hset) {
				nheight = n;
				hset = true;
			}
		}
		
		// Resize
		if(nwidth != (int)width && nheight != (int)height)
		{
			const int channels = png_get_rowbytes(png, info)/width;
			png_bytep d2 = (png_bytep) malloc(nwidth*nheight*channels);
			const float sx = (float) width/nwidth, sy = (float) height/nheight;
			int xx, yy, c;
			png_bytep d;
		
			for(int y = 0; y < nheight; y++)
			{
				yy = (int) (y*sy)*width;
				for(int x = 0; x < nwidth; x++)
				{
					xx = (int) (x*sx);
					d = pData + (yy+xx)*channels;
		
					for (c = 0; c < channels; c++)
						*d2++ = *d++;
				}
			}
			
			free(pData);
			pData = d2;
			width = nwidth;
			height = nheight;
		}*/		
		
		// 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;
			alpha = 0;
		}
		else if(color == PNG_COLOR_TYPE_GRAY_ALPHA || color == PNG_COLOR_TYPE_RGB_ALPHA)
		{
			m_colorflag = GL_RGBA;
			m_components = 4;
			alpha = 8;
		}
		else {
			cout << "[Texture::Load] Format flag missing?!" << endl;
			return 1;
		}
		
		png_read_end(png, endinfo);
		png_destroy_read_struct(&png, &info, &endinfo);
		
		// Close .png file
		if(fclose(pFileHandle))
		{
			cout << "[Texture::Load] Can't close file?!" << endl;
			return 1;
		}

		if(makegl)
			MakeGLTexture();			
		
		return 0;
	}
	
	GLuint Texture::MakeGLTexture()
	{
		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;
	}
}
