/*
	HEJPEG.C

	wxWindows image file (JPEG format) display routines, including the 
	main function called from the engine:

		hugo_displaypicture

	for the Hugo Engine

	Copyright (c) 1995-2006 by Kent Tessman
*/

#define USE_BILINEARSTRETCHBLT

#if !defined (NO_GRAPHICS)

#ifdef USE_BILINEARSTRETCHBLT
#include <math.h>
#endif

#include "hewx.h"

#include "wx/image.h"
#include "wx/wfstream.h"

#ifdef __WXMSW__
#undef GetProp
#endif

extern "C"
{
#include "heheader.h"

int hugo_displaypicture(FILE *infile);
}

wxFile *TrytoOpenwxFile(char *name, char *where);
int DisplaywxImage(wxImage *img);
int DisplayJPEG(wxFile *wxfile);

#ifdef USE_BILINEARSTRETCHBLT
bool BilinearStretchBlt(wxImage dest_img, wxImage src_img);
#endif

int hugo_hasgraphics(void)
{
	return graphics_enabled;
}

/* hugo_displaypicture

	(This is the only routine that gets called from the engine proper.)

	Assumes that "filename" is hot--i.e., opened and positioned--but
	closes it before returning.

	Loads and displays a JPEG picture, sizing it to the screen area
	described by the current text window.  If smaller than the
	current text window, the image should be centered, not stretched.

	Ultimately, graphic formats other than JPEG may be supported,
	hence the identification of the type flag and the switch
	at the end.

	Returns false if it fails because of an ERROR.
*/

int hugo_displaypicture(FILE *infile)
{
	long pos;
	wxFile *wxfile;
	
	/* So that graphics can be dynamically enabled/disabled */
	if (!hugo_hasgraphics())
	{
		fclose(infile);
		return true;		/* not an error */
	}

	// Reopen infile as a wxFile at the same position
	pos = ftell(infile);
	fclose(infile);
	// If the resource is not blank, we're using a resource file,
	// so uppercase the name
	if (strcpy(loaded_resname, "")) strupr(loaded_filename);
	if (!(wxfile = TrytoOpenwxFile(loaded_filename, (char *)"games")))
	{
		if (!(wxfile = TrytoOpenwxFile(loaded_filename, (char *)"object")))
		{
			return false;
		}
	}
	// Seek wxfile to the same position infile was at
	wxfile->Seek((off_t)pos);	

	/* Before doing any drawing, mainly because we need to make sure there
	   is no scroll_offset, or anything like that:
	*/
	canvas->Update(FALSE);

	switch (resource_type)
	{
		case JPEG_R:
			if (!DisplayJPEG(wxfile)) goto Failed;
			break;

		default:        /* unrecognized */
#if defined (DEBUGGER)
			SwitchtoDebugger();
			DebugMessageBox("Picture Loading Error", "Unrecognized graphic format");
			SwitchtoGame();
#endif
			goto Failed;
	}

	delete wxfile;
	return 1;	// success

Failed:
	delete wxfile;
	return 0;
}


/* TrytoOpenwxFile
 * 
 *   is similar to hemisc.c's TrytoOpen, but returns a wxFile.
 */

extern char gamepath[];  // from hemisc.c

wxFile *TrytoOpenwxFile(char *name, char *where)
{
	char drive[MAXDRIVE], dir[MAXDIR], fname[MAXFILENAME], ext[MAXEXT];
	char envvar[32];
	wxFile *tempfile;
	char temppath[MAXPATH];

	// Don't let wxWindows print standard not-found messages
	wxLogNull nolog;

	/* Try to open the given, vanilla filename */
	tempfile = new wxFile(wxString::FromAscii(name));
	if (tempfile->IsOpened())
		return tempfile;
	delete tempfile;

	hugo_splitpath(name, drive, dir, fname, ext);  /* file to open */
	
	/* If the given filename doesn't already specify where to find it */
	if (!strcmp(drive, "") || !strcmp(dir, ""))
	{
		/* Check gamefile directory */
		hugo_makepath(temppath, (char *)"", gamepath, fname, ext);
		tempfile = new wxFile(wxString::FromAscii(temppath));
		if (tempfile->IsOpened())
			return tempfile;
		delete tempfile;

		/* Check environment variables */
		strcpy(envvar, "hugo_");  /* make up the actual var. name */
		strcat(envvar, where);

		if (getenv(strupr(envvar)))
		{
			hugo_makepath(temppath, (char *)"", getenv(strupr(envvar)), fname, ext);

			tempfile = new wxFile(wxString::FromAscii(temppath));
			if (tempfile->IsOpened())
				return tempfile;
			delete tempfile;
		}
	}

	/* return NULL if not openable */
	return NULL;
}


/* DisplaywxImage
 * 
 *   is called by the completed image-loading routine (such as
 *   DisplayJPEG) in order to copy the loaded wxImage to dcMem's bitmap.
 */

int DisplaywxImage(wxImage *img)
{
	// First of all, find out if we're going to have to scale the
	// image down to fit in the defined window
	double scale = 1.0;
	int width = img->GetWidth();
	int height = img->GetHeight();
	if (width > physical_windowwidth)
		scale = (double)physical_windowwidth/(double)width;
	if ((double)height*scale > (double)physical_windowheight)
		scale = (double)physical_windowheight/(double)height;
	if (scale!=1.0)
	{
		width = (int)((double)width*scale);
		height = (int)((double)height*scale);
	}

	// Figure out where the bitmap should be centered
	int x = (physical_windowwidth-width)/2;
	int y = (physical_windowheight-height)/2;
	
// IMPORTANT:  The following calls to wxBitmap::wxBitmap(&wxImage) must come
// before the construction of dcMem, since under wxMac, DCs
// cannot overlap (since they'll throw off GWorld sync and rendering).

	if (scale==1.0)		// unscaled bitmap
	{
#if !wxCHECK_VERSION(2, 3, 2)
		wxBitmap bmp = img->ConvertToBitmap();
#else
		wxBitmap bmp(img);
#endif
		wxMemoryDC dcMem;
		if (bitmap) dcMem.SelectObject(*bitmap);
		dcMem.DrawBitmap(bmp, physical_windowleft+x, physical_windowtop+y, FALSE);
	}
	else			// scaled bitmap
	{
		wxImage img_scaled;
		
#ifdef USE_BILINEARSTRETCHBLT
		if (graphics_smoothing)
		{
			img_scaled = wxImage(width, height);
			BilinearStretchBlt(img_scaled, img); 
		}
		else
			img_scaled = img->Scale(width, height);
#else
		img_scaled = img->Scale(width, height);
#endif

#if !wxCHECK_VERSION(2, 3, 2)
		wxBitmap bmp = img_scaled.ConvertToBitmap();
#else
		wxBitmap bmp(img_scaled);
#endif
		wxMemoryDC dcMem;
		if (bitmap) dcMem.SelectObject(*bitmap);
		dcMem.DrawBitmap(bmp, physical_windowleft+x, physical_windowtop+y, FALSE);

	}
	
	return true;
}


/* DisplayJPEG */

int DisplayJPEG(wxFile *wxfile)
{
	wxLogNull nolog;
//	wxFileInputStream stream = wxFileInputStream(*wxfile);
//	wxImage img = wxImage(stream, wxBITMAP_TYPE_JPEG);
	wxFileInputStream *stream = new wxFileInputStream(*wxfile);
	wxImage img = wxImage(*stream, wxBITMAP_TYPE_JPEG);
	if (!img.Ok()) return false;
	delete stream;

	return DisplaywxImage(&img);
}


//---------------------------------------------------------------------------
// BilinearStretchBlt
//---------------------------------------------------------------------------

#ifdef USE_BILINEARSTRETCHBLT

inline wxColor BitmapPixelAt(unsigned char *src, int width, int x, int y)
{
	unsigned char r, g, b;
	r = *(src + y*width*3 + x*3+2);
	g = *(src + y*width*3 + x*3+1);
	b = *(src + y*width*3 + x*3);
	return wxColor(r, g, b);
}

inline void SetBitmapPixelAt(unsigned char *dest, int width, int x, int y, wxColor c)
{
	*(dest + y*width*3 + x*3+2) = c.Red();
	*(dest + y*width*3 + x*3+1) = c.Green();
	*(dest + y*width*3 + x*3)   = c.Blue();
}

bool BilinearStretchBlt(wxImage dest_img, wxImage src_img)
{
	unsigned char *dest, *src;
	int dest_width, dest_height, src_width, src_height;
	double nXFactor, nYFactor;
	double fraction_x, fraction_y, one_minus_x, one_minus_y;
	int ceil_x, ceil_y, floor_x, floor_y;
	wxColor c1, c2, c3, c4;
	unsigned char red, green, blue, b1, b2;
	int x, y;

	dest = dest_img.GetData();
	dest_width = dest_img.GetWidth();
	dest_height = dest_img.GetHeight();
	src = src_img.GetData();
	src_width = src_img.GetWidth();
	src_height = src_img.GetHeight();
	
	nXFactor = (double)src_width/(double)dest_width;
	nYFactor = (double)src_height/(double)dest_height;

	for (x=0; x<dest_width; x++)
	for (y=0; y<dest_height; y++)
	{
		floor_x = (int)floor(x * nXFactor);
		floor_y = (int)floor(y * nYFactor);
		ceil_x = floor_x + 1;
		if (ceil_x >= src_width) ceil_x = floor_x;
		ceil_y = floor_y + 1;
		if (ceil_y >= src_height) ceil_y = floor_y;
		fraction_x = x * nXFactor - floor_x;
		fraction_y = y * nYFactor - floor_y;
		one_minus_x = 1.0 - fraction_x;
		one_minus_y = 1.0 - fraction_y;

		c1 = BitmapPixelAt(src, src_width, floor_x, floor_y);
		c2 = BitmapPixelAt(src, src_width, ceil_x,  floor_y);
		c3 = BitmapPixelAt(src, src_width, floor_x, ceil_y);
		c4 = BitmapPixelAt(src, src_width, ceil_x,  ceil_y);

		// Blue
		b1 = (unsigned char)(one_minus_x * c1.Blue() + fraction_x * c2.Blue());
		b2 = (unsigned char)(one_minus_x * c3.Blue() + fraction_x * c4.Blue());
		blue = (unsigned char)(one_minus_y * (double)(b1) + fraction_y * (double)(b2));

		// Green
		b1 = (unsigned char)(one_minus_x * c1.Green() + fraction_x * c2.Green());
		b2 = (unsigned char)(one_minus_x * c3.Green() + fraction_x * c4.Green());
		green = (unsigned char)(one_minus_y * (double)(b1) + fraction_y * (double)(b2));

		// Red
		b1 = (unsigned char)(one_minus_x * c1.Red() + fraction_x * c2.Red());
		b2 = (unsigned char)(one_minus_x * c3.Red() + fraction_x * c4.Red());
		red = (unsigned char)(one_minus_y * (double)(b1) + fraction_y * (double)(b2));

		SetBitmapPixelAt(dest, dest_width, x, y, wxColor(red, green, blue));
	}

	return true;
}

#endif	// USE_BILINEARSTRETCHBLT

#else	// NO_GRAPHICS

extern "C"
{

#include "heheader.h"

int hugo_hasgraphics(void)
{
	return 0;
}

int hugo_displaypicture(FILE *infile)
{
	fclose(infile);
	return 1;	// not an error
}

}  // extern "C"

#endif  // NO_GRAPHICS
