/***********************************************************************
	created:	20/8/2004
	author:		Paul D Turner (with code from Jeff Leigh)
	
	purpose:	Defines interface to the colour class used to represent
				colour values within the system
*************************************************************************/
/***************************************************************************
 *   Copyright (C) 2004 - 2015 Paul D Turner & The CEGUI Development Team
 *
 *   Permission is hereby granted, free of charge, to any person obtaining
 *   a copy of this software and associated documentation files (the
 *   "Software"), to deal in the Software without restriction, including
 *   without limitation the rights to use, copy, modify, merge, publish,
 *   distribute, sublicense, and/or sell copies of the Software, and to
 *   permit persons to whom the Software is furnished to do so, subject to
 *   the following conditions:
 *
 *   The above copyright notice and this permission notice shall be
 *   included in all copies or substantial portions of the Software.
 *
 *   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 *   EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
 *   MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
 *   IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
 *   OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
 *   ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
 *   OTHER DEALINGS IN THE SOFTWARE.
 ***************************************************************************/
#ifndef _CEGUIColour_h_
#define _CEGUIColour_h_

#include "CEGUI/Base.h"

namespace CEGUI
{
typedef std::uint32_t argb_t;    //!< 32 bit ARGB representation of a colour.

/*!
\brief
	Class representing colour values within the system.
*/
class CEGUIEXPORT Colour
{
public:
	/*************************************************************************
		Construction & Destruction
	*************************************************************************/
	Colour(void);
	Colour(const Colour& val);
	Colour(float red, float green, float blue, float alpha = 1.0f);
	Colour(argb_t argb);

	/*************************************************************************
		Accessors
	*************************************************************************/
	argb_t	getARGB(void) const
	{
		if (!d_argbValid)
		{
			d_argb = calculateARGB();
			d_argbValid = true;
		}

		return d_argb;
	}
	
	float	getAlpha(void) const	{return d_alpha;}
	float	getRed(void) const		{return d_red;}
	float	getGreen(void) const	{return d_green;}
	float	getBlue(void) const		{return d_blue;}

    /*!
    \brief
        Calculates and returns the hue value based on the Colour

    \return
        The hue as a float value.
    */
    float	getHue(void) const;

    /*!
    \brief
        Calculates and returns the saturation value based on the Colour

    \return
        The saturation as a float value.
    */
	float	getSaturation(void) const;

    /*!
    \brief
        Calculates and returns the lumination value based on the Colour

    \return
        The lumination as a float value.
    */
	float	getLumination(void) const;


	/*************************************************************************
		Manipulators
	*************************************************************************/
	void	setARGB(argb_t argb);
	inline void setAlpha(float alpha)
    {
        d_argbValid = false;
        d_alpha = alpha;
    }

	inline void setRed(float red)
    {   
        d_argbValid = false;
        d_red = red;
    }

	inline void setGreen(float green)
    {
        d_argbValid = false;
        d_green = green;
    }

	inline void setBlue(float blue)
    {
        d_argbValid = false;
        d_blue = blue;
    }

	inline void set(float red, float green, float blue, float alpha = 1.0f)
    {
        d_argbValid = false;
        d_alpha = alpha;
        d_red = red;
        d_green = green;
        d_blue = blue;
    }

	inline void setRGB(float red, float green, float blue)
    {
        d_argbValid = false;
        d_red = red;
        d_green = green;
        d_blue = blue;
    }

	inline void setRGB(const Colour& val)
    {
        d_red = val.d_red;
        d_green = val.d_green;
        d_blue = val.d_blue;
        if (d_argbValid)
        {
            d_argbValid = val.d_argbValid;
            if (d_argbValid)
                d_argb = (d_argb & 0xFF000000) | (val.d_argb & 0x00FFFFFF);
        }
    }

	void	setHSL(float hue, float saturation, float luminance, float alpha = 1.0f);

	void	invertColour(void);
	void	invertColourWithAlpha(void);

	/*************************************************************************
		Operators
	*************************************************************************/
	inline Colour& operator=(argb_t val)
    {
        setARGB(val);
        return *this;
    }

	inline Colour& operator=(const Colour& val)
    {
        d_alpha = val.d_alpha;
        d_red   = val.d_red;
        d_green = val.d_green;
        d_blue  = val.d_blue;
        d_argb  = val.d_argb;
        d_argbValid = val.d_argbValid;

        return *this;
    }

	inline Colour& operator&=(argb_t val)
    {
        setARGB(getARGB() & val);
        return *this;
    }

	inline Colour& operator&=(const Colour& val)
    {
        setARGB(getARGB() & val.getARGB());
        return *this;
    }

	inline Colour& operator|=(argb_t val)
    {
        setARGB(getARGB() | val);
        return *this;
    }

	inline Colour& operator|=(const Colour& val)
    {
        setARGB(getARGB() | val.getARGB());
        return *this;
    }

	inline Colour& operator<<=(int val)
    {
        setARGB(getARGB() << val);
        return *this;
    }

	inline Colour& operator>>=(int val)
    {
        setARGB(getARGB() >> val);
        return *this;
    }

	inline Colour operator+(const Colour& val) const
    {
        return Colour(
            d_red   + val.d_red, 
            d_green + val.d_green, 
            d_blue  + val.d_blue,
            d_alpha + val.d_alpha
        );
    }

	inline Colour operator-(const Colour& val) const
    {
        return Colour(
            d_red   - val.d_red,
            d_green - val.d_green,
            d_blue  - val.d_blue,
            d_alpha - val.d_alpha
        );
    }

	inline Colour operator*(const float val) const
    {       
        return Colour(
            d_red   * val, 
            d_green * val, 
            d_blue  * val,
            d_alpha * val 
        );  
    }

    inline Colour& operator*=(const Colour& val)
    {
        d_red *= val.d_red;
        d_blue *= val.d_blue;
        d_green *= val.d_green;
        d_alpha *= val.d_alpha;

		d_argbValid = false;

        return *this;
    }

	/*************************************************************************
		Compare operators
	*************************************************************************/
	inline bool operator==(const Colour& rhs) const
    {
        return d_red   == rhs.d_red   &&
               d_green == rhs.d_green &&
               d_blue  == rhs.d_blue  &&
               d_alpha == rhs.d_alpha;
    }

	inline bool operator!=(const Colour& rhs) const
    {
        return !(*this == rhs);
    }

    /*!
    \brief Writes a Colour to a stream
    */
    friend std::ostream& operator << (std::ostream& s, const Colour& val);

    /*!
    \brief Extracts a Colour from a stream
    */
    friend std::istream& operator >> (std::istream& inStream, Colour& val);

	//
	// Conversion operators
	//
	operator argb_t() const		{return getARGB();}

    static argb_t calculateArgb(std::uint8_t alpha, std::uint8_t red,
        std::uint8_t green, std::uint8_t blue)
    {
        return (
            static_cast<argb_t>(alpha) << 24 |
            static_cast<argb_t>(red) << 16 |
            static_cast<argb_t>(green) << 8 |
            static_cast<argb_t>(blue)
            );
    }


private:
	/*************************************************************************
		Implementation Methods
	*************************************************************************/
	/*!
	\brief
		calculate and return the ARGB value based on the current colour component values.
	*/
	argb_t	calculateARGB(void) const;

	/*************************************************************************
		Implementation Data
	*************************************************************************/
	float d_alpha, d_red, d_green, d_blue;		//!< Colour components.
	mutable argb_t d_argb;						//!< Colour as ARGB value.
	mutable bool d_argbValid;					//!< True if argb value is valid.
};

inline Colour operator *(const Colour& a, const Colour& b)
{
    return Colour(a.getRed() * b.getRed(),
        a.getGreen() * b.getGreen(),
        a.getBlue() * b.getBlue(),
        a.getAlpha() * b.getAlpha());
}

} // End of  CEGUI namespace section


#endif	// end of guard _CEGUIColour_h_
