/*
 * This source file is part of KEOS (Free 3D Engine)
 * For the latest info, see http://www.keosengine.org/
 * E-mails : thierry.vouriot@keosengine.org, yeri@keosengine.org
 * 
 * This program is free software; you can redistribute it and/or modify it under
 * the terms of the GNU Lesser General Public License as published by the Free Software
 * Foundation; either version 2 of the License, or (at your option) any later
 * version.
 *
 * This program is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
 * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License along with
 * this program; if not, write to the Free Software Foundation, Inc., 59 Temple
 * Place - Suite 330, Boston, MA 02111-1307, USA, or go to
 * http://www.gnu.org/copyleft/lesser.txt.
 *
 */

#ifndef KEOS_COLOR_H
#define KEOS_COLOR_H

#include "KeosPrerequisites.h"

namespace Keos
{
    //-----------------------------------------------------------------------
    /** Class manipulating colors.
     */
    class CColor
    {
    public :

        /** Default constructor.
         *  @param nColor Color (ex: 0xFF00FF00 for red).
         */
        CColor(ulong nColor = 0xFF000000)
        {
          m_nColor = nColor;
        }

        /** Constructor using 4 constituents.
         *  @param r Red canal.
         *  @param g Green canal.
         *  @param b Blue canal.
         *  @param a Alpha canal.
         */
        CColor(uchar r, uchar g, uchar b, uchar a = 0xFF)
        {
            Set(r, g, b, a);
        }

        /** Return the color in ARGB mode.
         *  @return Color in ARGB mode.
         */
        ulong ToARGB() const
        {
            return (GetAlpha() << 24) | (GetRed() << 16) | (GetGreen() << 8) | (GetBlue() << 0);
        }

        /** Return the color in ABGR mode.
         *  @return Color in ABGR mode.
         */
        ulong ToABGR() const
        {
            return (GetAlpha() << 24) | (GetBlue() << 16) | (GetGreen() << 8) | (GetRed() << 0);
        }

        /** Return the color in RGBA mode.
         *  @return Color in RGBA mode.
         */
        ulong ToRGBA() const
        {
            //return (GetRed() << 24) | (GetGreen() << 16) | (GetBlue() << 8) | (GetAlpha() << 0);
			return m_nColor; //@todo c pas mieux ça ?
        }

        /** Return the Alpha canal.
         *  @return The Alpha canal.
         */
        uchar GetAlpha() const
        {
            return static_cast<uchar>((m_nColor & 0xFF000000) >> 24);
        }

        /** Return the Red canal.
         *  @return The Red canal.
         */
        uchar GetRed() const
        {
            return static_cast<uchar>((m_nColor & 0x00FF0000) >> 16);
        }

        /** Return the Green canal.
         *  @return The Green canal.
         */
        uchar GetGreen() const
        {
            return static_cast<uchar>((m_nColor & 0x0000FF00) >> 8);
        }

        /** Return the Blue canal.
         *  @return The Blue canal.
         */
        uchar GetBlue() const
        {
            return static_cast<uchar>((m_nColor & 0x000000FF) >> 0);
        }

        /** Create new color with 4 floats [0..1].
         *  @param r Red canal.
         *  @param g Green canal.
         *  @param b Blue canal.
         *  @param a Alpha canal.
         */
        void SetFloats(float r, float g, float b, float a = 1.0f)
        {
            int R = static_cast<int>(r * 255.0f);
            int G = static_cast<int>(g * 255.0f);
            int B = static_cast<int>(b * 255.0f);
            int A = static_cast<int>(a * 255.0f);

	          SetInt(R, G, B, A);
        }
 
        /** Create new color with 4 uchar [0..255].
         *  @param r Red canal.
         *  @param g Green canal.
         *  @param b Blue canal.
         *  @param a Alpha canal.
         */
        void Set(uchar r, uchar g, uchar b, uchar a = 0xFF)
        {
            m_nColor = (a << 24) | (r << 16) | (g << 8) | (b << 0);
        }

        /** Convert the color in 4 floats.
         *  @param Dest Array which will contain the 4 floats.
         */
        void ToFloat(float Dest[]) const
        {
            Dest[0] = GetRed()   / 255.0f;
            Dest[1] = GetGreen() / 255.0f;
            Dest[2] = GetBlue()  / 255.0f;
            Dest[3] = GetAlpha() / 255.0f;
        }

        /** == operator.
         */
        bool operator ==(const CColor& c) const
        {
            return m_nColor == c.m_nColor;
        }

        /** != operator.
         */
        bool operator !=(const CColor& c) const
        {
            return !(*this == c);
        }

        /** += operator.
         */
        const CColor& operator +=(const CColor& c)
        {
            int R = GetRed()   + c.GetRed();
	        int G = GetGreen() + c.GetGreen();
	        int B = GetBlue()  + c.GetBlue();
	        int A = GetAlpha() + c.GetAlpha();

            SetInt(R, G, B, A);

	        return *this;
        }

        /** -= operator.
         */
        const CColor& operator -=(const CColor& c)
        {
            int R = GetRed()   - c.GetRed();
	        int G = GetGreen() - c.GetGreen();
	        int B = GetBlue()  - c.GetBlue();
	        int A = GetAlpha() - c.GetAlpha();

            SetInt(R, G, B, A);

            return *this;
        }

        /** + operator.
         */
        CColor operator +(const CColor& c) const
        {
            int R = GetRed()   + c.GetRed();
	        int G = GetGreen() + c.GetGreen();
	        int B = GetBlue()  + c.GetBlue();
	        int A = GetAlpha() + c.GetAlpha();

            CColor Ret;
            Ret.SetInt(R, G, B, A);

	        return Ret;
        }

        /** - operator.
         */
        CColor operator -(const CColor& c) const
        {
            int R = GetRed()   - c.GetRed();
	        int G = GetGreen() - c.GetGreen();
	        int B = GetBlue()  - c.GetBlue();
	        int A = GetAlpha() - c.GetAlpha();

            CColor Ret;
            Ret.SetInt(R, G, B, A);

	        return Ret;
        }

        /** * operator.
         */
        CColor operator *(float v) const
        {
            int R = static_cast<int>(GetRed()   * v);
	        int G = static_cast<int>(GetGreen() * v);
	        int B = static_cast<int>(GetBlue()  * v);
	        int A = static_cast<int>(GetAlpha() * v);

            CColor Ret;
            Ret.SetInt(R, G, B, A);

	        return Ret;
        }

        /** *= operator.
         */
        const CColor& operator *=(float v)
        {
			int R = static_cast<int>(GetRed()   * v);
	        int G = static_cast<int>(GetGreen() * v);
	        int B = static_cast<int>(GetBlue()  * v);
	        int A = static_cast<int>(GetAlpha() * v);

	        SetInt(R, G, B, A);

            return *this;
        }

        /** / operator.
         */
        CColor operator /(float v) const
        {
            return *this * (1.0f / v);
        }

        /** /= operator.
         */
        const CColor& operator /=(float v)
        {
            return *this *= (1.0f / v);
        }

        /** Add two colors.
         *  @param c Color to add.
         */
        CColor Add(const CColor& c) const
        {
            return *this + c;
        }

        /** Modulate two colors.
         *  @param c Color to modulate.
         */
        CColor Modulate(const CColor& c) const
        {
            uchar R = static_cast<uchar>(GetRed()   * c.GetRed()   / 255);
            uchar G = static_cast<uchar>(GetGreen() * c.GetGreen() / 255);
            uchar B = static_cast<uchar>(GetBlue()  * c.GetBlue()  / 255);
            uchar A = static_cast<uchar>(GetAlpha() * c.GetAlpha() / 255);

            return CColor(R, G, B, A);
        }
        
        //! Predefined color: White
        static const ulong White = 0xFFFFFFFF;
        //! Predefined color: Black
        static const ulong Black = 0xFF000000;
        //! Predefined color: Red
        static const ulong Red   = 0xFFFF0000;
        //! Predefined color: Green
        static const ulong Green = 0xFF00FF00;
        //! Predefined color: Blue
        static const ulong Blue  = 0xFF0000FF;

        /** Overload of the operator >>.
         *
         */
        friend std::istream& operator >>(std::istream& Stream, CColor& Color)
        {
            int R, G, B, A;
            Stream >> R >> G >> B >> A;
            Color.SetInt(R, G, B, A);

            return Stream;
        }

        /** Overload of the operator <<.
         *
         */
        friend std::ostream& operator <<(std::ostream& Stream, const CColor& Color)
        {
            return Stream << (int)Color.GetRed() << " " << (int)Color.GetGreen() << " " << (int)Color.GetBlue() << " " << (int)Color.GetAlpha();
        }

    private :

        /** Create new color with 4 int [0..255].
         *  @param r Red canal.
         *  @param g Green canal.
         *  @param b Blue canal.
         *  @param a Alpha canal.
         */
        void SetInt(int r, int g, int b, int a = 0xFF)
        {
            uchar R = (r >= 0) ? (r <= 255 ? r : 255) : 0;
	        uchar G = (g >= 0) ? (g <= 255 ? g : 255) : 0;
	        uchar B = (b >= 0) ? (b <= 255 ? b : 255) : 0;
	        uchar A = (a >= 0) ? (a <= 255 ? a : 255) : 0;

	        Set(R, G, B);
        }

        //! Current color
        ulong m_nColor;
    };

} // namespace Keos


#endif // KEOS_COLOR_H
