// *********************************************************
// Ray Tracer Class
// Author : Tamy Boubekeur (boubek@gmail.com).
// Copyright (C) 2012 Tamy Boubekeur.
// All rights reserved.
// *********************************************************

#ifndef COLOR_H
#define COLOR_H

#include <GL/glew.h>
#include <QColor>

struct Color
{
    static const GLenum format = GL_RGBA;
    static const GLenum type = GL_UNSIGNED_INT_8_8_8_8_REV;
    unsigned char r;
    unsigned char g;
    unsigned char b;
    unsigned char a;

    Color(unsigned int r_ = 0, unsigned int g_ = 0, unsigned int b_ = 0, unsigned int a_ = 255) : r(r_), g(g_), b(b_), a(a_) {}
    Color(float r_, float g_ = 0, float b_ = 0, float a_ = 1) : r(r_ * 255.0f), g(g_ * 255.0f), b(b_ * 255.0f), a(a_ * 255.0f) {}
    Color(const QColor& c) : r(c.red()), g(c.green()), b(c.blue()), a(c.alpha()) {}

    float atF(unsigned int t) const
    {
        switch(t)
        {
            case 0: return redF();
            case 1: return greenF();
            case 2: return blueF();
            default: return alphaF();
        }
    }

    float redF() const { return float(r) / 255.0f; }
    float greenF() const { return float(g) / 255.0f; }
    float blueF() const { return float(b) / 255.0f; }
    float alphaF() const { return float(a) / 255.0f; }

    Color& operator=(const QColor& c)
    {
        r = c.red();
        g = c.green();
        b = c.blue();
        a = c.alpha();
        return *this;
    }

    Color& operator*=(const Color& o)
    {
        r = ((float)o.r * (float)r) / 255.0f;
        g = ((float)o.g * (float)g) / 255.0f;
        b = ((float)o.b * (float)b) / 255.0f;
        a = ((float)o.a * (float)a) / 255.0f;
        return *this;
    }

    Color operator*(const Color& o) const
    {
        Color c = *this;
        c *= o;
        return c;
    }

    Color& operator+=(const Color& o)
    {
        // Note: this works correctly only if alpha is 255,
        // as addition does not include it.
        int r_ = (int)o.r + (int)r;
        int g_ = (int)o.g + (int)g;
        int b_ = (int)o.b + (int)b;
        int a_ = (int)o.a + (int)a;
        r = std::min(std::max(r_,0),255);
        g = std::min(std::max(g_,0),255);
        b = std::min(std::max(b_,0),255);
        a = std::min(std::max(a_,0),255);
        return *this;
    }

    Color operator+(const Color& o)
    {
        Color c = *this;
        c += o;
        return c;
    }

    Color& operator*=(float f)
    {
        // 255.01f: avoid precision errors
        r = std::min(255.01f, std::max(0.0f, float(r) * f));
        g = std::min(255.01f, std::max(0.0f, float(g) * f));
        b = std::min(255.01f, std::max(0.0f, float(b) * f));
        return *this;
    }

    Color operator*(float d) const
    {
        Color c = *this;
        c *= d;
        return c;
    }
};

inline Color operator*(float d, const Color& c) { return c * d; }

struct Colorf
{
public:
    float r, g, b, a;

    Colorf(float r_=0, float g_=0, float b_=0, float a_=1) :
        r(std::min(std::max(r_,0.0f),1.0f)),
        g(std::min(std::max(g_,0.0f),1.0f)),
        b(std::min(std::max(b_,0.0f),1.0f)),
        a(std::min(std::max(a_,0.0f),1.0f))
    {}
    Colorf(unsigned int r_, unsigned int g_ = 0, unsigned int b_ = 0, unsigned int a_ = 255) :
        r(r_ / 255.0f), g(g_ / 255.0f), b(b_ / 255.0f), a(a_ / 255.0f) {}
    Colorf(const QColor& c) : r(c.redF()), g(c.greenF()), b(c.blueF()), a(c.alphaF()) {}
    Colorf(const Color& c) : r(c.redF()), g(c.greenF()), b(c.blueF()), a(c.alphaF()) {}

    Colorf& addNoClamp(const Colorf& o)
    {
        r += o.r;
        g += o.g;
        b += o.b;
        a += o.a;
        return *this;
    }

    Colorf& mulNoClamp(float f)
    {
        r *= f;
        g *= f;
        b *= f;
        a *= f;
        return *this;
    }

    Colorf& operator*=(const Colorf& o)
    {
        r *= o.r;
        g *= o.g;
        b *= o.b;
        a *= o.a;
        return *this;
    }

    Colorf operator*(const Colorf& o) const
    {
        Colorf c = *this;
        c *= o;
        return c;
    }

    Colorf& operator+=(const Colorf& o)
    {
        // Note: this works correctly only if alpha is 255,
        // as addition does not include it.
        r = std::min(std::max(r + o.r,0.0f),1.0f);
        g = std::min(std::max(g + o.g,0.0f),1.0f);
        b = std::min(std::max(b + o.b,0.0f),1.0f);
        a = std::min(std::max(a + o.a,0.0f),1.0f);
        return *this;
    }

    Colorf operator+(const Colorf& o)
    {
        Colorf c = *this;
        c += o;
        return c;
    }

    Colorf& operator*=(float f)
    {
        r = std::min(std::max(r * f,0.0f),1.0f);
        g = std::min(std::max(g * f,0.0f),1.0f);
        b = std::min(std::max(b * f,0.0f),1.0f);
        return *this;
    }

    Colorf operator*(float d) const
    {
        Colorf c = *this;
        c *= d;
        return c;
    }

    Colorf& operator/=(float f)
    {
        return operator*=(1 / f);
    }

    Colorf operator/(float d) const
    {
        Colorf c = *this;
        c /= d;
        return c;
    }

    operator Color() const
    {
        return Color(r, g, b, a);
    }

    QColor toQColor() const
    {
        return QColor::fromRgbF(r, g, b, a);
    }
};

inline Colorf operator*(float d, const Colorf& c) { return c * d; }

#endif // COLOR_H

// Some Emacs-Hints -- please don't remove:
//
//  Local Variables:
//  mode:C++
//  tab-width:4
//  End:
