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

// pyramid.hpp — ガウシアン画像ピラミッド
//
// 新規実装:
//   - ガウシアンぼかし + 2x ダウンサンプリング
//   - Lucas-Kanade フロー、特徴点検出の前提

#ifndef CALX_IMAGE_PYRAMID_HPP
#define CALX_IMAGE_PYRAMID_HPP

#include <image/Image.hpp>
#include <image/filters/ndimage.hpp>
#include <cassert>
#include <vector>

namespace calx::image {

    /// ガウシアンピラミッドを構築
    /// @param img     入力画像 (1ch)
    /// @param levels  ピラミッドのレベル数 (元画像含む)
    /// @param sigma   ガウシアンカーネルの σ
    /// @return        ピラミッド (levels[0] = 元画像, levels[i] = 1/2^i 解像度)
    template<typename T>
    std::vector<Image<T>> buildPyramid(const Image<T>& img, int levels, T sigma = T{1.5}) {
        assert(levels >= 1);
        assert(img.numChannels() == 1);

        std::vector<Image<T>> pyramid;
        pyramid.reserve(levels);
        pyramid.push_back(img);

        int kernelSize = static_cast<int>(std::ceil(sigma * 3)) * 2 + 1;
        if (kernelSize < 3) kernelSize = 3;

        for (int lv = 1; lv < levels; ++lv) {
            const auto& prev = pyramid.back();
            std::size_t ph = prev.height();
            std::size_t pw = prev.width();

            if (ph < 4 || pw < 4) break;

            // ガウシアンぼかし
            Matrix<T> kernel = gaussianKernel<T>(kernelSize, sigma);
            Matrix<T> blurred = convolve(prev.channel(0), kernel);

            // 2x ダウンサンプリング
            std::size_t nh = ph / 2;
            std::size_t nw = pw / 2;
            Matrix<T> down(nh, nw);
            for (std::size_t y = 0; y < nh; ++y) {
                for (std::size_t x = 0; x < nw; ++x) {
                    down(y, x) = blurred(y * 2, x * 2);
                }
            }

            pyramid.emplace_back(std::move(down));
        }

        return pyramid;
    }

    /// 多チャンネル画像のガウシアンピラミッド
    /// 各チャンネルに対して個別にピラミッドを構築し、
    /// レベルごとに多チャンネル Image に再構成する
    template<typename T>
    std::vector<Image<T>> buildPyramidMultiChannel(const Image<T>& img, int levels, T sigma = T{1.5}) {
        if (img.numChannels() == 1) return buildPyramid(img, levels, sigma);

        // 各チャンネルのピラミッドを構築
        std::vector<std::vector<Image<T>>> chPyramids;
        for (int ch = 0; ch < img.numChannels(); ++ch) {
            Image<T> singleCh(img.channel(ch));
            chPyramids.push_back(buildPyramid(singleCh, levels, sigma));
        }

        // レベルごとに再構成
        int actualLevels = static_cast<int>(chPyramids[0].size());
        std::vector<Image<T>> pyramid;
        pyramid.reserve(actualLevels);

        for (int lv = 0; lv < actualLevels; ++lv) {
            std::size_t h = chPyramids[0][lv].height();
            std::size_t w = chPyramids[0][lv].width();
            Image<T> merged(h, w, img.numChannels());
            for (int ch = 0; ch < img.numChannels(); ++ch) {
                merged.channel(ch) = chPyramids[ch][lv].channel(0);
            }
            pyramid.push_back(std::move(merged));
        }

        return pyramid;
    }

} // namespace calx::image

#endif // CALX_IMAGE_PYRAMID_HPP
