//------------------------------------------------------------------------------
// emColor.h
//
// Copyright (C) 2001,2003-2008,2010,2014,2018,2022 Oliver Hamann.
//
// Homepage: http://eaglemode.sourceforge.net/
//
// This program is free software: you can redistribute it and/or modify it under
// the terms of the GNU General Public License version 3 as published by the
// Free Software Foundation.
//
// 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 General Public License version 3 for
// more details.
//
// You should have received a copy of the GNU General Public License version 3
// along with this program. If not, see <http://www.gnu.org/licenses/>.
//------------------------------------------------------------------------------

#ifndef emColor_h
#define emColor_h

#ifndef emStd2_h
#include <emCore/emStd2.h>
#endif


//==============================================================================
//================================== emColor ===================================
//==============================================================================

class emColor {

public:

        // Class for a 32-bit color value. A color has four components, each of
        // eight bits: red, green, blue and alpha. An alpha value of 255 means
        // opaque, 0 means totally transparent. A here so called "packed color"
        // is a 32-bit unsigned integer with:
        //   Bits 0-7  : Alpha component
        //   Bits 8-15 : Blue component
        //   Bits 16-23: Green component
        //   Bits 24-31: Red component

        enum {
                // Some predefined packed colors.
                BLACK  =0x000000ff,
                WHITE  =0xffffffff,
                GRAY   =0x808080ff,
                RED    =0xff0000ff,
                GREEN  =0x00ff00ff,
                BLUE   =0x0000ffff,
                YELLOW =0xffff00ff,
                CYAN   =0x00ffffff,
                MAGENTA=0xff00ffff
        };

        emColor();
                // Performs no initialization! (emColor is a primitive type)

        emColor(emUInt32 packed);
                // Construct from a packed color.

        emColor(emByte red, emByte green, emByte blue, emByte alpha=255);
                // Construct from color components.

        emColor(const emColor & color);
                // Construct by copying a color.

        emColor(const emColor & rgb, emByte alpha);
                // Construct by copying a color, but override the alpha
                // component.

        void TryParse(const char * str);
                // Try to set this color by interpreting the given string as a
                // color. Throw an error message on failure. Currently, this
                // tries to interpret only X11 color strings like "#f8a91c" or
                // "Powder Blue". Future extension may even accept additional
                // formats.

        emColor & operator = (const emColor & color);
        emColor & operator = (emUInt32 packed);
                // Copy a color or a packed color.

        operator emUInt32 () const;
        emUInt32 Get() const;
                // Convert this color to a packed color.

        void Set(emUInt32 packed);
        void Set(emByte red, emByte green, emByte blue, emByte alpha=255);
        void Set(const emColor & color);
        void Set(const emColor & rgb, emByte alpha);
                // Like the constructors.

        emByte GetRed() const;
        emByte GetGreen() const;
        emByte GetBlue() const;
        emByte GetAlpha() const;
        void SetRed(emByte red);
        void SetGreen(emByte green);
        void SetBlue(emByte blue);
        void SetAlpha(emByte alpha);
                // Get or set individual components.

        bool IsTotallyTransparent() const;
                // Ask whether the alpha component is 0.

        bool IsOpaque() const;
                // Ask whether the alpha component is 255.

        bool IsGrey() const;
                // Ask whether the red, green and blue components are equal.

        emByte GetGrey() const;
        void SetGrey(emByte grey, emByte alpha=255);
                // Get or set in grey format.

        float GetHue() const;
        float GetSat() const;
        float GetVal() const;
        void SetHue(float hue);
        void SetSat(float sat);
        void SetVal(float val);
        void SetHSVA(float hue, float sat, float val, emByte alpha=255);
                // Get or set in Hue-Saturation-Value format. Hue is in degrees
                // (0.0-360.0). Sat and val are in percent (0.0-100.0).

        emColor GetBlended(const emColor & color, float weight) const;
                // Return a blending of this color and a given color. The given
                // weight of the given color is in percent.

        emColor GetLighted(float light) const;
                // Get a shaded or lighted version of this color. The light
                // parameter ranges from -100.0 to 100.0. -100.0 means black
                // (fully shaded), 0.0 means no change, 100.0 means white (fully
                // highlighted).

        emColor GetTransparented(float tp) const;
                // Get a more opaque or transparent version of this color. The
                // argument tp ranges from -100.0 to 100.0. -100.0 means fully
                // opaque, 0.0 means no change, 100.0 means fully transparent.

        bool operator == (const emColor & color) const;
        bool operator != (const emColor & color) const;
        bool operator == (emUInt32 packed) const;
        bool operator != (emUInt32 packed) const;
        friend bool operator == (emUInt32 packed, const emColor & color);
        friend bool operator != (emUInt32 packed, const emColor & color);
                // Compare colors.

private:
        union {
                emUInt32 Packed;
                struct {
#                       if EM_BYTE_ORDER==4321
                                emByte Red, Green, Blue, Alpha;
#                       elif EM_BYTE_ORDER==1234
                                emByte Alpha, Blue, Green, Red;
#                       elif EM_BYTE_ORDER==3412
                                emByte Green, Red, Alpha, Blue;
#                       else
#                               error unexpected value for EM_BYTE_ORDER
#                       endif
                } Components;
        };
};

inline emColor::emColor()
{
}

inline emColor::emColor(emUInt32 packed)
{
        Packed=packed;
}

inline emColor::emColor(emByte red, emByte green, emByte blue, emByte alpha)
{
        Components.Red=red;
        Components.Green=green;
        Components.Blue=blue;
        Components.Alpha=alpha;
}

inline emColor::emColor(const emColor & color)
{
        Packed=color.Packed;
}

inline emColor::emColor(const emColor & rgb, emByte alpha)
{
        // Did not work error-free with every compiler:
        //  Packed=rgb.Packed;
        //  Components.Alpha=alpha;
        // Therefore:
        Components.Red=rgb.Components.Red;
        Components.Green=rgb.Components.Green;
        Components.Blue=rgb.Components.Blue;
        Components.Alpha=alpha;
}

inline emColor & emColor::operator = (const emColor & color)
{
        Packed=color.Packed;
        return *this;
}

inline emColor & emColor::operator = (emUInt32 packed)
{
        Packed=packed;
        return *this;
}

inline emColor::operator emUInt32 () const
{
        return Packed;
}

inline emUInt32 emColor::Get() const
{
        return Packed;
}

inline void emColor::Set(emUInt32 packed)
{
        Packed=packed;
}

inline void emColor::Set(emByte red, emByte green, emByte blue, emByte alpha)
{
        Components.Red=red;
        Components.Green=green;
        Components.Blue=blue;
        Components.Alpha=alpha;
}

inline void emColor::Set(const emColor & color)
{
        Packed=color.Packed;
}

inline void emColor::Set(const emColor & rgb, emByte alpha)
{
        // Did not work error-free with every compiler:
        //  Packed=rgb.Packed;
        //  Components.Alpha=alpha;
        // Therefore:
        Components.Red=rgb.Components.Red;
        Components.Green=rgb.Components.Green;
        Components.Blue=rgb.Components.Blue;
        Components.Alpha=alpha;
}

inline emByte emColor::GetRed() const
{
        return Components.Red;
}

inline emByte emColor::GetGreen() const
{
        return Components.Green;
}

inline emByte emColor::GetBlue() const
{
        return Components.Blue;
}

inline emByte emColor::GetAlpha() const
{
        return Components.Alpha;
}

inline void emColor::SetRed(emByte red)
{
        Components.Red=red;
}

inline void emColor::SetGreen(emByte green)
{
        Components.Green=green;
}

inline void emColor::SetBlue(emByte blue)
{
        Components.Blue=blue;
}

inline void emColor::SetAlpha(emByte alpha)
{
        Components.Alpha=alpha;
}

inline bool emColor::IsTotallyTransparent() const
{
        return Components.Alpha==0;
}

inline bool emColor::IsOpaque() const
{
        return Components.Alpha==255;
}

inline bool emColor::IsGrey() const
{
        return
                Components.Red==Components.Green &&
                Components.Red==Components.Blue
        ;
}

inline emByte emColor::GetGrey() const
{
        return (emByte)(
                (
                        ((int)Components.Red)+
                        ((int)Components.Green)+
                        ((int)Components.Blue)+
                        1
                )/3
        );
}

inline void emColor::SetGrey(emByte grey, emByte alpha)
{
        Components.Red=grey;
        Components.Green=grey;
        Components.Blue=grey;
        Components.Alpha=alpha;
}

inline void emColor::SetHue(float hue)
{
        SetHSVA(hue,GetSat(),GetVal(),GetAlpha());
}

inline void emColor::SetSat(float sat)
{
        SetHSVA(GetHue(),sat,GetVal(),GetAlpha());
}

inline void emColor::SetVal(float val)
{
        SetHSVA(GetHue(),GetSat(),val,GetAlpha());
}

inline bool emColor::operator == (const emColor & color) const
{
        return Packed==color.Packed;
}

inline bool emColor::operator != (const emColor & color) const
{
        return Packed!=color.Packed;
}

inline bool emColor::operator == (emUInt32 packed) const
{
        return Packed==packed;
}

inline bool emColor::operator != (emUInt32 packed) const
{
        return Packed!=packed;
}

inline bool operator == (emUInt32 packed, const emColor & color)
{
        return packed==color.Packed;
}

inline bool operator != (emUInt32 packed, const emColor & color)
{
        return packed!=color.Packed;
}


#endif