//=============================================================================
#ifndef GRAPHENE_TEXTURE_PROCESSING_H
#define GRAPHENE_TEXTURE_PROCESSING_H
//=============================================================================

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

#include <array>

#include <opencv2/opencv.hpp>

#include <graphene/surface_mesh/scene_graph/Surface_mesh_node.h>
#include <graphene/character/scene_graph/Character_node.h>

#include <graphene/surface_mesh/algorithms/texture_processing/utility/Color_warp_rbf.h>

#include <config/Gender.h>

#include "Texture_neighborhood.h"


//== NAMESPACE ================================================================


namespace graphene {
namespace utility {


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


class Texture_processing
{

public:


    // constructor
    Texture_processing();

    bool  compute_texture_halfscan_graph_cut(const std::string& partial_texture_generation_script,
            const std::string& texture_merge_script,
            const std::vector<std::string>& project_directories,
            const std::vector<std::string>& filenames_invtrans_meshes,
            const std::vector<std::string>& filenames_masks,
            const std::string& fn_mask,
            const std::string& fn_critical_overlap_region,
            const std::string& fn_texture);

    bool  compute_texture_graph_cut(const std::string& texture_generation_script,
            const std::string& project_dir,
            const std::string& fn_invtrans_mesh,
            const std::string& fn_mask,
            const std::string& fn_texture);

    bool compute_texture_metashape(const std::string& metashape_exec,
            const std::string& scancap_dir,
            const std::string& fn_metashape_project,
            const std::string& fn_invtrans_mesh,
            const std::string& fn_metashape_texture,
            const int texture_resolution = 4096);

    bool compute_texture_transfer_apple(const std::string& fn_invtrans_mesh,
                                        const std::string& fn_apple_mesh,
                                        const std::string& fn_avatar_tex,
                                        const std::string& fn_apple_tex,
                                        const std::string& fn_apple_nm,
                                        const std::string& fn_apple_ao,
                                        const std::string& fn_apple_roughness,
                                        const std::string& fn_mask,
                                        const std::string& fn_save_location,
                                        const int texture_resolution = 4096);

    bool texturemerge(gl::Texture_type dst_texturetype,
                      const std::string& src_texturefilename,
                      const std::string& mask_filename,
                      const std::string &dirichlet_mask_filename,
                      bool use_poisson,
                      scene_graph::Surface_mesh_node& mesh_node);

    bool repair_hand_texture(const std::string &dir_template_db,
                             const std::string& fn_mask_hands_comparison,
                             const std::string& fn_mask_hands_repair,
                             const std::string& fn_mask_hands_repair_start,
                             const std::string& fn_mask_hands_repair_min,
                             const std::string& fn_mask_hands_repair_max,
                             const std::string &fn_mask_hands_dirichlet,
                             scene_graph::Surface_mesh_node& mesh_node,
                             config::Gender gender = config::GENDER_UNSPECIFIED);

    bool merge_hand_texture(const std::string& fn_hand_texture,
                            const std::string& fn_hand_mask,
                            const std::string& fn_hand_mask_dirichlet,
                            scene_graph::Surface_mesh_node& mesh_node,
                            const std::string& fn_histogram_matching = "");

    bool repair_armpit_texture(
            const std::string &fn_mask_armpit_repair,
            scene_graph::Surface_mesh_node &mesh_node);

    bool adjust_eye_and_teeth_luminance(
            const std::string &fn_mask_face_and_ears,
            const std::string &fn_mask_eyes_and_teeth_inverse,
            bool invert_target_mask,
            bool invert_direction,
            scene_graph::Surface_mesh_node& mesh_node
            );

    bool adjust_iris_color(scene_graph::Character_node& mesh_node,
                           const std::string& fn_frontal_image,
                           const std::string& fn_contour_eyes,
                           const std::string& eyemesh_left_name,
                           const std::string& eyemesh_right_name);


public:


    // ... binary mask in [0 , 255] with one channel ...
    bool gaussian_blur(cv::Mat& image, const cv::Mat& image_mask, const unsigned int size, bool& resized) const;

    // ... continuous mask in [0 , 255] with one channel ...
    bool contrast_brightness(cv::Mat& image, const cv::Mat& image_mask, const double contrast_parameter, const double brightness_parameter, bool& resized) const;

    bool kmeans(cv::Mat& image, const int cluster_count) const;

    bool linear_blend(cv::Mat& image, const cv::Mat& image_A, const cv::Mat& image_B, const cv::Mat& image_mask, const double val, bool& resized) const;

    bool copy_and_paste(cv::Mat& image, const cv::Mat& image_src, bool& resized) const;
    bool copy_and_paste(cv::Mat& image, const cv::Mat& image_src, const cv::Mat& image_mask, bool& resized) const;

    bool seamless_clone(cv::Mat& image, const cv::Mat& image_src, const cv::Mat& image_mask, bool& resized,
                        const bool use_opencv_PIE = false, const cv::Mat& dirichlet_mask = cv::Mat(),
                        const Texel_neighborhood& mapping_neighborhood = Texel_neighborhood()) const;

    // source_image:     source image with luminance we like to have
    // target_image:     target image with luminance to adjust
    // source_mask:      corresponding mask for mean-luminance computation
    // target_mask:      region to modify from target_image
    // reference_mask:   corresponding mask for mean-luminance computation
    // resized:          did we resize the target image?
    // invert_direction: direction from mean_src to mean_tar or the other way around
    bool luminance_mean_shift(const cv::Mat& source_image, cv::Mat& target_image,
                              const cv::Mat& source_mask, const cv::Mat& target_mask,
                              const cv::Mat& reference_mask, bool& resized,
                              const bool invert_direction = false,
                              const bool clamp_mean_shift = false) const;

    bool luminance_mean_shift(const unsigned int mean_luminance, cv::Mat& target_image,
                              const cv::Mat& target_mask, const cv::Mat& reference_mask,
                              bool& resized) const;

    bool color_warp_rbf(const std::vector<Color_warp_rbf::Color_constraint>& color_constraints_lab, cv::Mat& target_image, const cv::Mat& target_mask, bool& resized) const;

    bool generate_mask(const int dim_texture, graphene::surface_mesh::Surface_mesh& mesh, cv::Mat& image_mask) const;

    bool generate_selection(graphene::surface_mesh::Surface_mesh& mesh, const cv::Mat& image_mask) const;

    bool compute_mask_from_black_areas(cv::Mat& out_mask, const cv::Mat& image, const cv::Mat& source_mask) const;

private:

    //helpers
    bool round_to_next_valid_pixel(const double pix_i, const double pix_j, const cv::Mat& texture_colored, graphene::Vec2ui& result) const;

    void fill_holes(cv::Mat& image_no_holes, const cv::Mat& image_with_holes);

    void split_mask_upper_lower(cv::Mat& mask_upper, cv::Mat& mask_lower, const cv::Mat& mask);

    bool size_differs(std::vector<cv::Mat>& images, cv::Size& largest_size);


};


//=============================================================================
} // namespace utility
} // namespace graphene
//=============================================================================
#endif // GRAPHENE_TEXTURE_PROCESSING_H
//=============================================================================
