/*
 * Color space conversion helper routines
 * Convert between rgb and hsv and between rgb and hls
 */
#include "common.h"

#include <math.h>

#include "color_helpers.h"

void
__imlib_rgb_to_hsv(int r, int g, int b, float *h, float *s, float *v)
{
    float           min, max, delta;

    min = (((r < g) ? r : g) < b) ? ((r < g) ? r : g) : b;
    max = (((r > g) ? r : g) > b) ? ((r > g) ? r : g) : b;

    *v = max / 255.f;
    delta = (max - min);
    if (delta == 0)
    {
        *h = 0.0;
        *s = 0.0;
        return;
    }
    *s = delta / max;
    if (r == max)
        *h = (g - b) / delta;
    else if (g == max)
        *h = 2.f + (b - r) / delta;
    else
        *h = 4.f + (r - g) / delta;
    *h *= 60.f;
    if (*h < 0)
        *h += 360.f;
}

void
__imlib_hsv_to_rgb(float h, float s, float v, int *r, int *g, int *b)
{
    float           f, vf;
    int             i, p, q, t, vv;

    vf = 255.f * v;
    vv = (int)round(vf);

    if (s == 0.f)
    {
        *r = *g = *b = vv;
        return;
    }

    h /= 60.f;
    i = floor(h);
    f = h - (float)i;
    p = (int)round(vf * (1.f - s));
    q = (int)round(vf * (1.f - (s * f)));
    t = (int)round(vf * (1.f - s * (1.f - f)));

    switch (i % 6)
    {
    case 0:
        *r = vv;
        *g = t;
        *b = p;
        break;
    case 1:
        *r = q;
        *g = vv;
        *b = p;
        break;
    case 2:
        *r = p;
        *g = vv;
        *b = t;
        break;
    case 3:
        *r = p;
        *g = q;
        *b = vv;
        break;
    case 4:
        *r = t;
        *g = p;
        *b = vv;
        break;
    case 5:
    default:
        *r = vv;
        *g = p;
        *b = q;
        break;
    }
}

void
__imlib_rgb_to_hls(int r, int g, int b, float *hue, float *lightness,
                   float *saturation)
{
    int             f;
    float           i, j, k, max, min, d;

    i = ((float)r) / 255.f;
    j = ((float)g) / 255.f;
    k = ((float)b) / 255.f;

    f = 0;
    max = min = i;
    if (j > max)
    {
        max = j;
        f = 1;
    }
    else
        min = j;
    if (k > max)
    {
        max = k;
        f = 2;
    }
    else if (k < min)
        min = k;
    d = max - min;

    *lightness = (max + min) / 2.f;
    if (d == 0)
    {
        *saturation = 0;
        *hue = 0;
    }
    else
    {
        if (*lightness < 0.5f)
            *saturation = d / (max + min);
        else
            *saturation = d / (2 - max - min);
        switch (f)
        {
        case 0:
            *hue = (j - k) / d;
            break;
        case 1:
            *hue = 2 + (k - i) / d;
            break;
        case 2:
            *hue = 4 + (i - j) / d;
            break;
        }
        *hue *= 60.f;
        if (*hue < 0)
            *hue += 360.f;
    }
}

void
__imlib_hls_to_rgb(float hue, float lightness, float saturation, int *r, int *g,
                   int *b)
{
    float           m1, m2, m21, h;

    if (saturation == 0)
        *r = *g = *b = (int)(lightness * 255.f);
    else
    {
        if (lightness <= 0.5f)
            m2 = lightness * (1 + saturation);
        else
            m2 = lightness + saturation + lightness * saturation;
        m1 = 2 * lightness - m2;
        m21 = m2 - m1;
        h = hue + 120;
        if (h > 360)
            h -= 360;
        else if (h < 0)
            h += 360;
        if (h < 60)
            *r = (int)(255.f * (m1 + m21 * h / 60.f));
        else if (h < 180)
            *r = (int)(255.f * m2);
        else if (h < 240)
            *r = (int)(255.f * (m1 + m21 * (240.f - h) / 60.f));
        else
            *r = (int)(255.f * m1);
        h = hue;
        if (h > 360)
            h -= 360;
        else if (h < 0)
            h += 360;
        if (h < 60)
            *g = (int)(255.f * (m1 + m21 * h / 60.f));
        else if (h < 180)
            *g = (int)(255.f * m2);
        else if (h < 240)
            *g = (int)(255.f * (m1 + m21 * (240.f - h) / 60.f));
        else
            *g = (int)(255.f * m1);
        h = hue - 120;
        if (h > 360)
            h -= 360;
        else if (h < 0)
            h += 360;
        if (h < 60)
            *b = (int)(255.f * (m1 + m21 * h / 60.f));
        else if (h < 180)
            *b = (int)(255.f * m2);
        else if (h < 240)
            *b = (int)(255.f * (m1 + m21 * (240.f - h) / 60.f));
        else
            *b = (int)(255.f * m1);
    }
}
