#pragma once

#define WHUT

#include <cmath>
#include <vectors/vec2.h>
#include <vectors/vec3.h>
#include <vectors/vec4.h>

namespace color
{
    typedef vec3f rgb;
    typedef vec4f rgba;
    typedef vec3f hsv;
    typedef vec4f hsva;

    const rgba black  = rgba(0.0, 0.0, 0.0, 1);
    const rgba white  = rgba(1.0, 1.0, 1.0, 1);
    const rgba gray   = rgba(.50, .50, .50, 1);
    const rgba silver = rgba(.75, .75, .75, 1);

    const rgba red    = rgba(.67, .19, .19, 1);
    const rgba purple = rgba(.46, .26, .54, 1);
    const rgba pink   = rgba(.85, .34, .39, 1);
    const rgba maroon = rgba(.27, .16, .23, 1);

    const rgba green  = rgba(.41, .74, .19, 1);
    const rgba lime   = rgba(.59, .89, .31, 1);
    const rgba grass  = rgba(.29, .41, .18, 1);
    const rgba olive  = rgba(.56, .59, .29, 1);

    const rgba blue   = rgba(.39, .61, 1.0, 1);
    const rgba cyan   = rgba(.37, .80, .89, 1);
    const rgba violet = rgba(.25, .25, .45, 1);
    const rgba navy   = rgba(.13, .12, .20, 1);

    const rgba brown  = rgba(.40, .22, .19, 1);
    const rgba orange = rgba(.87, .44, .15, 1);
    const rgba yellow = rgba(.98, .95, .21, 1);
    const rgba sand   = rgba(.93, .76, .60, 1);

    constexpr rgb uint2rgb(uint32_t color) noexcept
    {
        return rgb((float)((color>>16)&0xff)/255.0f,
                   (float)((color>>8)&0xff)/255.0f,
                   (float)((color)&0xff)/255.0f);
    }

    constexpr rgba uint2rgba(uint32_t color, float alpha = 1.0f) noexcept
    {
        return rgba((float)((color>>16)&0xff)/255.0f,
                    (float)((color>>8)&0xff)/255.0f,
                    (float)((color)&0xff)/255.0f,
                    alpha);
    }

    constexpr uint32_t rgb2uint(const rgb &color)
    {
        uint32_t ret = 0;
        ret |= ((uint32_t)(color.r*255)&0xff)<<16;
        ret |= ((uint32_t)(color.g*255)&0xff)<<8;
        ret |= ((uint32_t)(color.b*255)&0xff);
        return ret;
    }

    constexpr rgb hsv2rgb(const hsv &in) noexcept
    {
        int x = int(in.h)%360;

        float vmin = in.v*(1.0f-in.s);
        float a = (in.v - vmin)*(x%60)/60;
        float vinc = vmin+a;
        float vdec = in.v-a;

        switch(x/60)
        {
        case 0:  return rgb(in.v, vinc, vmin);
        case 1:  return rgb(vdec, in.v, vmin);
        case 2:  return rgb(vmin, in.v, vinc);
        case 3:  return rgb(vmin, vdec, in.v);
        case 4:  return rgb(vinc, vmin, in.v);
        default: return rgb(in.v, vmin, vdec);
        }
    }

    constexpr rgba hsv2rgba(const hsv &color, float alpha = 1.0f) noexcept
    {
        return rgba(hsv2rgb(color), alpha);
    }

    constexpr rgba hsv2rgba(const hsva &color) noexcept
    {
        return rgba(hsv2rgb(hsv(color.h, color.s, color.v)), color.a);
    }

    constexpr rgba alpha(float a) noexcept
    {
        return rgba(1, 1, 1, a);
    }

    constexpr rgba lighten(const rgba &color, float value) noexcept
    {
        return color+rgba(value, value, value, 0);
    }

    constexpr rgba darken(const rgba &color, float value) noexcept
    {
        return color-rgba(value, value, value, 0);
    }

    constexpr rgba saturate(rgba color, float value) noexcept
    {
        float mx = std::max(color.r, std::max(color.g, color.b));
        if(mx <= 0)
            return color;
        value += 1;
        for(int i = 0; i < 3; i++)
            color.data[i] -= (mx-color.data[i])*value;
        return color;
    }

    constexpr rgba desaturate(rgba color, float value) noexcept
    {
        float mx = std::max(color.r, std::max(color.g, color.b));
        if(mx <= 0)
            return color;
        for(int i = 0; i < 3; i++)
            color.data[i] += (mx-color.data[i])*value;
        return color;
    }

    constexpr hsv rgb2hsv(const rgb &in) noexcept
    {
        hsv result;
        float cmax = in.max();
        float cmin = in.min();
        float delta = cmax-cmin;

        if(delta == 0)
            result.h = 0;
        else if(cmax == in.r)
            result.h = fmod((in.g-in.b)/delta, 6);
        else if(cmax == in.y)
            result.h = (in.b-in.r)/delta+2;
        else if(cmax == in.z)
            result.h = (in.r-in.g)/delta+4;
        result.h *= 60;
        if(cmax == 0)
            result.s = 0;
        else
            result.s = delta/cmax;
        result.v = cmax;
        return result;
    }
}
