////////////////////////////////////////////////////////////////////////////////
//    Scorched3D (c) 2000-2003
//
//    This file is part of Scorched3D.
//
//    Scorched3D 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.
//
//    Scorched3D 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 Scorched3D; if not, write to the Free Software
//    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
////////////////////////////////////////////////////////////////////////////////

#include <stdio.h>
#include <math.h>
#include <GLEXT/GLState.h>
#include <GLEXT/GLBitmap.h>
#include <common/Defines.h>
#include <SDL/SDL.h>

GLBitmap::GLBitmap() :
	width_(0), height_(0), alpha_(false), newbits_(0)
{

}

GLBitmap::GLBitmap(int startWidth, int startHeight, bool alpha, unsigned char fill) : 
	width_(startWidth), height_(startHeight), alpha_(alpha), newbits_(0)
{
	createBlank(startWidth, startHeight, alpha, fill);
}

GLBitmap::GLBitmap(const char * filename, bool alpha) :
	newbits_(0), width_(0), height_(0), alpha_(alpha)
{
	if (filename)
	{
		loadFromFile(filename, alpha);
	}
}

GLBitmap::GLBitmap(const char * filename, const char *alphafilename, bool invert) : 
	newbits_(0), width_(0), height_(0), alpha_(false)
{
	GLBitmap bitmap(filename, false);
	GLBitmap alpha(alphafilename, false);

	if (bitmap.getBits() && alpha.getBits() && 
		bitmap.getWidth() == alpha.getWidth() &&
		bitmap.getHeight() == alpha.getHeight())
	{
		createBlank(bitmap.getWidth(), bitmap.getHeight(), true);
		GLubyte *bbits = bitmap.getBits();
		GLubyte *abits = alpha.getBits();
		GLubyte *bits = getBits();
		for (int y=0; y<bitmap.getHeight(); y++)
		{
			for (int x=0; x<bitmap.getWidth(); x++)
			{
				bits[0] = bbits[0];
				bits[1] = bbits[1];
				bits[2] = bbits[2];

				GLubyte avg = GLubyte(int(abits[0] + abits[1] + abits[2]) / 3);
				if (invert)
				{
					bits[3] = GLubyte(255 - avg);
				}
				else
				{
					bits[3] = avg;
				}

				bbits += 3;
				abits += 3;
				bits += 4;
			}
		}
	}
}

GLBitmap::~GLBitmap()
{
	clear();
}

void GLBitmap::resize(int newWidth, int newHeight)
{
	if (!newbits_) return;

	if (width_ == newWidth &&
		height_ == newHeight)
	{
		return;
	}

	GLubyte *oldbits = newbits_;
	int oldWidth = width_;
	int oldHeight = height_;
	newbits_ = 0;

	createBlank(newWidth, newHeight, alpha_);
	int result = gluScaleImage(
		(alpha_?GL_RGBA:GL_RGB), 
		oldWidth, oldHeight,
		GL_UNSIGNED_BYTE, oldbits,
		newWidth, newHeight, 
		GL_UNSIGNED_BYTE, newbits_);
	if (result != 0)
	{
		const char *error = (const char *) gluErrorString(result);
		dialogExit("gluScaleImage", error);
	}

	delete [] oldbits;
}

void GLBitmap::createBlank(int width, int height, bool alpha, unsigned char fill)
{
	clear();
	width_ = width;
	height_ = height;
	alpha_ = alpha;
	int bitsize = getComponents() * width * height;

	newbits_ = new GLubyte[bitsize];
	memset(newbits_, fill, bitsize);
}

void GLBitmap::clear()
{
	delete [] newbits_;
	newbits_ = 0;
	width_ = 0;
	height_ = 0;
}

void GLBitmap::alphaMult(float mult)
{
	if (!getAlpha()) return;
	
	GLubyte *bits = getBits();
	for (int y=0; y<getHeight(); y++)
	{
		for (int x=0; x<getWidth(); x++)
		{
			float a = float(bits[3]) * mult;
			a = MAX(a, 255);
			a = MIN(a, 0);
			bits[3] = GLubyte(a);
		}
	}
}

bool GLBitmap::loadFromFile(const char * filename, bool alpha)
{
	SDL_Surface *image = SDL_LoadBMP(filename);
	if (!image) return false;

	if (image->format->BitsPerPixel != 24)
	{
		dialogExit("GLBitmap",
			formatString("ERROR: Bitmap \"%s\" is not encoded as a 24bit bitmap",
				filename));
	}

	// Create the internal byte array
	createBlank(image->w, image->h, alpha);

	// Convert the returned bits from BGR to RGB
	// and flip the verticle scan lines
	GLubyte *from = (GLubyte *) image->pixels;
	for (int i=0; i<height_; i ++)
	{
		GLubyte *destRow = ((GLubyte *) newbits_) + ((height_ - i - 1) * (width_ * getComponents()));
		for (int j=0; j<width_; j++)
		{
			GLubyte *dest = destRow + (j * getComponents());

			dest[0] = from[2];
			dest[1] = from[1];
			dest[2] = from[0];
			if (alpha)
			{
				dest[3] = (GLubyte)(from[0]+from[1]+from[2]==0?0:255);
			}
			
			from+=3;
		}
	}
	
	SDL_FreeSurface(image);
	return true;
}

bool GLBitmap::writeToFile(const char * filename)
{
	if (!newbits_) return false;

	GLubyte *brgbits = new GLubyte[width_ * height_ * 3];

	// Convert the returned bits from RGB to BGR
	// and flip the verticle scan lines
	GLubyte *from = (GLubyte *) newbits_;
	for (int i=0; i<height_; i ++)
	{
		GLubyte *destRow = ((GLubyte *) brgbits) + ((height_ - i - 1) * (width_ * 3));
		for (int j=0; j<width_; j++)
		{
			GLubyte *dest = destRow + (j * getComponents());

			dest[0] = from[0];
			dest[1] = from[1];
			dest[2] = from[2];
			from+=getComponents();
		}
	}

    /* SDL interprets each pixel as a 32-bit number, so our masks must depend
       on the endianness (byte order) of the machine */
    Uint32 rmask, gmask, bmask;
#if SDL_BYTEORDER == SDL_BIG_ENDIAN
    rmask = 0xff000000;
    gmask = 0x00ff0000;
    bmask = 0x0000ff00;
#else
    rmask = 0x000000ff;
    gmask = 0x0000ff00;
    bmask = 0x00ff0000;
#endif

	SDL_Surface *image = SDL_CreateRGBSurface( 
		SDL_SWSURFACE, width_, height_, 24, rmask, gmask, bmask, 0);
	memcpy(image->pixels, brgbits, width_ * height_ * 3);

	if (!image) return false;

	SDL_SaveBMP(image, filename);
    SDL_FreeSurface (image);

	delete [] brgbits;
	return true;
}

void GLBitmap::grabScreen()
{
	GLint		viewport[4];		/* Current viewport */
	glGetIntegerv(GL_VIEWPORT, viewport);
	createBlank(viewport[2], viewport[3], false);

	glFinish();				/* Finish all OpenGL commands */
	glPixelStorei(GL_PACK_ALIGNMENT, 4);	/* Force 4-byte alignment */
	glPixelStorei(GL_PACK_ROW_LENGTH, 0);
	glPixelStorei(GL_PACK_SKIP_ROWS, 0);
	glPixelStorei(GL_PACK_SKIP_PIXELS, 0);

	glReadPixels(0, 0, width_, height_, GL_RGB, GL_UNSIGNED_BYTE, newbits_);
}
