/****************************************************************************************


		MCColorRGB.h

		Copyright: (c) 2001 Eovia Corp. All rights reserved.

		Author:	Alec
		Date:	7/12/98

		Revision History

		when		who			what & why
		==========	==========	======================================================
		7/12/98		Alec		new today - classes for common color ops/math
		
*****************************************************************************************/

#ifndef ColorRGB_H
#define ColorRGB_H

#if CP_PRAGMA_ONCE
#pragma once
#endif

#include "MCBasicTypes.h"
#include "Real.h"
#include "MCMath.h"
#include "MCRGBColor.h"
#include "MCAssert.h"
#include "Vector2Fwd.h"

class TMCColorRGBA;
class TMCColorRGBA8;


/**********************************************************************\
*                                                                      *
*           Macro Definitions                                          *
*                                                                      *
\**********************************************************************/

// These macros are similar to the vector math operations.  The advantage here is
// that an RGBA color can be treated as 3 (RGB) or 4 (RGBA) components.

// RGB operations
#define macroRGBOp1(c1,op1,c2) 				\
		{									\
			(c1).red 	op1 (c2).red;		\
			(c1).green 	op1 (c2).green;		\
			(c1).blue 	op1 (c2).blue;		\
		}
		
#define macroRGBOp1Scalar(c1,op1,scalar) 	\
		{									\
			(c1).red 	op1 (scalar);		\
			(c1).green 	op1 (scalar);		\
			(c1).blue 	op1 (scalar);		\
		}

#define macroRGBOp2(c1,op1,c2,op2,c3) 					\
		{												\
			(c1).red 	op1 (c2).red 	op2 (c3).red;	\
			(c1).green 	op1 (c2).green 	op2 (c3).green;	\
			(c1).blue 	op1 (c2).blue 	op2 (c3).blue;	\
		}
		
#define macroRGBOp2Scalar(c1,op1,c2,op2,scalar) 		\
		{												\
			(c1).red 	op1 (c2).red 	op2 (scalar);	\
			(c1).green 	op1 (c2).green 	op2 (scalar);	\
			(c1).blue 	op1 (c2).blue 	op2 (scalar);	\
		}

// RGBA operations		
#define macroRGBAOp1(c1,op1,c2) 			\
		{									\
			(c1).red 	op1 (c2).red;		\
			(c1).green 	op1 (c2).green;		\
			(c1).blue 	op1 (c2).blue;		\
			(c1).alpha 	op1 (c2).alpha;		\
		}

#define macroRGBAOp1Scalar(c1,op1,scalar) 	\
		{									\
			(c1).red 	op1 (scalar);		\
			(c1).green 	op1 (scalar);		\
			(c1).blue 	op1 (scalar);		\
			(c1).alpha 	op1 (scalar);		\
		}
	
#define macroRGBAOp2(c1,op1,c2,op2,c3) 					\
		{												\
			(c1).red 	op1 (c2).red 	op2 (c3).red;	\
			(c1).green 	op1 (c2).green 	op2 (c3).green;	\
			(c1).blue 	op1 (c2).blue 	op2 (c3).blue;	\
			(c1).alpha 	op1 (c2).alpha 	op2 (c3).alpha;	\
		}

#define macroRGBAOp2Scalar(c1,op1,c2,op2,scalar) 	\
		{											\
			(c1).red 	op1 (c2).red 	op2 scalar;	\
			(c1).green 	op1 (c2).green 	op2 scalar;	\
			(c1).blue 	op1 (c2).blue 	op2 scalar;	\
			(c1).alpha 	op1 (c2).alpha 	op2 scalar;	\
		}


/// \addtogroup MCCore
/// \{

/// <P> TMCColorRGB is the floating point color used for most calculations since it 
/// can do interpolation without converting int-to-float. <BR>
/// Regular colors are supposed to be between zero (black) and one. </P>
///
/// <P> Other color classes are TMCColorRGBA, TMCColorRGBA32, TMCColorRGBA8</P>
class TMCColorRGB : public TMCObject0
{	
	public:
		static const real kMaxIntensity;		///< equals 1.0f
		static const real kInvMaxIntensity;		///< equals 1.0f/kMaxIntensity;
				
		static const TMCColorRGB kBlack; 		///< (0,0,0);
		static const TMCColorRGB kWhite;		///< (kMaxIntensity,kMaxIntensity,kMaxIntensity);
			
	public:
		// constructors/setters
		inline TMCColorRGB					(void);
		inline TMCColorRGB					(const real rr,	const real gg, const real bb);
		inline TMCColorRGB					(const TMCColorRGB& inColor);
		inline TMCColorRGB					(const TMCRGBColor& inColor);

		inline void Set						(const real rr, const real gg, const real bb);

		boolean IsEqual						(const TMCColorRGB& rhs, const real epsilon = 0.0) const;
		
		// magnitudes
		inline real GetMagnitude			(void) const;
		inline real GetMagnitudeSquared		(void) const;
		
		// find the min and max intensity
		inline real GetMinOfRGB				(void) const;
		inline real GetMaxOfRGB				(void) const;
		
		// see if the color falls in between [0, 1]	- mainly for debug		
		boolean		IsClamped				(void) const;
		
		// clamp the color value to between [0, 1]
		void		Clamp					(void);
		
		// linear/bilinear interpolation, interpolants must be betwen 0 and 1
		void		Interpolate				(const TMCColorRGB&	from, const TMCColorRGB& to, const real	interpolant);
		void		Interpolate				(const TVector2& interpolants, // x [left/right], y [lower/upper]
											 const TMCColorRGB& lowerLeft,
											 const TMCColorRGB& lowerRight,
											 const TMCColorRGB& upperLeft,
											 const TMCColorRGB& upperRight);
		
		// color accumulation is used so often and we want to take advantage of fmul-add
		// this is equiv to  *this += rhs * scalar, no clamping is done
		inline void AccumRGB				(const TMCColorRGB&	rhs, // alpha ignored
											 const real	scalar );  
				
		// these are operators on all the components
		inline boolean		operator==		(const TMCColorRGB& rhs ) const;
		inline boolean		operator!=		(const TMCColorRGB& rhs ) const;
		inline boolean		operator<		(const real t) { return red<t || green<t || blue<t; }
		inline boolean		operator>		(const real t) { return red>t || green>t || blue>t; }
		
		inline TMCColorRGB& operator+=		(const TMCColorRGB& rhs);
		inline TMCColorRGB& operator-=		(const TMCColorRGB& rhs);
		inline TMCColorRGB& operator*=		(const real s);
		inline TMCColorRGB& operator/=		(const real s);
	
		inline TMCColorRGB	operator+		(const TMCColorRGB& rhs) const;
		inline TMCColorRGB	operator-		(const TMCColorRGB& rhs) const;
		inline TMCColorRGB	operator*		(const real s) const;
		inline TMCColorRGB	operator/		(const real s) const;

		// dot, term-by-term product
		inline real			operator*		(const TMCColorRGB& rhs) const;
		inline TMCColorRGB  operator%		(const TMCColorRGB& rhs) const;
		
		// conversion operations to the other colors
		TMCColorRGBA8&		ToRGBA8			(TMCColorRGBA8&	c, const uint8 alphaValue) const;
		TMCColorRGBA8&		ToRGBA8Unclamped(TMCColorRGBA8& c, const uint8 alphaValue) const;
				
		TMCColorRGBA&		ToRGBA			(TMCColorRGBA& 	c, const float alphaValue) const;
		
		inline void			FromTMCRGBColor	(const TMCRGBColor& inColor);
		inline void			ToTMCRGBColor	(TMCRGBColor&		outColor) const;

		// convertion from other colors
		inline TMCColorRGB& operator=		(class TMCColorRGBA &c);

		inline real& operator[](int32 i) {  MCAssert(i>=0 && i<=3); return ((real*)this)[i]; }
		inline const real operator[](int32 i) const {  MCAssert(i>=0 && i<=3); return ((real*)this)[i]; }

		// returns the address of first component in the structure
		inline const real* GetAddrFirstComponent(void) const;

		// return the intensity of the color
		inline real Intensity() const { return 0.1f*red+0.6f*green+0.3f*blue; }

		// ****************************************************************************
		//								GetHLS
		// 
		// -Description:
		//		return the color value in HLS. H, L and S are between 0 and 1.
		// -Parameters:
		//		the returned values of H, L and S.
		void GetHLS( real32& H, real32& L, real32& S ) const;

		// ****************************************************************************
		//								SetHLS
		// 
		// -Description:
		//		set the color from HLS values
		// -Parameters:
		//		the values of H, L and S
		void SetHLS( const real32 h, const real32 l, const real32 s );
		 
	public:
		// in order to facilitate a simpler move away from old colors
		// do not use R,G,B,A in new code (use the full names)
		union { real red; 	real R; };
		union { real green;	real G; };
		union { real blue;	real B; };
};



class TMCColorSignedRGB8 : public TMCObject0
{
public:
	inline TMCColorSignedRGB8(void);
	inline TMCColorSignedRGB8(const int32 rr, const int32 gg, const int32 bb);
	inline TMCColorSignedRGB8(const TMCColorRGB& color);

	inline void Set(const int32 rr, const int32 gg, const int32 bb);
	inline void Set(const TMCColorRGB& color);

	inline int32	Red() const;
	inline int32	Green() const;
	inline int32	Blue() const;

	inline boolean operator == (const TMCColorSignedRGB8 rhs) const;
	inline boolean operator != (const TMCColorSignedRGB8 rhs) const;

	void	Interpolate(const TMCColorSignedRGB8& from, const TMCColorSignedRGB8& to, const real interpolant);

protected:
#if MC_LITTLE_ENDIAN
	uint8	blue;
	uint8	green;
	uint8	red;
	uint8	sign;
#elif MC_BIG_ENDIAN
	uint8	sign;
	uint8	red;
	uint8	green;
	uint8	blue;
#else
		#error - unknown platform
#endif
};


class TMCColorRGBE8 : public TMCObject0
{
public:
	inline TMCColorRGBE8(void);
	inline TMCColorRGBE8(const int32 rr, const int32 gg, const int32 bb);
	inline TMCColorRGBE8(const TMCColorRGB& color);

	inline void Set(const int32 rr, const int32 gg, const int32 bb);
	inline void Set(const TMCColorRGB& color);

	inline int32	Red() const;
	inline int32	Green() const;
	inline int32	Blue() const;

	inline boolean operator == (const TMCColorRGBE8 rhs) const;
	inline boolean operator != (const TMCColorRGBE8 rhs) const;

	void	Interpolate(const TMCColorRGBE8& from, const TMCColorRGBE8& to, const real interpolant);

protected:
#if MC_LITTLE_ENDIAN
	uint8	blue;
	uint8	green;
	uint8	red;
	uint8	exponent;
#elif MC_BIG_ENDIAN
	uint8	exponent;
	uint8	red;
	uint8	green;
	uint8	blue;
#else
	#error - unknown platform
#endif
};


/// \}





inline TMCColorRGB::TMCColorRGB	(void) { }
inline TMCColorRGB::TMCColorRGB	(const real rr, const real gg, const real bb )
								: red( rr ), green( gg ), blue( bb )
{ }

inline TMCColorRGB::TMCColorRGB	(const TMCRGBColor& inColor)
{
	FromTMCRGBColor(inColor);
}
inline TMCColorRGB::TMCColorRGB(const TMCColorRGB& inColor):red(inColor.red), green(inColor.green), blue(inColor.blue)
{

}
inline void TMCColorRGB::FromTMCRGBColor(const TMCRGBColor& inColor)
{
	red		= real(inColor.rr) / real(TMCRGBColor::sWhite.rr);
	green	= real(inColor.gg) / real(TMCRGBColor::sWhite.gg);
	blue	= real(inColor.bb) / real(TMCRGBColor::sWhite.bb);
}

inline void TMCColorRGB::ToTMCRGBColor(TMCRGBColor& outColor) const
{
	outColor.rr	= (uint16) (red		* TMCRGBColor::sWhite.rr);
	outColor.gg	= (uint16) (green	* TMCRGBColor::sWhite.gg);
	outColor.bb	= (uint16) (blue	* TMCRGBColor::sWhite.bb);
}

inline void TMCColorRGB::Set					(const real rr, const real gg, const real bb)
	{ red = rr; green = gg; blue = bb; }

inline real TMCColorRGB::GetMagnitude			(void) const
	{ return RealSqrt( GetMagnitudeSquared() ); }

inline real TMCColorRGB::GetMagnitudeSquared	(void) const
	{ return red * red + green * green + blue * blue; }
		
inline real TMCColorRGB::GetMinOfRGB			(void) const
	{ real min = ( red < green ); return ( min < blue ) ? min : blue; }

inline real TMCColorRGB::GetMaxOfRGB			(void) const
	{ real min = ( red > green ); return ( min > blue ) ? min : blue; }

inline boolean TMCColorRGB::operator==			(const TMCColorRGB& rhs) const 
	{ return IsEqual( rhs ); }

inline boolean TMCColorRGB::operator!=			(const TMCColorRGB& rhs) const 
	{ return !IsEqual( rhs ); }
		
inline TMCColorRGB& TMCColorRGB::operator+= (const TMCColorRGB& rhs)
 { { (*this).red += (rhs).red; (*this).green += (rhs).green; (*this).blue += (rhs).blue; }; return *this; }

inline TMCColorRGB& TMCColorRGB::operator-= (const TMCColorRGB& rhs)
 { { (*this).red -= (rhs).red; (*this).green -= (rhs).green; (*this).blue -= (rhs).blue; }; return *this; }

inline TMCColorRGB& TMCColorRGB::operator*= (const real s)
 { { (*this).red *= (s); (*this).green *= (s); (*this).blue *= (s); }; return *this; }

inline TMCColorRGB& TMCColorRGB::operator/= (const real s)
 { { (*this).red /= (s); (*this).green /= (s); (*this).blue /= (s); }; return *this; }

inline TMCColorRGB TMCColorRGB::operator+ (const TMCColorRGB& rhs) const
 { return TMCColorRGB( *this ) += rhs; }
inline TMCColorRGB TMCColorRGB::operator- (const TMCColorRGB& rhs) const
 { return TMCColorRGB( *this ) -= rhs; }
inline TMCColorRGB TMCColorRGB::operator* (const real s) const
 { return TMCColorRGB( *this ) *= s; }
inline TMCColorRGB TMCColorRGB::operator/ (const real s) const
 { return TMCColorRGB( *this ) /= s; }

inline real TMCColorRGB::operator* (const TMCColorRGB& rhs) const
 { return red * rhs.red + green * rhs.green + blue * rhs.blue; }

inline TMCColorRGB TMCColorRGB::operator% (const TMCColorRGB& rhs) const
 { return TMCColorRGB( red * rhs.red, green * rhs.green, blue * rhs.blue ); }

inline void TMCColorRGB::AccumRGB (const TMCColorRGB& rhs, const real scalar )
 { { (*this).red += (rhs).red * (scalar); (*this).green += (rhs).green * (scalar); (*this).blue += (rhs).blue * (scalar); }; }

const real* TMCColorRGB::GetAddrFirstComponent (void) const
 { return &red; }



inline TMCColorSignedRGB8::TMCColorSignedRGB8()
{
}

inline TMCColorSignedRGB8::TMCColorSignedRGB8(const int32 rr, const int32 gg, const int32 bb)
{
	Set(rr, gg, bb);
}

inline TMCColorSignedRGB8::TMCColorSignedRGB8(const TMCColorRGB& color)
{
	Set(color);
}

inline void TMCColorSignedRGB8::Set(const int32 rr, const int32 gg, const int32 bb)
{
	sign = 0;

	if (rr < 0) { red = -rr; sign |= 0x01; }
	else { red = rr; }

	if (gg < 0) { green = -gg; sign |= 0x02; }
	else { green = gg; }

	if (bb < 0) { blue = -bb; sign |= 0x04; }
	else { blue = bb; }
}

inline void TMCColorSignedRGB8::Set(const TMCColorRGB& color)
{
	sign = 0;

	if (color.red < 0) { red = MC_Clamp(-color.red, 0, 1)*255; sign |= 0x01; }
	else { red = MC_Clamp(color.red, 0, 1)*255; }

	if (color.green < 0) { green = MC_Clamp(-color.green, 0, 1)*255; sign |= 0x02; }
	else { green = MC_Clamp(color.green, 0, 1)*255; }

	if (color.blue < 0) { blue = MC_Clamp(-color.blue, 0, 1)*255; sign |= 0x04; }
	else { blue = MC_Clamp(color.blue, 0, 1)*255; }
}

inline int32 TMCColorSignedRGB8::Red() const
{
	if (sign&0x01) return -red;
	else return red;
}

inline int32 TMCColorSignedRGB8::Green() const
{
	if (sign&0x02) return -green;
	else return green;
}

inline int32 TMCColorSignedRGB8::Blue() const
{
	if (sign&0x04) return -blue;
	else return blue;
}

inline boolean TMCColorSignedRGB8::operator == (const TMCColorSignedRGB8 rhs) const
{ return *(uint32*)this == *(uint32*)&rhs; }

inline boolean TMCColorSignedRGB8::operator != (const TMCColorSignedRGB8 rhs) const
{ return *(uint32*)this != *(uint32*)&rhs; }



inline TMCColorRGBE8::TMCColorRGBE8()
{
}

inline TMCColorRGBE8::TMCColorRGBE8(const int32 rr, const int32 gg, const int32 bb)
{
	Set(rr, gg, bb);
}

inline TMCColorRGBE8::TMCColorRGBE8(const TMCColorRGB& color)
{
	Set(color);
}

inline void TMCColorRGBE8::Set(const int32 rr, const int32 gg, const int32 bb)
{
	exponent = MC_Clamp((MC_Max(rr, gg, bb)+254)/255, 1, 255);

	red = rr/exponent;
	green = gg/exponent;
	blue = bb/exponent;
}

inline void TMCColorRGBE8::Set(const TMCColorRGB& color)
{
	Set(MC_Clamp(color.red, 0, 255)*255, MC_Clamp(color.green, 0, 255)*255, MC_Clamp(color.blue, 0, 255)*255);
}

inline int32 TMCColorRGBE8::Red() const
{
	return red*exponent;
}

inline int32 TMCColorRGBE8::Green() const
{
	return green*exponent;
}

inline int32 TMCColorRGBE8::Blue() const
{
	return blue*exponent;
}

inline boolean TMCColorRGBE8::operator == (const TMCColorRGBE8 rhs) const
{ return *(uint32*)this == *(uint32*)&rhs; }

inline boolean TMCColorRGBE8::operator != (const TMCColorRGBE8 rhs) const
{ return *(uint32*)this != *(uint32*)&rhs; }

	
#endif