// Copyright (C) 2026 Kiyotsugu Arai
// SPDX-License-Identifier: LGPL-3.0-or-later

// RGB.hpp — RGB 色型 + RGBA, HSV 変換
//
// 移植元: lib++20 メディア/画像/RGB型.h
// 改良点:
//   - calx namespace
//   - RGBA 追加
//   - HSV 変換
//   - operator<=> (C++20)

#ifndef CALX_IMAGE_TYPES_RGB_HPP
#define CALX_IMAGE_TYPES_RGB_HPP

#include <algorithm>
#include <cmath>
#include <cstdint>
#include <ostream>

namespace calx::image {

    /// RGB 色型
    template<typename T>
    struct RGB {
        T r{}, g{}, b{};

        RGB() = default;
        RGB(T r, T g, T b) : r(r), g(g), b(b) {}

        template<typename U>
        explicit RGB(const RGB<U>& c) : r(static_cast<T>(c.r)),
            g(static_cast<T>(c.g)), b(static_cast<T>(c.b)) {}

        RGB operator+(const RGB& c) const { return {r + c.r, g + c.g, b + c.b}; }
        RGB operator-(const RGB& c) const { return {r - c.r, g - c.g, b - c.b}; }
        RGB operator*(T s) const { return {r * s, g * s, b * s}; }
        friend RGB operator*(T s, const RGB& c) { return c * s; }
        RGB operator*(const RGB& c) const { return {r * c.r, g * c.g, b * c.b}; }
        RGB operator/(T s) const { return {r / s, g / s, b / s}; }

        RGB& operator+=(const RGB& c) { r += c.r; g += c.g; b += c.b; return *this; }
        RGB& operator-=(const RGB& c) { r -= c.r; g -= c.g; b -= c.b; return *this; }
        RGB& operator*=(T s) { r *= s; g *= s; b *= s; return *this; }

        bool operator==(const RGB&) const = default;

        /// [0,1] の範囲にクランプ
        RGB clamped() const {
            return {std::clamp(r, T{0}, T{1}),
                    std::clamp(g, T{0}, T{1}),
                    std::clamp(b, T{0}, T{1})};
        }

        /// グレースケール (ITU-R BT.601)
        T luminance() const { return T{0.299} * r + T{0.587} * g + T{0.114} * b; }
    };

    /// RGBA 色型 (アルファ付き)
    template<typename T>
    struct RGBA {
        T r{}, g{}, b{}, a{1};

        RGBA() = default;
        RGBA(T r, T g, T b, T a = T{1}) : r(r), g(g), b(b), a(a) {}
        explicit RGBA(const RGB<T>& c, T a = T{1}) : r(c.r), g(c.g), b(c.b), a(a) {}

        RGB<T> rgb() const { return {r, g, b}; }
        bool operator==(const RGBA&) const = default;
    };

    /// HSV → RGB 変換
    template<typename T>
    RGB<T> hsvToRgb(T h, T s, T v) {
        if (s <= T{0}) return {v, v, v};
        if (h >= T{360}) h = T{0};
        h /= T{60};
        int i = static_cast<int>(h);
        T f = h - i;
        T p = v * (T{1} - s);
        T q = v * (T{1} - s * f);
        T t = v * (T{1} - s * (T{1} - f));
        switch (i) {
            case 0: return {v, t, p};
            case 1: return {q, v, p};
            case 2: return {p, v, t};
            case 3: return {p, q, v};
            case 4: return {t, p, v};
            default: return {v, p, q};
        }
    }

    /// RGB → HSV 変換 (h: 0-360, s: 0-1, v: 0-1)
    template<typename T>
    void rgbToHsv(const RGB<T>& c, T& h, T& s, T& v) {
        T maxC = std::max({c.r, c.g, c.b});
        T minC = std::min({c.r, c.g, c.b});
        T delta = maxC - minC;

        v = maxC;
        s = (maxC > T{0}) ? delta / maxC : T{0};

        if (delta <= T{0}) {
            h = T{0};
        } else if (maxC == c.r) {
            h = T{60} * std::fmod((c.g - c.b) / delta, T{6});
        } else if (maxC == c.g) {
            h = T{60} * ((c.b - c.r) / delta + T{2});
        } else {
            h = T{60} * ((c.r - c.g) / delta + T{4});
        }
        if (h < T{0}) h += T{360};
    }

    /// ストリーム出力
    template<typename T>
    std::ostream& operator<<(std::ostream& os, const RGB<T>& c) {
        return os << "RGB(" << c.r << ", " << c.g << ", " << c.b << ")";
    }

    template<typename T>
    std::ostream& operator<<(std::ostream& os, const RGBA<T>& c) {
        return os << "RGBA(" << c.r << ", " << c.g << ", " << c.b << ", " << c.a << ")";
    }

    /// 便利な型エイリアス
    using RGB8 = RGB<std::uint8_t>;
    using RGBf = RGB<float>;
    using RGBd = RGB<double>;
    using RGBA8 = RGBA<std::uint8_t>;
    using RGBAf = RGBA<float>;
    using RGBAd = RGBA<double>;

} // namespace calx::image

#endif // CALX_IMAGE_TYPES_RGB_HPP
