#include "VexImage.h"
#include <FS/FS_Info.h>
#include <math.h>
#include <SYS/SYS_Math.h>

// The default return type on a sampling error
UT_Vector4 VexImage::sErrorValue = UT_Vector4(1.0f, 0.0f, 1.0f, 0.0f);

VexImage::VexImage(UT_String inImageName)
{
	mImageName    = inImageName;
	mLoadedBitmap = LoadBitmap(inImageName);
	if(mLoadedBitmap == NULL)
	{
		mInitialized = false;
		return;
	}

	// If we loaded the bitmap correctly, initialization was succesfull
	mInitialized = true;
}



VexImage::~VexImage()
{
	// On destruction, clear image data
	ClearImageData();
}



// Loads the bitmap in to memory
FIBITMAP* VexImage::LoadBitmap(const UT_String& inImageName)
{
	// See if the image exists
	FS_Info img_info = FS_Info(mImageName);
	if(!img_info.fileExists())
		return NULL;

	// Figure out the image format
	mFormat = FreeImage_GetFileType((const char*)mImageName);
	if(mFormat == FIF_UNKNOWN)
		return NULL;

	// Load the thing if we figured it out
	FIBITMAP* bitmap = FreeImage_Load(mFormat, (const char*)mImageName);
	if (bitmap  == NULL)
		return NULL;

	// Get the type from it
	mType = FreeImage_GetImageType(bitmap);
	if (mType == FIT_UNKNOWN)
		return NULL;

	// Sample some info
	mWidth	=	FreeImage_GetWidth(bitmap);
	mHeight =	FreeImage_GetHeight(bitmap);
	mBitPP	=	FreeImage_GetBPP(bitmap);
	mBytesPP =	FreeImage_GetLine(bitmap) / mWidth;

	// Return the loaded bitmap
	return bitmap;
}



// Samples the color value for a specific uv coordinate
UT_Vector4 VexImage::GetColorValue(const float& inU, const float& inV, const int& inMode, int* IOsuccess)
{
	if (!mInitialized || mLoadedBitmap == NULL)
	{
		*IOsuccess = 0;
		return sErrorValue;
	}
	
	float fu;		//Du (0-1)
	float fv;		//Dv (0-1)

	// If mode is wrap, mod input u and v
	if(inMode == 0)
	{
		fu = fmodf(inU, 1.0f);
		fv = fmodf(inV, 1.0f);

		// Complement negative coordinates
		if (fu < 0.0f)
			fu = 1.0f - SYSabs(fu);
		if (fv < 0.0f)
			fv = 1.0f - SYSabs(fv);
	}
	// Otherwise streak (clamp values)
	else
	{
		fu = SYSclamp(inU, 0.0f, 1.0f);
		fv = SYSclamp(inV, 0.0f, 1.0f);
	}

	// Output pixel values
	float output_pixels[4] = {0.0f, 0.0f, 0.0f, 0.0f};

	// Get the pixel value based on a bilinear sample pass
	SampleBilinear(fu, fv, mWidth, mHeight, mLoadedBitmap, output_pixels);

	// Success!
	*IOsuccess = 1;

	return UT_Vector4(output_pixels[0], output_pixels[1], output_pixels[2], output_pixels[3]);
}



// Interpolates 4 pixels based on a uv pixel coordinate
void VexImage::SampleBilinear(const float& inU, const float& inV, const unsigned& inWidth, const unsigned& inHeight, FIBITMAP* inBitmap, float* outValues)
{
	// Get image sample positions
	float fu = SYSclamp(inU * (float)inWidth  - 0.5f, 0.0f, (float)inWidth  - 1.0f);
	float fv = SYSclamp(inV * (float)inHeight - 0.5f, 0.0f, (float)inHeight - 1.0f);
	
	// Get Min
	float x = SYSfloor(fu);
	float y = SYSfloor(fv);

	// Get Ratio
	float u_ratio = fu - x;
	float v_ratio = fv - y;

	// Get pixel indices for lookup
	int px = (int)x;
	int py = (int)y;
	int tx = SYSclamp(px+1, 0, (int)inWidth -1);
	int ty = SYSclamp(py+1, 0, (int)inHeight-1);

	// Initialize pixel arrays
	float m_XY[4] = {0.0f, 0.0f, 0.0f, 0.0f};
	float m_Xy[4] = {0.0f, 0.0f, 0.0f, 0.0f};
	float m_xY[4] = {0.0f, 0.0f, 0.0f, 0.0f};
	float m_xy[4] = {0.0f, 0.0f, 0.0f, 0.0f};

	// Sample the corner pixel values
	GetPixelValues(px,	 py,		inBitmap, m_xy);
	GetPixelValues(tx,	 py,		inBitmap, m_Xy);
	GetPixelValues(px,	 ty,		inBitmap, m_xY);
	GetPixelValues(tx,	 ty,		inBitmap, m_XY);

	// Interpolate based on delta values
	for(int i=0; i < 4; i++)
	{
		float hy = ((1.0f - u_ratio) * m_xy[i]) + (u_ratio * m_Xy[i]);
		float hY = ((1.0f - u_ratio) * m_xY[i]) + (u_ratio * m_XY[i]);

		outValues[i] = ((1.0f - v_ratio) * hy) + (v_ratio * hY);
	}
}



void VexImage::ClearImageData()
{
	if (mLoadedBitmap != NULL)
		FreeImage_Unload(mLoadedBitmap);
	mLoadedBitmap = NULL;

	mWidth  = 0;
	mHeight = 0;
	mBitPP  = 0;
	mFormat = FIF_UNKNOWN;
	mType   = FIT_UNKNOWN;
}



// Assigns the value of a pixel in float(RGBA) to outValues, based on the data specified
void VexImage::GetPixelValues(const int& inX, const int& inY, FIBITMAP* inBitmap, float* outValues)
{
	// First sample the image type
	FREE_IMAGE_TYPE image_type = FreeImage_GetImageType(inBitmap);

	// Now return values based on sampled info
	if (image_type == FIT_BITMAP)
	{
		BYTE* bits = (BYTE*)FreeImage_GetScanLine(inBitmap, inY);
		bits += (inX * (FreeImage_GetLine(inBitmap) / FreeImage_GetWidth(inBitmap)));

		outValues[0] = (float)bits[FI_RGBA_RED]		/ VEX_IMG_08_UNSIGNED;
		outValues[1] = (float)bits[FI_RGBA_GREEN]	/ VEX_IMG_08_UNSIGNED;
		outValues[2] = (float)bits[FI_RGBA_BLUE]	/ VEX_IMG_08_UNSIGNED;
		outValues[3] = 1.0f;

		if (FreeImage_GetBPP(inBitmap) == 32)
			outValues[3] = (float)bits[FI_RGBA_ALPHA] / VEX_IMG_08_UNSIGNED;	
	}
	else if(image_type == FIT_RGB16)
	{
		FIRGB16 *bits = (FIRGB16*)FreeImage_GetScanLine(inBitmap, inY);
		outValues[0] = (float)(bits[inX].red)		/ VEX_IMG_08_UNSIGNED;
		outValues[1] = (float)(bits[inX].green)		/ VEX_IMG_08_UNSIGNED;
		outValues[2] = (float)(bits[inX].blue)		/ VEX_IMG_08_UNSIGNED;
		outValues[3] = 1.0f;
	}
	else if(image_type == FIT_RGBA16)
	{
		FIRGBA16 *bits = (FIRGBA16*)FreeImage_GetScanLine(inBitmap, inY);
		outValues[0] = (float)(bits[inX].red)		/ VEX_IMG_16_UNSIGNED;
		outValues[1] = (float)(bits[inX].green)		/ VEX_IMG_16_UNSIGNED;
		outValues[2] = (float)(bits[inX].blue)		/ VEX_IMG_16_UNSIGNED;
		outValues[3] = (float)(bits[inX].alpha)		/ VEX_IMG_16_UNSIGNED;
	}
	else if(image_type == FIT_UINT16)
	{
		unsigned short* bits = (unsigned short*)FreeImage_GetScanLine(inBitmap, inY);
		outValues[0] = (float)bits[inX]				/ VEX_IMG_16_UNSIGNED;
		outValues[1] = (float)bits[inX]				/ VEX_IMG_16_UNSIGNED;
		outValues[2] = (float)bits[inX]				/ VEX_IMG_16_UNSIGNED;
		outValues[3] = 1.0f;
	}
	else if(image_type == FIT_INT16)
	{
		short* bits = (short*)FreeImage_GetScanLine(inBitmap, inY);
		outValues[0] = (float)bits[inX]				/ VEX_IMG_16_SIGNED;
		outValues[1] = (float)bits[inX]				/ VEX_IMG_16_SIGNED;
		outValues[2] = (float)bits[inX]				/ VEX_IMG_16_SIGNED;	
		outValues[3] = 1.0f;

	}
	else if(image_type == FIT_UINT32)
	{
		unsigned long* bits = (unsigned long*)FreeImage_GetScanLine(inBitmap, inY);
		outValues[0] = (float)bits[inX]				/ VEX_IMG_32_UNSIGNED;
		outValues[1] = (float)bits[inX]				/ VEX_IMG_32_UNSIGNED;
		outValues[2] = (float)bits[inX]				/ VEX_IMG_32_UNSIGNED;
		outValues[3] = 1.0f;

	}
	else if(image_type == FIT_INT32)
	{
		long* bits = (long*)FreeImage_GetScanLine(inBitmap, inY);
		outValues[0] = (float)bits[inX]				/ VEX_IMG_32_SIGNED;
		outValues[1] = (float)bits[inX]				/ VEX_IMG_32_SIGNED;
		outValues[2] = (float)bits[inX]				/ VEX_IMG_32_SIGNED;	
		outValues[3] = 1.0f;

	}
	else if(image_type == FIT_RGBAF)
	{
		FIRGBAF *bits = (FIRGBAF*)FreeImage_GetScanLine(inBitmap, inY);
		outValues[0] = bits[inX].red;
		outValues[1] = bits[inX].green;
		outValues[2] = bits[inX].blue;
		outValues[3] = bits[inX].alpha;
	}
	else if(image_type == FIT_RGBF)
	{
		FIRGBF* bits = (FIRGBF*)FreeImage_GetScanLine(inBitmap, inY);
		outValues[0] = bits[inX].red;
		outValues[1] = bits[inX].green;
		outValues[2] = bits[inX].blue;
		outValues[3] = 1.0f;
	}
	else if(image_type == FIT_FLOAT)
	{
		float* bits = (float*)FreeImage_GetScanLine(inBitmap, inY);
		outValues[0] = bits[inX];
		outValues[1] = bits[inX];
		outValues[2] = bits[inX];
		outValues[3] = 1.0f;
	}
	else
	{
		outValues[0] = 1.0f;
		outValues[1] = 0.0f;
		outValues[2] = 0.0f;
		outValues[3] = 0.0f;
	}
}