//=============================================================================
#ifndef IMAGE_SMOOTH_LAPLACE_ZERO_H
#define IMAGE_SMOOTH_LAPLACE_ZERO_H
//=============================================================================

//== INCLUDES =================================================================


#include <opencv2/opencv.hpp>

#include <map>
#include <array>

#include <Eigen/Dense>
#include <Eigen/Sparse>

#include "Texture_neighborhood.h"

//== CLASS DEFINITION =========================================================


class
Image_smooth_laplace_zero
{

    typedef Eigen::SparseMatrix<double> SpMat;
    typedef Eigen::Triplet<double>      Tripl;

public:

    Image_smooth_laplace_zero(const cv::Mat& source_image,
                              cv::Mat& source_mask,
                              cv::Mat& result_image,
                              const Texel_neighborhood& neighborhood = Texel_neighborhood());

private:

    enum Segment { inside,
                   boundary,
                   dirichlet,
                   outside };

    void segment_mask(const cv::Mat& source_mask, cv::Mat& segmentation, const Texel_neighborhood& neighborhood) const;

    void get_inner_coordinates(const cv::Mat& segmentation, std::vector<cv::Point>& result) const;

    void get_mapping_pixelcoordinate_to_idx(const cv::Mat& segmentation, std::map< unsigned int, std::map<unsigned int, unsigned int> >& mapping) const;

    Image_smooth_laplace_zero::SpMat assemble_A(const cv::Mat& segmentation, const std::vector<cv::Point>& inner_coordinates, const std::map< unsigned int, std::map<unsigned int, unsigned int> >& mapping_pixelcoordinate_to_idx,
                                                const Texel_neighborhood& neighborhood) const;

    Eigen::VectorXd assemble_b(const cv::Mat& segmentation, const std::vector<cv::Point>& inner_coordinates, const cv::Mat& src_channel,
                               const Texel_neighborhood& neighborhood) const;

    void solution_to_cvMat(const cv::Mat& segmentation,
                           const std::vector<cv::Point>& inner_coordinates,
                           const Eigen::VectorXd& x_eigen_b,
                           const Eigen::VectorXd& x_eigen_g,
                           const Eigen::VectorXd& x_eigen_r,
                           cv::Mat& result_channel_b,
                           cv::Mat& result_channel_g,
                           cv::Mat& result_channel_r,
                           const cv::Mat& target_channel_b,
                           const cv::Mat& target_channel_g,
                           const cv::Mat& target_channel_r) const;

    const bool solve_sle(const SpMat& A,
                         const Eigen::VectorXd& b_b,
                         const Eigen::VectorXd& b_g,
                         const Eigen::VectorXd& b_r,
                         Eigen::VectorXd& x_b,
                         Eigen::VectorXd& x_g,
                         Eigen::VectorXd& x_r,
                         const bool is_symmetric) const;

};


//=============================================================================
#endif // IMAGE_SMOOTH_LAPLACE_ZERO_H
//=============================================================================
