//-----------------------------------------------------------------------------
//
// ImageLib Utility Sources
// Copyright (C) 2000-2002 by Denton Woods
// Last modified: 05/25/2002 <--Y2K Compliant! =]
//
// Filename: src-ILU/src/ilu_rotate.c
//
// Description: Rotates an image.
//
//-----------------------------------------------------------------------------


#include "ilu_internal.h"
#include "ilu_states.h"


ILboolean ILAPIENTRY iluRotate(ILfloat Angle)
{
	ILimage	*Temp, *Temp1, *CurImage = NULL;
	ILenum	PalType = 0;

	iluCurImage = ilGetCurImage();
	if (iluCurImage == NULL) {
		ilSetError(ILU_ILLEGAL_OPERATION);
		return IL_FALSE;
	}

	if (iluCurImage->Format == IL_COLOUR_INDEX) {
		PalType = iluCurImage->Pal.PalType;
		CurImage = iluCurImage;
		iluCurImage = iConvertImage(iluCurImage, ilGetPalBaseType(CurImage->Pal.PalType), IL_UNSIGNED_BYTE);
	}

	Temp = iluRotate_(iluCurImage, Angle);
	if (Temp != NULL) {
		if (PalType != 0) {
			ilCloseImage(iluCurImage);
			Temp1 = iConvertImage(Temp, IL_COLOUR_INDEX, IL_UNSIGNED_BYTE);
			ilCloseImage(Temp);
			Temp = Temp1;
			ilSetCurImage(CurImage);
		}
		ilTexImage(Temp->Width, Temp->Height, Temp->Depth, Temp->Bpp, Temp->Format, Temp->Type, Temp->Data);
		if (PalType != 0) {
			iluCurImage = ilGetCurImage();
			iluCurImage->Pal.PalSize = Temp->Pal.PalSize;
			iluCurImage->Pal.PalType = Temp->Pal.PalType;
			iluCurImage->Pal.Palette = (ILubyte*)ialloc(Temp->Pal.PalSize);
			if (iluCurImage->Pal.Palette == NULL) {
				ilCloseImage(Temp);
				return IL_FALSE;
			}
			memcpy(iluCurImage->Pal.Palette, Temp->Pal.Palette, Temp->Pal.PalSize);
		}

		iluCurImage->Origin = Temp->Origin;
		ilCloseImage(Temp);
		return IL_TRUE;
	}
	return IL_FALSE;
}


ILboolean ILAPIENTRY iluRotate3D(ILfloat x, ILfloat y, ILfloat z, ILfloat Angle)
{
	ILimage *Temp;

// return IL_FALSE;

	iluCurImage = ilGetCurImage();
	Temp = iluRotate3D_(iluCurImage, x, y, z, Angle);
	if (Temp != NULL) {
		ilTexImage(Temp->Width, Temp->Height, Temp->Depth, Temp->Bpp, Temp->Format, Temp->Type, Temp->Data);
		iluCurImage->Origin = Temp->Origin;
		ilSetPal(&Temp->Pal);
		ilCloseImage(Temp);
		return IL_TRUE;
	}
	return IL_FALSE;
}


//! Rotates a bitmap any angle.
//  Code help comes from http://www.leunen.com/cbuilder/rotbmp.html.
ILAPI ILimage* ILAPIENTRY iluRotate_(ILimage *Image, ILfloat Angle)
{
	ILimage		*Rotated = NULL;
	ILint		x, y, c;
	ILdouble	Cos, Sin;
	ILuint		RotOffset, ImgOffset;
	ILint		MinX, MinY, MaxX, MaxY;
	ILushort	*ShortPtr;
	ILuint		*IntPtr;
	ILdouble	*DblPtr;
	ILdouble	Point1x, Point1y, Point2x, Point2y, Point3x, Point3y;
	ILint		SrcX, SrcY;

	// Multiples of 90 are special.
	Angle = (ILfloat)fmod((ILdouble)Angle, 360.0);
	if (Angle < 0)
		Angle = 360.0f + Angle;

	Cos = (ILdouble)cos((IL_PI * Angle) / 180.0);
	Sin = (ILdouble)sin((IL_PI * Angle) / 180.0);

	Point1x = (-(ILint)Image->Height * Sin);
	Point1y = (Image->Height * Cos);
	Point2x = (Image->Width * Cos - Image->Height * Sin);
	Point2y = (Image->Height * Cos + Image->Width * Sin);
	Point3x = (Image->Width * Cos);
	Point3y = (Image->Width * Sin);

	MinX = (ILint)IL_MIN(0, IL_MIN(Point1x, IL_MIN(Point2x, Point3x)));
	MinY = (ILint)IL_MIN(0, IL_MIN(Point1y, IL_MIN(Point2y, Point3y)));
	MaxX = (ILint)IL_MAX(Point1x, IL_MAX(Point2x, Point3x));
	MaxY = (ILint)IL_MAX(Point1y, IL_MAX(Point2y, Point3y));

	Rotated = (ILimage*)icalloc(1, sizeof(ILimage));
	if (Rotated == NULL)
		return NULL;
	if (ilCopyImageAttr(Rotated, Image) == IL_FALSE) {
		ilCloseImage(Rotated);
		return NULL;
	}

	if (ilResizeImage(Rotated, (ILuint)ceil(fabs(MaxX) - MinX), (ILuint)ceil(fabs(MaxY) - MinY), 1, Image->Bpp, Image->Bpc) == IL_FALSE) {
		ilCloseImage(Rotated);
		return IL_FALSE;
	}

	ilClearImage_(Rotated);

	ShortPtr = (ILushort*)iluCurImage->Data;
	IntPtr = (ILuint*)iluCurImage->Data;
	DblPtr = (ILdouble*)iluCurImage->Data;

	//if (iluFilter == ILU_NEAREST) {
	switch (iluCurImage->Bpc)
	{
		case 1:  // Byte-based (most images)
			if (Angle == 90.0) {
				for (x = 0; x < (ILint)Image->Width; x++) {
					for (y = 0; y < (ILint)Image->Height; y++) {
						RotOffset = x * Rotated->Bps + (Image->Width - 1 - y) * Rotated->Bpp;
						ImgOffset = y * Image->Bps + x * Image->Bpp;
						for (c = 0; c < Rotated->Bpp; c++) {
							Rotated->Data[RotOffset + c] = Image->Data[ImgOffset + c];
						}
					} 
				} 
			}
			else if (Angle == 180.0) {
				for (x = 0; x < (ILint)Image->Width; x++) {
					for (y = 0; y < (ILint)Image->Height; y++) {
						RotOffset = (Image->Height - 1 - y) * Rotated->Bps + x * Rotated->Bpp;
						ImgOffset = y * Image->Bps + x * Image->Bpp;
						for (c = 0; c < Rotated->Bpp; c++) {
							Rotated->Data[RotOffset + c] = Image->Data[ImgOffset + c];
						}
					} 
				} 
			}
			else if (Angle == 270.0) {
				for (x = 0; x < (ILint)Image->Width; x++) {
					for (y = 0; y < (ILint)Image->Height; y++) {
						RotOffset = (Image->Height - 1 - x) * Rotated->Bps + y * Rotated->Bpp;
						ImgOffset = y * Image->Bps + x * Image->Bpp;
						for (c = 0; c < Rotated->Bpp; c++) {
							Rotated->Data[RotOffset + c] = Image->Data[ImgOffset + c];
						}
					} 
				} 
			}
			else {
				for (x = 0; x < (ILint)Rotated->Width; x++) {
					for (y = 0; y < (ILint)Rotated->Height; y++) {
						SrcX = (ILint)((x + MinX) * Cos + (y + MinY) * Sin);
						SrcY = (ILint)((y + MinY) * Cos - (x + MinX) * Sin);
						if (SrcX >= 0 && SrcX < (ILint)Image->Width && SrcY >= 0 && SrcY < (ILint)Image->Height) {
							RotOffset = y * Rotated->Bps + x * Rotated->Bpp;
							ImgOffset = (ILuint)SrcY * Image->Bps + (ILuint)SrcX * Image->Bpp;
							for (c = 0; c < Rotated->Bpp; c++) {
								Rotated->Data[RotOffset + c] = Image->Data[ImgOffset + c];
							}
						}
					}
				}
			}
			break;

		case 2:  // Short-based
			Image->Bps /= 2;   // Makes it easier to just
			Rotated->Bps /= 2; //   cast to short.

			if (Angle == 90.0) {
				for (x = 0; x < (ILint)Image->Width; x++) {
					for (y = 0; y < (ILint)Image->Height; y++) {
						RotOffset = x * Rotated->Bps + (Image->Width - 1 - y) * Rotated->Bpp;
						ImgOffset = y * Image->Bps + x * Image->Bpp;
						for (c = 0; c < Rotated->Bpp; c++) {
							((ILushort*)(Rotated->Data))[RotOffset + c] = ShortPtr[ImgOffset + c];
						}
					} 
				} 
			}
			else if (Angle == 180.0) {
				for (x = 0; x < (ILint)Image->Width; x++) {
					for (y = 0; y < (ILint)Image->Height; y++) {
						RotOffset = (Image->Height - 1 - y) * Rotated->Bps + x * Rotated->Bpp;
						ImgOffset = y * Image->Bps + x * Image->Bpp;
						for (c = 0; c < Rotated->Bpp; c++) {
							((ILushort*)(Rotated->Data))[RotOffset + c] = ShortPtr[ImgOffset + c];
						}
					} 
				} 
			}
			else if (Angle == 270.0) {
				for (x = 0; x < (ILint)Image->Width; x++) {
					for (y = 0; y < (ILint)Image->Height; y++) {
						RotOffset = (Image->Height - 1 - x) * Rotated->Bps + y * Rotated->Bpp;
						ImgOffset = y * Image->Bps + x * Image->Bpp;
						for (c = 0; c < Rotated->Bpp; c++) {
							((ILushort*)(Rotated->Data))[RotOffset + c] = ShortPtr[ImgOffset + c];
						}
					} 
				} 
			}
			else {
				for (x = 0; x < (ILint)Rotated->Width; x++) {
					for (y = 0; y < (ILint)Rotated->Height; y++) {
						SrcX = (ILint)((x + MinX) * Cos + (y + MinY) * Sin);
						SrcY = (ILint)((y + MinY) * Cos - (x + MinX) * Sin);
						if (SrcX >= 0 && SrcX < (ILint)Image->Width && SrcY >= 0 && SrcY < (ILint)Image->Height) {
							RotOffset = y * Rotated->Bps + x * Rotated->Bpp;
							ImgOffset = (ILuint)SrcY * Image->Bps + (ILuint)SrcX * Image->Bpp;
							for (c = 0; c < Rotated->Bpp; c++) {
								((ILushort*)(Rotated->Data))[RotOffset + c] = ShortPtr[ImgOffset + c];
							}
						}
					}
				}
			}
			Image->Bps *= 2;
			Rotated->Bps *= 2;
			break;

		case 4:  // Floats or 32-bit integers
			Image->Bps /= 4;
			Rotated->Bps /= 4;

			if (Angle == 90.0) {
				for (x = 0; x < (ILint)Image->Width; x++) {
					for (y = 0; y < (ILint)Image->Height; y++) {
						RotOffset = x * Rotated->Bps + (Image->Width - 1 - y) * Rotated->Bpp;
						ImgOffset = y * Image->Bps + x * Image->Bpp;
						for (c = 0; c < Rotated->Bpp; c++) {
							((ILuint*)(Rotated->Data))[RotOffset + c] = IntPtr[ImgOffset + c];
						}
					} 
				} 
			}
			else if (Angle == 180.0) {
				for (x = 0; x < (ILint)Image->Width; x++) {
					for (y = 0; y < (ILint)Image->Height; y++) {
						RotOffset = (Image->Height - 1 - y) * Rotated->Bps + x * Rotated->Bpp;
						ImgOffset = y * Image->Bps + x * Image->Bpp;
						for (c = 0; c < Rotated->Bpp; c++) {
							((ILuint*)(Rotated->Data))[RotOffset + c] = IntPtr[ImgOffset + c];
						}
					} 
				} 
			}
			else if (Angle == 270.0) {
				for (x = 0; x < (ILint)Image->Width; x++) {
					for (y = 0; y < (ILint)Image->Height; y++) {
						RotOffset = (Image->Height - 1 - x) * Rotated->Bps + y * Rotated->Bpp;
						ImgOffset = y * Image->Bps + x * Image->Bpp;
						for (c = 0; c < Rotated->Bpp; c++) {
							((ILuint*)(Rotated->Data))[RotOffset + c] = IntPtr[ImgOffset + c];
						}
					} 
				} 
			}
			else {
				for (x = 0; x < (ILint)Rotated->Width; x++) {
					for (y = 0; y < (ILint)Rotated->Height; y++) {
						SrcX = (ILint)((x + MinX) * Cos + (y + MinY) * Sin);
						SrcY = (ILint)((y + MinY) * Cos - (x + MinX) * Sin);
						if (SrcX >= 0 && SrcX < (ILint)Image->Width && SrcY >= 0 && SrcY < (ILint)Image->Height) {
							RotOffset = y * Rotated->Bps + x * Rotated->Bpp;
							ImgOffset = (ILuint)SrcY * Image->Bps + (ILuint)SrcX * Image->Bpp;
							for (c = 0; c < Rotated->Bpp; c++) {
								((ILuint*)(Rotated->Data))[RotOffset + c] = IntPtr[ImgOffset + c];
							}
						}
					}
				}
			}
			Image->Bps *= 4;
			Rotated->Bps *= 4;
			break;

		case 8:  // Double or 64-bit integers
			Image->Bps /= 8;
			Rotated->Bps /= 8;

			if (Angle == 90.0) {
				for (x = 0; x < (ILint)Image->Width; x++) {
					for (y = 0; y < (ILint)Image->Height; y++) {
						RotOffset = x * Rotated->Bps + (Image->Width - 1 - y) * Rotated->Bpp;
						ImgOffset = y * Image->Bps + x * Image->Bpp;
						for (c = 0; c < Rotated->Bpp; c++) {
							((ILdouble*)(Rotated->Data))[RotOffset + c] = DblPtr[ImgOffset + c];
						}
					} 
				} 
			}
			else if (Angle == 180.0) {
				for (x = 0; x < (ILint)Image->Width; x++) { 
					for (y = 0; y < (ILint)Image->Height; y++) { 
						RotOffset = (Image->Height - 1 - y) * Rotated->Bps + x * Rotated->Bpp;
						ImgOffset = y * Image->Bps + x * Image->Bpp;
						for (c = 0; c < Rotated->Bpp; c++) {
							((ILdouble*)(Rotated->Data))[RotOffset + c] = DblPtr[ImgOffset + c];
						}
					} 
				} 
			}
			else if (Angle == 270.0) {
				for (x = 0; x < (ILint)Image->Width; x++) {
					for (y = 0; y < (ILint)Image->Height; y++) {
						RotOffset = (Image->Height - 1 - x) * Rotated->Bps + y * Rotated->Bpp;
						ImgOffset = y * Image->Bps + x * Image->Bpp;
						for (c = 0; c < Rotated->Bpp; c++) {
							((ILdouble*)(Rotated->Data))[RotOffset + c] = DblPtr[ImgOffset + c];
						}
					} 
				} 
			}
			else {
				for (x = 0; x < (ILint)Rotated->Width; x++) {
					for (y = 0; y < (ILint)Rotated->Height; y++) {
						SrcX = (ILint)((x + MinX) * Cos + (y + MinY) * Sin);
						SrcY = (ILint)((y + MinY) * Cos - (x + MinX) * Sin);
						if (SrcX >= 0 && SrcX < (ILint)Image->Width && SrcY >= 0 && SrcY < (ILint)Image->Height) {
							RotOffset = y * Rotated->Bps + x * Rotated->Bpp;
							ImgOffset = (ILuint)SrcY * Image->Bps + (ILuint)SrcX * Image->Bpp;
							for (c = 0; c < Rotated->Bpp; c++) {
								((ILdouble*)(Rotated->Data))[RotOffset + c] = DblPtr[ImgOffset + c];
							}
						}
					}
				}
			}
			Image->Bps *= 8;
			Rotated->Bps *= 8;
			break;
	}

	return Rotated;
}


ILAPI ILimage* ILAPIENTRY iluRotate3D_(ILimage *Image, ILfloat x, ILfloat y, ILfloat z, ILfloat Angle)
{
	Image; x; y; z; Angle;
	return NULL;
}
