using System;
using System.Drawing;

/// <summary>Представление цвета в формате HSB</summary>
public struct HSB
{
    /// <summary>Оттенок цвета (находится в диапазоне от 0 до 360)</summary>
    double hue;
    /// <summary>Насыщенность цвета (находится в диапазоне от 0 до 1)</summary>
    double saturation;
    /// <summary>Яркость цвета (находится в диапазоне от 0 до 1)</summary>
    double brightness;

    public HSB(double hue, double saturation, double brightness)
    {
        if (hue < 0 || hue > 360)
            throw new ArgumentOutOfRangeException("Значение оттенка H вышло за пределы [0,360)");
        if (saturation < 0 || saturation > 1)
            throw new ArgumentOutOfRangeException("Значение насыщенности S вышло за пределы [0,1]");
        if (brightness < 0 || brightness > 1)
            throw new ArgumentOutOfRangeException("Значение яркости S вышло за пределы [0,1]");
        this.hue = hue;
        this.saturation = saturation;
        this.brightness = brightness;           
    }

    /// <summary>Конструктор для преобразования цвета из пространства RGB</summary>
    /// <param name="rgb">Цвет в пространстве RGB</param>
    public HSB(Color rgb)
    {
        byte min = Math.Min(rgb.R, Math.Min(rgb.G, rgb.B)); // Минимальная из трех компонент (R,G,B)
        byte max = Math.Max(rgb.R, Math.Max(rgb.G, rgb.B)); // Максимальная из трех компонент (R,G,B)
        byte delta = (byte)(max - min);    // Диапазон изменения компонент
        // Яркость равна максимальной компоненте (R,G,B)
        brightness = max / 255.0;

        if (max == 0)
        {
            saturation = 0;
            hue = Double.NaN; // Оттенок неопределен
        }
        else
        {
            // Насыщенность равна отношению диапазона к макс. компоненте
            saturation = delta / (double)max;
            // Вычисляем оттенок цвета
            if (rgb.R == max)
                hue = (rgb.G - rgb.B) / (double)delta;
            else if (rgb.G == max)
                hue = 2 + (rgb.B - rgb.R) / (double)delta;
            else
                hue = 4 + (rgb.R - rgb.G) / (double)delta;
            if ((hue *= 60) < 0)
                hue += 360;
        }
    }

    public static implicit operator HSB(Color rgb)
    {
        return new HSB(rgb);
    }

    public static implicit operator Color(HSB hsv)
    {
        // Компоненты цвета в пространстве RGB. Каждая из компонент лежит в диапазоне от 0 до 1.
        double r = 0, g = 0, b = 0;
        if (hsv.S == 0) // Равная нулю насыщенность означает оттенки серого
                r = g = b = hsv.B;
        else
        {
            double h = hsv.H/60.0;
            int i = (int)Math.Floor(h);
            double f = h - i;
            double p = hsv.B * (1 - hsv.S);
            double q = hsv.B * (1 - hsv.S * f);
            double t = hsv.B * (1 - hsv.S * (1 - f));
            switch (i)
            {
                case 0:
                    r = hsv.B; g = t; b = p;
                    break;
                case 1:
                    r = q; g = hsv.B; b = p;
                    break;
                case 2:
                    r = p; g = hsv.B; b = t;
                    break;
                case 3:
                    r = p; g = q; b = hsv.B;
                    break;
                case 4:
                    r = t; g = p; b = hsv.B;
                    break;
                case 5:
                    r = hsv.B; g = p; b = q;
                    break;
            }
        }
        return Color.FromArgb((byte)(255 * r), (byte)(255 * g), (byte)(255 * b));
    }

    public double H
    {
        get { return hue; }
    }

    public double S
    {
        get { return saturation; }
    }

    public double B
    {
        get { return this.brightness; }
    }

    public override string ToString()
    {
        return String.Format("HSB({0},{1},{2})", hue, saturation, brightness);
    }
}