/** 
 * @file v3color.h
 * @brief LLColor3 class header file.
 *
 * $LicenseInfo:firstyear=2001&license=viewergpl$
 * 
 * Copyright (c) 2001-2008, Linden Research, Inc.
 * 
 * Second Life Viewer Source Code
 * The source code in this file ("Source Code") is provided by Linden Lab
 * to you under the terms of the GNU General Public License, version 2.0
 * ("GPL"), unless you have obtained a separate licensing agreement
 * ("Other License"), formally executed by you and Linden Lab.  Terms of
 * the GPL can be found in doc/GPL-license.txt in this distribution, or
 * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
 * 
 * There are special exceptions to the terms and conditions of the GPL as
 * it is applied to this Source Code. View the full text of the exception
 * in the file doc/FLOSS-exception.txt in this software distribution, or
 * online at http://secondlifegrid.net/programs/open_source/licensing/flossexception
 * 
 * By copying, modifying or distributing this software, you acknowledge
 * that you have read and understood your obligations described above,
 * and agree to abide by those obligations.
 * 
 * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
 * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
 * COMPLETENESS OR PERFORMANCE.
 * $/LicenseInfo$
 */

#ifndef LL_V3COLOR_H
#define LL_V3COLOR_H

class LLColor4;
class LLVector4;

#include "llerror.h"
#include "llmath.h"
#include "llsd.h"

//  LLColor3 = |r g b|

static const U32 LENGTHOFCOLOR3 = 3;

class LLColor3
{
public:
	F32 mV[LENGTHOFCOLOR3];

	static LLColor3 white;
	static LLColor3 black;
	static LLColor3 grey;

public:
	LLColor3();							// Initializes LLColor3 to (0, 0, 0)
	LLColor3(F32 r, F32 g, F32 b);		// Initializes LLColor3 to (r, g, b)
	LLColor3(const F32 *vec);			// Initializes LLColor3 to (vec[0]. vec[1], vec[2])
	LLColor3(char *color_string);       // html format color ie "#FFDDEE"
	explicit LLColor3(const LLColor4& color4);  // "explicit" to avoid automatic conversion
	explicit LLColor3(const LLVector4& vector4);  // "explicit" to avoid automatic conversion
	LLColor3(const LLSD& sd);
	

	LLSD getValue() const
	{
		LLSD ret;
		ret[0] = mV[0];
		ret[1] = mV[1];
		ret[2] = mV[2];
		return ret;
	}

	void setValue(const LLSD& sd)
	{
		mV[0] = (F32) sd[0].asReal();;
		mV[1] = (F32) sd[1].asReal();;
		mV[2] = (F32) sd[2].asReal();;
	}

	void calcHSL(F32* hue, F32* saturation, F32* luminance) const;
	
	const LLColor3&	setToBlack();					// Clears LLColor3 to (0, 0, 0)
	const LLColor3&	setToWhite();					// Zero LLColor3 to (0, 0, 0)
	const LLColor3&	setVec(F32 x, F32 y, F32 z);	// Sets LLColor3 to (x, y, z)
	const LLColor3&	setVec(const LLColor3 &vec);	// Sets LLColor3 to vec
	const LLColor3&	setVec(const F32 *vec);			// Sets LLColor3 to vec

	F32		magVec() const;				// Returns magnitude of LLColor3
	F32		magVecSquared() const;		// Returns magnitude squared of LLColor3
	F32		normVec();					// Normalizes and returns the magnitude of LLColor3
	F32		brightness() const;			// Returns brightness of LLColor3

	const LLColor3&	operator=(const LLColor4 &a);
	
	friend std::ostream&	 operator<<(std::ostream& s, const LLColor3 &a);		// Print a
	friend LLColor3 operator+(const LLColor3 &a, const LLColor3 &b);	// Return vector a + b
	friend LLColor3 operator-(const LLColor3 &a, const LLColor3 &b);	// Return vector a minus b

	friend const LLColor3& operator+=(LLColor3 &a, const LLColor3 &b);	// Return vector a + b
	friend const LLColor3& operator-=(LLColor3 &a, const LLColor3 &b);	// Return vector a minus b
	friend const LLColor3& operator*=(LLColor3 &a, const LLColor3 &b);

	friend LLColor3 operator*(const LLColor3 &a, const LLColor3 &b);	// Return component wise a * b
	friend LLColor3 operator*(const LLColor3 &a, F32 k);				// Return a times scaler k
	friend LLColor3 operator*(F32 k, const LLColor3 &a);				// Return a times scaler k

	friend bool operator==(const LLColor3 &a, const LLColor3 &b);		// Return a == b
	friend bool operator!=(const LLColor3 &a, const LLColor3 &b);		// Return a != b

	friend const LLColor3& operator*=(LLColor3 &a, F32 k);				// Return a times scaler k

	friend LLColor3 operator-(const LLColor3 &a);					// Return vector 1-rgb (inverse)

	inline void clamp();
	inline void exp();	// Do an exponential on the color
};

LLColor3 lerp(const LLColor3 &a, const LLColor3 &b, F32 u);


void LLColor3::clamp()
{
	// Clamp the color...
	if (mV[0] < 0.f)
	{
		mV[0] = 0.f;
	}
	else if (mV[0] > 1.f)
	{
		mV[0] = 1.f;
	}
	if (mV[1] < 0.f)
	{
		mV[1] = 0.f;
	}
	else if (mV[1] > 1.f)
	{
		mV[1] = 1.f;
	}
	if (mV[2] < 0.f)
	{
		mV[2] = 0.f;
	}
	else if (mV[2] > 1.f)
	{
		mV[2] = 1.f;
	}
}

// Non-member functions 
F32		distVec(const LLColor3 &a, const LLColor3 &b);		// Returns distance between a and b
F32		distVec_squared(const LLColor3 &a, const LLColor3 &b);// Returns distance sqaured between a and b

inline LLColor3::LLColor3(void)
{
	mV[0] = 0.f;
	mV[1] = 0.f;
	mV[2] = 0.f;
}

inline LLColor3::LLColor3(F32 r, F32 g, F32 b)
{
	mV[VX] = r;
	mV[VY] = g;
	mV[VZ] = b;
}


inline LLColor3::LLColor3(const F32 *vec)
{
	mV[VX] = vec[VX];
	mV[VY] = vec[VY];
	mV[VZ] = vec[VZ];
}

#if LL_WINDOWS
# pragma warning( disable : 4996 ) // strncpy teh sux0r
#endif

inline LLColor3::LLColor3(char* color_string) // takes a string of format "RRGGBB" where RR is hex 00..FF 
{
	if (strlen(color_string) <  6)		/* Flawfinder: ignore */
	{
		mV[0] = 0.f;
		mV[1] = 0.f;
		mV[2] = 0.f;		
		return;
	}

	static char tempstr[7];		/* Flawfinder: ignore */
	strncpy(tempstr,color_string,6);		/* Flawfinder: ignore */
	tempstr[6] = '\0';
	mV[VZ] = (F32)strtol(&tempstr[4],NULL,16)/255.f;
	tempstr[4] = '\0';
	mV[VY] = (F32)strtol(&tempstr[2],NULL,16)/255.f;
	tempstr[2] = '\0';
	mV[VX] = (F32)strtol(&tempstr[0],NULL,16)/255.f;
}

inline const LLColor3&	LLColor3::setToBlack(void)
{
	mV[0] = 0.f;
	mV[1] = 0.f;
	mV[2] = 0.f;
	return (*this);
}

inline const LLColor3&	LLColor3::setToWhite(void)
{
	mV[0] = 1.f;
	mV[1] = 1.f;
	mV[2] = 1.f;
	return (*this);
}

inline const LLColor3&	LLColor3::setVec(F32 r, F32 g, F32 b)
{
	mV[0] = r;
	mV[1] = g;
	mV[2] = b;
	return (*this);
}

inline const LLColor3&	LLColor3::setVec(const LLColor3 &vec)
{
	mV[0] = vec.mV[0];
	mV[1] = vec.mV[1];
	mV[2] = vec.mV[2];
	return (*this);
}

inline const LLColor3&	LLColor3::setVec(const F32 *vec)
{
	mV[0] = vec[0];
	mV[1] = vec[1];
	mV[2] = vec[2];
	return (*this);
}

inline F32		LLColor3::brightness(void) const
{
	return (mV[0] + mV[1] + mV[2]) / 3.0f;
}

inline F32		LLColor3::magVec(void) const
{
	return fsqrtf(mV[0]*mV[0] + mV[1]*mV[1] + mV[2]*mV[2]);
}

inline F32		LLColor3::magVecSquared(void) const
{
	return mV[0]*mV[0] + mV[1]*mV[1] + mV[2]*mV[2];
}

inline F32		LLColor3::normVec(void)
{
	F32 mag = fsqrtf(mV[0]*mV[0] + mV[1]*mV[1] + mV[2]*mV[2]);
	F32 oomag;

	if (mag)
	{
		oomag = 1.f/mag;
		mV[0] *= oomag;
		mV[1] *= oomag;
		mV[2] *= oomag;
	}
	return (mag);
}

inline void LLColor3::exp()
{
#if 0
	mV[0] = ::exp(mV[0]);
	mV[1] = ::exp(mV[1]);
	mV[2] = ::exp(mV[2]);
#else
	mV[0] = (F32)LL_FAST_EXP(mV[0]);
	mV[1] = (F32)LL_FAST_EXP(mV[1]);
	mV[2] = (F32)LL_FAST_EXP(mV[2]);
#endif
}


inline LLColor3 operator+(const LLColor3 &a, const LLColor3 &b)
{
	return LLColor3(
		a.mV[0] + b.mV[0],
		a.mV[1] + b.mV[1],
		a.mV[2] + b.mV[2]);
}

inline LLColor3 operator-(const LLColor3 &a, const LLColor3 &b)
{
	return LLColor3(
		a.mV[0] - b.mV[0],
		a.mV[1] - b.mV[1],
		a.mV[2] - b.mV[2]);
}

inline LLColor3  operator*(const LLColor3 &a, const LLColor3 &b)
{
	return LLColor3(
		a.mV[0] * b.mV[0],
		a.mV[1] * b.mV[1],
		a.mV[2] * b.mV[2]);
}

inline LLColor3 operator*(const LLColor3 &a, F32 k)
{
	return LLColor3( a.mV[0] * k, a.mV[1] * k, a.mV[2] * k );
}

inline LLColor3 operator*(F32 k, const LLColor3 &a)
{
	return LLColor3( a.mV[0] * k, a.mV[1] * k, a.mV[2] * k );
}

inline bool operator==(const LLColor3 &a, const LLColor3 &b)
{
	return (  (a.mV[0] == b.mV[0])
			&&(a.mV[1] == b.mV[1])
			&&(a.mV[2] == b.mV[2]));
}

inline bool operator!=(const LLColor3 &a, const LLColor3 &b)
{
	return (  (a.mV[0] != b.mV[0])
			||(a.mV[1] != b.mV[1])
			||(a.mV[2] != b.mV[2]));
}

inline const LLColor3 &operator*=(LLColor3 &a, const LLColor3 &b)
{
	a.mV[0] *= b.mV[0];
	a.mV[1] *= b.mV[1];
	a.mV[2] *= b.mV[2];
	return a;
}

inline const LLColor3& operator+=(LLColor3 &a, const LLColor3 &b)
{
	a.mV[0] += b.mV[0];
	a.mV[1] += b.mV[1];
	a.mV[2] += b.mV[2];
	return a;
}

inline const LLColor3& operator-=(LLColor3 &a, const LLColor3 &b)
{
	a.mV[0] -= b.mV[0];
	a.mV[1] -= b.mV[1];
	a.mV[2] -= b.mV[2];
	return a;
}

inline const LLColor3& operator*=(LLColor3 &a, F32 k)
{
	a.mV[0] *= k;
	a.mV[1] *= k;
	a.mV[2] *= k;
	return a;
}

inline LLColor3 operator-(const LLColor3 &a)
{
	return LLColor3(
		1.f - a.mV[0],
		1.f - a.mV[1],
		1.f - a.mV[2] );
}

// Non-member functions

inline F32		distVec(const LLColor3 &a, const LLColor3 &b)
{
	F32 x = a.mV[0] - b.mV[0];
	F32 y = a.mV[1] - b.mV[1];
	F32 z = a.mV[2] - b.mV[2];
	return fsqrtf( x*x + y*y + z*z );
}

inline F32		distVec_squared(const LLColor3 &a, const LLColor3 &b)
{
	F32 x = a.mV[0] - b.mV[0];
	F32 y = a.mV[1] - b.mV[1];
	F32 z = a.mV[2] - b.mV[2];
	return x*x + y*y + z*z;
}

inline LLColor3 lerp(const LLColor3 &a, const LLColor3 &b, F32 u)
{
	return LLColor3(
		a.mV[VX] + (b.mV[VX] - a.mV[VX]) * u,
		a.mV[VY] + (b.mV[VY] - a.mV[VY]) * u,
		a.mV[VZ] + (b.mV[VZ] - a.mV[VZ]) * u);
}


#endif
