#ifndef LIMAP_OPTIMIZE_LINE_REFINEMENT_REFINEMENT_CONFIG_H_
#define LIMAP_OPTIMIZE_LINE_REFINEMENT_REFINEMENT_CONFIG_H_

#include <pybind11/pybind11.h>
#include <pybind11/numpy.h>
#include "_limap/helpers.h"

#include <ceres/ceres.h>
#include "ceresbase/interpolation.h"

namespace py = pybind11;

namespace limap
{

  namespace optimize
  {

    namespace line_refinement
    {

      class RefinementConfig
      {
      public:
        RefinementConfig()
        {
          line_geometric_loss_function.reset(new ceres::CauchyLoss(0.25));
          vp_loss_function.reset(new ceres::TrivialLoss());
          heatmap_loss_function.reset(new ceres::HuberLoss(0.001));
          fconsis_loss_function.reset(new ceres::CauchyLoss(0.25));

          solver_options.function_tolerance = 0.0;
          solver_options.gradient_tolerance = 0.0;
          solver_options.parameter_tolerance = 0.0;
          solver_options.minimizer_progress_to_stdout = true;
          solver_options.max_num_iterations = 100;
          solver_options.max_linear_solver_iterations = 200;
          solver_options.max_num_consecutive_invalid_steps = 10;
          solver_options.max_consecutive_nonmonotonic_steps = 10;
          solver_options.num_threads = -1;
          solver_options.logging_type = ceres::PER_MINIMIZER_ITERATION; // PER_MINIMIZER_ITERATION, SILENT
#if CERES_VERSION_MAJOR < 2
          solver_options.num_linear_solver_threads = -1;
#endif // CERES_VERSION_MAJOR
          problem_options.loss_function_ownership = ceres::DO_NOT_TAKE_OWNERSHIP;
        }
        RefinementConfig(py::dict dict) : RefinementConfig()
        {
          ASSIGN_PYDICT_ITEM(dict, use_geometric, bool);
          ASSIGN_PYDICT_ITEM(dict, min_num_images, int);
          ASSIGN_PYDICT_ITEM(dict, num_outliers_aggregate, int);
          ASSIGN_PYDICT_ITEM(dict, print_summary, bool);
          ASSIGN_PYDICT_ITEM(dict, geometric_alpha, double);
          ASSIGN_PYDICT_ITEM(dict, vp_multiplier, double);

          ASSIGN_PYDICT_ITEM(dict, sample_range_min, double);
          ASSIGN_PYDICT_ITEM(dict, sample_range_max, double);
          ASSIGN_PYDICT_ITEM(dict, n_samples_heatmap, int);
          ASSIGN_PYDICT_ITEM(dict, heatmap_multiplier, double);
          ASSIGN_PYDICT_ITEM(dict, n_samples_feature, int);
          ASSIGN_PYDICT_ITEM(dict, use_ref_descriptor, bool);
          ASSIGN_PYDICT_ITEM(dict, ref_multiplier, double);
          ASSIGN_PYDICT_ITEM(dict, fconsis_multiplier, double);
        }
        bool use_geometric = true;
        int min_num_images = 4;
        int num_outliers_aggregate = 2;

        // solver config
        ceres::Solver::Options solver_options;
        ceres::Problem::Options problem_options;
        bool print_summary = true;

        // geometric config
        std::shared_ptr<ceres::LossFunction> line_geometric_loss_function;
        double geometric_alpha = 10.0;

        // vp config
        std::shared_ptr<ceres::LossFunction> vp_loss_function;
        double vp_multiplier = 1.0;

        // heatmap config
        double sample_range_min = 0.05;
        double sample_range_max = 0.95;
        int n_samples_heatmap = 10;
        InterpolationConfig heatmap_interpolation_config;
        std::shared_ptr<ceres::LossFunction> heatmap_loss_function;
        double heatmap_multiplier = 1.0;

        // feature config
        int n_samples_feature = 100;
        bool use_ref_descriptor = false;
        double ref_multiplier = 5.0;
        InterpolationConfig feature_interpolation_config;
        std::shared_ptr<ceres::LossFunction> fconsis_loss_function;
        double fconsis_multiplier = 1.0;
      };

    } // namespace line_refinement

  } // namespace optimize

} // namespace limap

#endif
