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

// Image.hpp — 多チャンネル画像クラス (宣言)
//
// 実装は Image_impl.hpp に分離。
// 明示的インスタンス化: src/image/Image.cpp (float, double)
// 将来の SIMD/ASM 特化を可能にする構成。

#ifndef CALX_IMAGE_IMAGE_HPP
#define CALX_IMAGE_IMAGE_HPP

#include <math/core/matrix.hpp>
#include <image/types/RGB.hpp>
#include <algorithm>
#include <cassert>
#include <cmath>
#include <cstdint>
#include <stdexcept>
#include <vector>

namespace calx::image {

// ========================================================================
// 線分アンチエイリアス設定
// ========================================================================

enum class LineAA {
    None,             // Bresenham (アンチエイリアスなし、デフォルト)
    Wu,               // Wu's line algorithm (thickness=1 向き)
    GuptaSproull,     // Gupta-Sproull (Bresenham + フィルタカーネル)
    AreaSampling,     // 面積サンプリング (ピクセルと線分の重なり面積)
    SuperSampling,    // スーパーサンプリング (N×N サブピクセル)
    SDF               // 符号付き距離場 (smoothstep)
};

struct LineAAParams {
    LineAA method = LineAA::None;
    double filterRadius = 1.5;
    int superSampleN = 4;
    double sdfSmooth = 0.7;

    static LineAAParams none() { return {}; }
    static LineAAParams wu() { return {LineAA::Wu}; }
    static LineAAParams guptaSproull(double r = 1.5) { return {LineAA::GuptaSproull, r}; }
    static LineAAParams areaSampling() { return {LineAA::AreaSampling}; }
    static LineAAParams superSampling(int n = 4) { return {LineAA::SuperSampling, 1.5, n}; }
    static LineAAParams sdf(double smooth = 0.7) { return {LineAA::SDF, 1.5, 4, smooth}; }
};

// ========================================================================
// 線端・角スタイル (DRW-16, DRW-17)
// ========================================================================

enum class LineCap { Butt, Round, Square };
enum class LineJoin { Miter, Round, Bevel };

// ========================================================================
// ストロークスタイル (破線・cap・join 統合)
// ========================================================================

struct StrokeStyle {
    LineCap cap = LineCap::Butt;
    LineJoin join = LineJoin::Miter;
    double miterLimit = 10.0;

    // 破線パターン (DRW-15): {dash, gap, dash, gap, ...}
    std::vector<double> dashPattern;
    double dashOffset = 0;

    static StrokeStyle solid() { return {}; }
    static StrokeStyle dashed(double dash = 6, double gap = 4) {
        StrokeStyle s; s.dashPattern = {dash, gap}; return s;
    }
    static StrokeStyle dotted(double dot = 2, double gap = 3) {
        StrokeStyle s; s.dashPattern = {dot, gap}; return s;
    }
    static StrokeStyle dashDot(double dash = 8, double dot = 2, double gap = 3) {
        StrokeStyle s; s.dashPattern = {dash, gap, dot, gap}; return s;
    }
};

// ========================================================================
// Image<T> クラス
// ========================================================================

template<typename T>
    class Image {
    public:
        Image() = default;
        Image(std::size_t height, std::size_t width, T value = T{0})
            : channels_(1, Matrix<T>(height, width, value)) {}
        Image(std::size_t height, std::size_t width, int numChannels, T value = T{0})
            : channels_(numChannels, Matrix<T>(height, width, value)) {
            assert(numChannels >= 1 && numChannels <= 4);
        }
        explicit Image(const Matrix<T>& mat) : channels_(1, mat) {}
        explicit Image(Matrix<T>&& mat) : channels_(1, std::move(mat)) {}

        // ── 基本プロパティ (インライン) ──
        std::size_t height() const { return channels_.empty() ? 0 : channels_[0].rows(); }
        std::size_t width()  const { return channels_.empty() ? 0 : channels_[0].cols(); }
        int numChannels()    const { return static_cast<int>(channels_.size()); }
        bool empty()         const { return channels_.empty() || height() == 0; }
        Matrix<T>&       channel(int ch)       { return channels_[ch]; }
        const Matrix<T>& channel(int ch) const { return channels_[ch]; }

        // ── ピクセルアクセス (インライン) ──
        T& at(std::size_t y, std::size_t x) { return channels_[0](y, x); }
        const T& at(std::size_t y, std::size_t x) const { return channels_[0](y, x); }
        T& operator()(std::size_t y, std::size_t x) { return channels_[0](y, x); }
        const T& operator()(std::size_t y, std::size_t x) const { return channels_[0](y, x); }
        T& at(std::size_t y, std::size_t x, int ch) { return channels_[ch](y, x); }
        const T& at(std::size_t y, std::size_t x, int ch) const { return channels_[ch](y, x); }

        RGB<T> rgb(std::size_t y, std::size_t x) const {
            assert(numChannels() >= 3);
            return {channels_[0](y, x), channels_[1](y, x), channels_[2](y, x)};
        }
        void setRgb(std::size_t y, std::size_t x, const RGB<T>& c) {
            assert(numChannels() >= 3);
            channels_[0](y, x) = c.r; channels_[1](y, x) = c.g; channels_[2](y, x) = c.b;
        }

        // ── 変換 (インライン) ──
        Image<T> toGray() const;
        Image<T> toRgb() const;

        // ── 描画ヘルパー (実装は Image_impl.hpp) ──
        void drawLine(int x0, int y0, int x1, int y1,
                      const std::vector<T>& color, int thickness = 1,
                      const LineAAParams& aa = LineAAParams::none());

        void drawCircle(int cx, int cy, int radius,
                        const std::vector<T>& color, int thickness = 1,
                        const LineAAParams& aa = LineAAParams::none(),
                        double startAngle = 0, double endAngle = 360);

        void drawEllipse(int cx, int cy, int rx, int ry, double angle,
                         const std::vector<T>& color, int thickness = 1,
                         const LineAAParams& aa = LineAAParams::none(),
                         double startAngle = 0, double endAngle = 360);

        void drawRect(int x0, int y0, int x1, int y1,
                      const std::vector<T>& color, int thickness = 1,
                      const LineAAParams& aa = LineAAParams::none(),
                      double angle = 0);

        void drawText(int x, int y, const std::string& text,
                      const std::vector<T>& color, int scale = 1);

        // ── 追加描画プリミティブ (DRW-1〜10) ──
        void drawPolyline(const std::vector<std::pair<int,int>>& pts,
                          const std::vector<T>& color, int thickness = 1,
                          const LineAAParams& aa = LineAAParams::none(),
                          bool closed = false);

        void drawPolygon(const std::vector<std::pair<int,int>>& pts,
                         const std::vector<T>& color, int thickness = 1,
                         const LineAAParams& aa = LineAAParams::none(),
                         bool filled = false);

        void drawRoundedRect(int x0, int y0, int x1, int y1, int radius,
                             const std::vector<T>& color, int thickness = 1,
                             const LineAAParams& aa = LineAAParams::none(),
                             bool filled = false);

        void drawBezier(int x0, int y0, int x1, int y1,
                        int x2, int y2, int x3, int y3,
                        const std::vector<T>& color, int thickness = 1,
                        const LineAAParams& aa = LineAAParams::none(),
                        int segments = 32);

        void drawQuadBezier(int x0, int y0, int x1, int y1, int x2, int y2,
                            const std::vector<T>& color, int thickness = 1,
                            const LineAAParams& aa = LineAAParams::none(),
                            int segments = 32);

        void drawCatmullRom(const std::vector<std::pair<int,int>>& pts,
                            const std::vector<T>& color, int thickness = 1,
                            const LineAAParams& aa = LineAAParams::none(),
                            int segmentsPerSpan = 16);

        void fillCircle(int cx, int cy, int radius, const std::vector<T>& color);
        void fillEllipse(int cx, int cy, int rx, int ry, double angle,
                         const std::vector<T>& color);
        void fillRect(int x0, int y0, int x1, int y1,
                      const std::vector<T>& color, double angle = 0);

        void floodFill(int x, int y, const std::vector<T>& color,
                       bool connect8 = false);

        void drawArrow(int x0, int y0, int x1, int y1,
                       const std::vector<T>& color, int thickness = 1,
                       const LineAAParams& aa = LineAAParams::none(),
                       double headLength = 10, double headAngle = 30);

        enum class MarkerType { Cross, X, Diamond, Square, Circle, Triangle };
        void drawMarker(int x, int y, MarkerType type, int size,
                        const std::vector<T>& color, int thickness = 1,
                        const LineAAParams& aa = LineAAParams::none());

        void drawPie(int cx, int cy, int radius,
                     double startAngle, double endAngle,
                     const std::vector<T>& color, int thickness = 1,
                     const LineAAParams& aa = LineAAParams::none(),
                     bool filled = false);

        // ── DRW-15: 破線描画 ──
        void drawDashedLine(int x0, int y0, int x1, int y1,
                            const std::vector<T>& color, int thickness = 1,
                            const LineAAParams& aa = LineAAParams::none(),
                            const StrokeStyle& stroke = StrokeStyle::dashed());

        // ── DRW-22: サブピクセル座標 API (double 版) ──
        void drawLineF(double x0, double y0, double x1, double y1,
                       const std::vector<T>& color, int thickness = 1,
                       const LineAAParams& aa = LineAAParams::sdf());

        void drawCircleF(double cx, double cy, double radius,
                         const std::vector<T>& color, int thickness = 1,
                         const LineAAParams& aa = LineAAParams::sdf());

        // ── リサイズ・ROI ──
        Image<T> resize(std::size_t newH, std::size_t newW) const;
        Image<T> roi(std::size_t y, std::size_t x, std::size_t h, std::size_t w) const;

        // ── 内部ヘルパー (実装は Image_impl.hpp) ──
        void blendPixel(int px, int py, const std::vector<T>& color, T alpha);

    private:
        std::vector<Matrix<T>> channels_;

        static double pointLineSegDist(double px, double py,
                                        double ax, double ay, double bx, double by);

        void drawLineBresenham(int x0, int y0, int x1, int y1,
                               const std::vector<T>& color, int thickness);
        void drawLineWu(int x0, int y0, int x1, int y1,
                        const std::vector<T>& color);
        void drawLineGuptaSproull(int x0, int y0, int x1, int y1,
                                  const std::vector<T>& color, int thickness,
                                  double filterRadius);
        void drawLineArea(int x0, int y0, int x1, int y1,
                          const std::vector<T>& color, int thickness);
        void drawLineSuperSample(int x0, int y0, int x1, int y1,
                                 const std::vector<T>& color, int thickness, int N);
        void drawLineSDF(int x0, int y0, int x1, int y1,
                         const std::vector<T>& color, int thickness, double smooth);

    public:
        void fillPolygon(const std::vector<std::pair<int,int>>& pts,
                         const std::vector<T>& color);
    };

    // 便利な型エイリアス
    using ImageF = Image<float>;
    using ImageD = Image<double>;
    using Image8 = Image<uint8_t>;

} // namespace calx::image

// 明示的インスタンス化 (float, double) は src/image/Image.cpp で実施。
// Image<float>, Image<double> 以外の型を使う場合のみ Image_impl.hpp を直接インクルード:
//   #include <image/Image_impl.hpp>

#endif // CALX_IMAGE_IMAGE_HPP
