/*------------------------------------------------------------------------------------- base
*-------*/

#define FixMinMax( x,min,max)           MIN( (max), MAX( (min), (x)))

typedef struct ColorRGB_f {
    float red;
    float green;
    float blue;
} ColorRGB_f;

/*------------------------------------------------------------------------------------- variant A
* source: http://en.wikipedia.org/wiki/HSL_and_HSV
* input : H,S,L: all normalized into [0...1] in respect of: [0...360°],[0...100%],[0...100%]
* output: R,G,B: all normalized into [0...1]
*-------*/

ColorRGB_f colorRGB_f_withHSL_vA(float hue, float saturation, float lightness)
{
    /*------------------------------------- prepare */
    float  hue6 = hue * 6; /* normalize hue [0...6) */
    while (hue6 <    0) hue6 += 6;
    while (hue6 >= 360) hue6 -= 6;
    if    (hue6 <    0) hue6  = 0;
    saturation = FixMinMax( saturation, 0, 1);
    lightness  = FixMinMax( lightness , 0, 1);
    /*-------------------------------------*/
    float  ll = 2 * lightness - 1;
    float  C  = (1 - ABS( ll)) * saturation;
    float  tt = fmod( hue6, 2) - 1;
    float  X  = C * (1 - ABS( tt));
    /*-------------------------------------*/
    float  rr = 0, gg = 0, bb = 0;
    if( 0 <= hue6 && hue6 < 1)
    {
        rr = C;
        gg = X;
    }
    else if( 1 <= hue6 && hue6 < 2)
    {
        rr = X;
        gg = C;
    }
    else if( 2 <= hue6 && hue6 < 3)
    {
        gg = C;
        bb = X;
    }
    else if( 3 <= hue6 && hue6 < 4)
    {
        gg = X;
        bb = C;
    }
    else if( 4 <= hue6 && hue6 < 5)
    {
        rr = X;
        bb = C;
    }
    else /* if( 5 <= hue6 && hue6 < 6) */
    {
        rr = C;
        bb = X;
    }
    /*-------------------------------------*/
    ColorRGB_f  rgb;
    float   m = lightness - C / 2;
    rgb.red   = rr + m;
    rgb.green = gg + m;
    rgb.blue  = bb + m;
    return rgb;
}

/*------------------------------------------------------------------------------------- variant B
* source: http://www.w3.org/TR/2011/REC-css3-color-20110607/#hsl-color
* input : H,S,L: all normalized into [0...1] in respect of: [0...360°],[0...100%],[0...100%]
* output: R,G,B: all normalized into [0...1]
*-------*/

ColorRGB_f colorRGB_f_withHSL_vB(float hue, float saturation, float lightness)
{
    saturation = FixMinMax( saturation, 0, 1);
    lightness  = FixMinMax( lightness , 0, 1);

    float m2 = 0;
    if (lightness <= 0.5)
        m2 = lightness * (1 + saturation);
    else
        m2 = lightness * (1 - saturation) + saturation;
    float      m1  = 2 * lightness - m2;
    ColorRGB_f rgb = {0,0,0};
    rgb.red   = colorValue_f_withHue_vB( m1, m2, hue + 1./3.);
    rgb.green = colorValue_f_withHue_vB( m1, m2, hue);
    rgb.blue  = colorValue_f_withHue_vB( m1, m2, hue - 1./3.);
    return rgb;
}

float colorValue_f_withHue_vB(float m1, float m2, float hue)
{
    while (hue >= 1) hue -= 1;
    while (hue <  0) hue += 1;
    if (1 > 6 * hue) return m1 + (m2 - m1) * hue * 6;
    if (1 > 2 * hue) return m2;
    if (2 > 3 * hue) return m1 + (m2 - m1) * (2./3. - hue) * 6;
    return m1;
}

/* original in ABC:
*
* HOW TO RETURN hsl.to.rgb(h, s, l):
*     SELECT:
*         l<=0.5: PUT l*(s+1) IN m2
*         ELSE: PUT l+s-l*s IN m2
*     PUT l*2-m2 IN m1
*     PUT hue.to.rgb(m1, m2, h+1/3) IN r
*     PUT hue.to.rgb(m1, m2, h    ) IN g
*     PUT hue.to.rgb(m1, m2, h-1/3) IN b
*     RETURN (r, g, b)
*
* HOW TO RETURN hue.to.rgb(m1, m2, h):
*     IF h<0: PUT h+1 IN h
*     IF h>1: PUT h-1 IN h
*     IF h*6<1: RETURN m1+(m2-m1)*h*6
*     IF h*2<1: RETURN m2
*     IF h*3<2: RETURN m1+(m2-m1)*(2/3-h)*6
*     RETURN m1
*/

/*------------------------------------------------------------------------------------- variant C
* it is very simple optimization of variant B
*-------*/

ColorRGB_f colorRGB_f_withHSL_vC(float hue, float saturation, float lightness)
{
    saturation = FixMinMax( saturation, 0, 1);
    lightness  = FixMinMax( lightness , 0, 1);

    if (saturation > 0)
        return colorRGB_f_withHSL_vB( hue, saturation, lightness);

    ColorRGB_f rgb = { lightness, lightness, lightness};
    return     rgb;
}

/*------------------------------------------------------------------------------------- variant D
* input : H,S,L: all normalized into [0...1] in respect of: [0...360°],[0...100%],[0...100%]
* output: R,G,B: all normalized into [0...1]
*-------*/

ColorRGB_f colorRGB_f_withHSL_vD(float hue, float saturation, float lightness)
{
    while (hue >= 1) hue -= 1;
    while (hue <  0) hue += 1;
    saturation = FixMinMax( saturation, 0, 1);
    lightness  = FixMinMax( lightness , 0, 1);

    float v = 0;
    if (lightness <= 0.5)
        v = lightness * (1 + saturation);
    else
    v = lightness + saturation - lightness * saturation;
    if (v <= 0)
    {
        ColorRGB_f rgb = { lightness, lightness, lightness};
        return     rgb;
    }

    ColorRGB_f       rgb;
    float  hue6    = fmod( hue * 6, 6); /* normalize hue [0...6) */
    int    sextant = floorf( hue6);
    float  fract   = hue6 - sextant;
    float  min     = 2 * lightness - v;
    float  sv      = (v - min) / v;
    float  vsf     = v * sv * fract;
    switch (sextant)
    {
        case  0: rgb.red = v;         rgb.green = min + vsf; rgb.blue = min;       break;
        case  1: rgb.red = v   - vsf; rgb.green = v;         rgb.blue = min;       break;
        case  2: rgb.red = min;       rgb.green = v;         rgb.blue = min + vsf; break;
        case  3: rgb.red = min;       rgb.green = v   - vsf; rgb.blue = v;         break;
        case  4: rgb.red = min + vsf; rgb.green = min;       rgb.blue = v;         break;
        case  5:
        default: rgb.red = v;         rgb.green = min;       rgb.blue = v   - vsf; break;
    }
    return rgb;
}
/*-----------------------------------------------------------------------------------------------*/