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

/**
 * @file Gradient.hpp
 * @brief グラデーション (7b-8: 線形/放射状/円錐)
 */

#ifndef CALX_IMAGE_DRAW_GRADIENT_HPP
#define CALX_IMAGE_DRAW_GRADIENT_HPP

#include <image/Image.hpp>
#include <vector>
#include <cmath>
#include <algorithm>

namespace calx::image {

/// グラデーションストップ
template<typename T>
struct GradientStop {
    double position;   // [0, 1]
    std::vector<T> color;
};

enum class GradientSpread { Pad, Repeat, Reflect };

/// グラデーションの色を position [0,1] から補間
template<typename T>
std::vector<T> interpolateGradient(const std::vector<GradientStop<T>>& stops, double t) {
    if (stops.empty()) return {};
    if (t <= stops.front().position) return stops.front().color;
    if (t >= stops.back().position) return stops.back().color;
    for (std::size_t i = 0; i + 1 < stops.size(); ++i) {
        if (t >= stops[i].position && t <= stops[i+1].position) {
            double f = (t - stops[i].position) / (stops[i+1].position - stops[i].position);
            std::vector<T> c(stops[i].color.size());
            for (std::size_t k = 0; k < c.size(); ++k)
                c[k] = stops[i].color[k] * T(1-f) + stops[i+1].color[k] * T(f);
            return c;
        }
    }
    return stops.back().color;
}

/// 線形グラデーション塗りつぶし
template<typename T>
void fillLinearGradient(Image<T>& img,
                         double x0, double y0, double x1, double y1,
                         const std::vector<GradientStop<T>>& stops,
                         GradientSpread spread = GradientSpread::Pad) {
    double dx = x1-x0, dy = y1-y0, len2 = dx*dx + dy*dy;
    if (len2 < 1e-12) return;

    for (std::size_t y = 0; y < img.height(); ++y)
        for (std::size_t x = 0; x < img.width(); ++x) {
            double t = ((x-x0)*dx + (y-y0)*dy) / len2;
            if (spread == GradientSpread::Repeat) t = t - std::floor(t);
            else if (spread == GradientSpread::Reflect) {
                t = t - std::floor(t);
                if (int(t * 2) % 2 == 1) t = 1.0 - t;
            } else t = std::clamp(t, 0.0, 1.0);
            auto c = interpolateGradient(stops, t);
            for (int ch = 0; ch < img.numChannels() && ch < static_cast<int>(c.size()); ++ch)
                img.at(y, x, ch) = c[ch];
        }
}

/// 放射状グラデーション塗りつぶし
template<typename T>
void fillRadialGradient(Image<T>& img,
                         double cx, double cy, double radius,
                         const std::vector<GradientStop<T>>& stops,
                         GradientSpread spread = GradientSpread::Pad) {
    for (std::size_t y = 0; y < img.height(); ++y)
        for (std::size_t x = 0; x < img.width(); ++x) {
            double d = std::sqrt((x-cx)*(x-cx) + (y-cy)*(y-cy));
            double t = d / radius;
            if (spread == GradientSpread::Repeat) t = t - std::floor(t);
            else if (spread == GradientSpread::Reflect) {
                t = t - std::floor(t);
                if (int(t * 2) % 2 == 1) t = 1.0 - t;
            } else t = std::clamp(t, 0.0, 1.0);
            auto c = interpolateGradient(stops, t);
            for (int ch = 0; ch < img.numChannels() && ch < static_cast<int>(c.size()); ++ch)
                img.at(y, x, ch) = c[ch];
        }
}

/// 円錐グラデーション (conic/sweep)
template<typename T>
void fillConicGradient(Image<T>& img,
                        double cx, double cy, double startAngle,
                        const std::vector<GradientStop<T>>& stops) {
    constexpr double pi2 = 6.283185307179586;
    for (std::size_t y = 0; y < img.height(); ++y)
        for (std::size_t x = 0; x < img.width(); ++x) {
            double angle = std::atan2(double(y) - cy, double(x) - cx) - startAngle;
            while (angle < 0) angle += pi2;
            double t = angle / pi2;
            auto c = interpolateGradient(stops, t);
            for (int ch = 0; ch < img.numChannels() && ch < static_cast<int>(c.size()); ++ch)
                img.at(y, x, ch) = c[ch];
        }
}

} // namespace calx::image

#endif
