#include <cctbx/boost_python/flex_fwd.h>

#include <cctbx/xray/twin_targets.h>

#include <boost/python/module.hpp>
#include <boost/python/def.hpp>
#include <boost/python/class.hpp>
#include <boost/python/args.hpp>
#include <boost/python/tuple.hpp>
#include <boost/python/return_value_policy.hpp>
#include <boost/python/copy_const_reference.hpp>
#include <scitbx/boost_python/is_polymorphic_workaround.h>

SCITBX_BOOST_IS_POLYMORPHIC_WORKAROUND(
  cctbx::xray::twin_targets::single_twin_likelihood<double>)

namespace cctbx { namespace xray { namespace twin_targets { namespace boost_python {
  namespace {




    struct twin_completion_wrappers
    {
      typedef twin_completion<double> w_t;
      static void
      wrap()
      {
        using namespace boost::python;

        class_<w_t>("twin_completion", no_init)
          .def(init< scitbx::af::const_ref< cctbx::miller::index<> > const&,
                     sgtbx::space_group const&,
                     bool const&,
                     scitbx::mat3<double> const& >
               (( arg("hkl"),
                  arg("space_group"),
                  arg("anomalous_flag"),
                  arg("twin_law") )))
          .def("twin_complete", &w_t::twin_complete )
          .def("check_free_flags", &w_t::check_free_flags)
          .def("get_free_model_selection", &w_t::get_free_model_selection)
          .def("twin_sum", &w_t::twin_sum)
          ;
      }


    };








    struct least_squares_hemihedral_twinning_on_i_wrappers
    {
      typedef least_squares_hemihedral_twinning_on_i<double> w_t;

      // A thin wrapper that wraps up the derivatives
      static boost::python::tuple
      d_target_d_ab(w_t const& self, scitbx::af::const_ref<std::complex<double> > const& f_model)
      {
        scitbx::af::tiny<scitbx::af::shared<double>, 2> result;
        result = self.d_target_d_ab( f_model );
        return boost::python::make_tuple( result[0], result[1] );
      }

      static void
      wrap()
      {
        using namespace boost::python;


        class_<w_t>("least_squares_hemihedral_twinning_on_i", no_init)
          .def(init<
               scitbx::af::const_ref< cctbx::miller::index<> > const&,  // 1 indices
               scitbx::af::const_ref< double > const&,                  // 2 i_obs
               scitbx::af::const_ref< double > const&,                  // 3 w_obs
               scitbx::af::const_ref< cctbx::miller::index<> > const&,  // 4 hl_obs
               sgtbx::space_group const&,                               // 5 space group
               bool const&,                                             // 6 anomalous flag
               double const&,                                           // 7 alpha
               scitbx::mat3<double> const&                              // 8 twin law)
               >
               ((arg("hkl_obs"),
                 arg("i_obs"),
                 arg("w_obs"),
                 arg("hkl_calc"),
                 arg("space_group"),
                 arg("anomalous_flag"),
                 arg("alpha"),
                 arg("twin_law")
                 )))
          .def("target", &w_t::target)
          .def("d_target_d_ab", d_target_d_ab )
          .def("d_target_d_fmodel", &w_t::d_target_d_fmodel)
          .def("d_target_d_alpha", &w_t::d_target_d_alpha )
          .def("alpha", (void(w_t::*)(double)) &w_t::alpha)
          .def("alpha", (double(w_t::*)()) &w_t::alpha)
          .def("set_weights", &w_t::set_weights)
          ;
      }
    };


    struct least_squares_hemihedral_twinning_on_f_wrappers
    {
      typedef least_squares_hemihedral_twinning_on_f<double> w_t;

      // A thin wrapper that wraps up the derivatives
      static boost::python::tuple
      d_target_d_ab(w_t const& self, scitbx::af::const_ref<std::complex<double> > const& f_model)
      {
        scitbx::af::tiny<scitbx::af::shared<double>, 2> result;
        result = self.d_target_d_ab( f_model );
        return boost::python::make_tuple( result[0], result[1] );
      }

      static void
      wrap()
      {
        using namespace boost::python;


        class_<w_t>("least_squares_hemihedral_twinning_on_f", no_init)
          .def(init<
               scitbx::af::const_ref< cctbx::miller::index<> > const&,  // 1 indices
               scitbx::af::const_ref< double > const&,                  // 2 i_obs
               scitbx::af::const_ref< double > const&,                  // 3 w_obs
               scitbx::af::const_ref< cctbx::miller::index<> > const&,  // 4 hl_obs
               sgtbx::space_group const&,                               // 5 space group
               bool const&,                                             // 6 anomalous flag
               double const&,                                           // 7 alpha
               scitbx::mat3<double> const&                              // 8 twin law)
               >
               ((arg("hkl_obs"),
                 arg("f_obs"),
                 arg("w_obs"),
                 arg("hkl_calc"),
                 arg("space_group"),
                 arg("anomalous_flag"),
                 arg("alpha"),
                 arg("twin_law")
                 )))
          .def("target", &w_t::target)
          .def("d_target_d_ab", d_target_d_ab )
          .def("d_target_d_fmodel", &w_t::d_target_d_fmodel)
          .def("d_target_d_alpha", &w_t::d_target_d_alpha )
          .def("alpha", (void(w_t::*)(double)) &w_t::alpha)
          .def("alpha", (double(w_t::*)()) &w_t::alpha)
          .def("set_weights", &w_t::set_weights)
          ;
      }
    };



    struct hemihedral_r_values_wrappers
    {
      typedef hemihedral_r_values<double> w_t;

      static void
      wrap()
      {
        using namespace boost::python;
        class_<w_t>("hemihedral_r_values", no_init)
        .def(init<
             scitbx::af::const_ref< cctbx::miller::index<> > const&,   // 1 obs indices
             scitbx::af::const_ref< cctbx::miller::index<> > const&,   // 2 calc indices
             sgtbx::space_group const&,                                // 3 space group
             bool const&,                                              // 4 anomalous flag
             scitbx::mat3<double> const&                               // 5 twin law
             >
             ((arg("hkl_obs"),
               arg("hkl_calc"),
               arg("space_group"),
               arg("anomalous_flag"),
               arg("twin_law")
             )))
        .def("r_intensity_abs", &w_t::r_intensity_abs,
             ( arg("f_obs"),
               arg("f_model"),
               arg("selection"),
               arg("twin_fraction")
             )
            )
        .def("r_intensity_sq", &w_t::r_intensity_sq,
             ( arg("f_obs"),
               arg("f_model"),
               arg("selection"),
               arg("twin_fraction")
             )
            )
        .def("r_amplitude_abs", &w_t::r_amplitude_abs,
             ( arg("f_obs"),
               arg("f_model"),
               arg("selection"),
               arg("twin_fraction")
             )
            )
        .def("r_amplitude_sq", &w_t::r_amplitude_sq,
             ( arg("f_obs"),
               arg("f_model"),
               arg("selection"),
               arg("twin_fraction")
             )
            )




        ;
      }
    };








   struct hemihedral_detwinner_wrappers
   {
     typedef hemihedral_detwinner<double> w_t;

      static boost::python::tuple
      detwin_with_twin_fraction(w_t const& self,
                                scitbx::af::const_ref<double> const& i_obs,
                                scitbx::af::const_ref<double> const& sig_obs,
                                double const& twin_fraction)
      {
        scitbx::af::tiny<scitbx::af::shared<double>, 2> result;
        result = self.detwin_with_twin_fraction(i_obs,sig_obs,twin_fraction);
        return boost::python::make_tuple( result[0], result[1] );
      }

      static boost::python::tuple
      twin_with_twin_fraction(w_t const& self,
                                scitbx::af::const_ref<double> const& i_obs,
                                scitbx::af::const_ref<double> const& sig_obs,
                                double const& twin_fraction)
      {
        scitbx::af::tiny<scitbx::af::shared<double>, 2> result;
        result = self.twin_with_twin_fraction(i_obs,sig_obs,twin_fraction);
        return boost::python::make_tuple( result[0], result[1] );
      }





      static boost::python::tuple
      detwin_with_model_data(w_t const& self,
                             scitbx::af::const_ref<double> const& i_obs,
                             scitbx::af::const_ref<double> const& sig_obs,
                             scitbx::af::const_ref<std::complex<double> > const& f_model,
                             double const& twin_fraction)
      {
        scitbx::af::tiny<scitbx::af::shared<double>, 2> result;
        result = self.detwin_with_model_data(i_obs,sig_obs,f_model,twin_fraction);
        return boost::python::make_tuple( result[0], result[1] );
      }

      static boost::python::tuple
      detwin_with_abs_model_data(w_t const& self,
                             scitbx::af::const_ref<double> const& i_obs,
                             scitbx::af::const_ref<double> const& sig_obs,
                             scitbx::af::const_ref<double> const& f_model,
                             double const& twin_fraction)
      {
        scitbx::af::tiny<scitbx::af::shared<double>, 2> result;
        result = self.detwin_with_model_data(i_obs,sig_obs,f_model,twin_fraction);
        return boost::python::make_tuple( result[0], result[1] );
      }

     static void
     wrap()
     {
       using namespace boost::python;
       class_<w_t>("hemihedral_detwinner", no_init)
       .def(init< scitbx::af::const_ref< cctbx::miller::index<> > const&,   // 1 obs indices
                  scitbx::af::const_ref< cctbx::miller::index<> > const&,   // 2 calc indices
                  sgtbx::space_group const&,                                // 3 space group
                  bool const&,                                              // 4 anomalous flag
                  scitbx::mat3<double> const&                               // 5 twin law
                >
                ((arg("hkl_obs"),
                  arg("hkl_calc"),
                  arg("space_group"),
                  arg("anomalous_flag"),
                  arg("twin_law")
                )))

       .def("detwin_with_twin_fraction", detwin_with_twin_fraction,
             ( arg("i_obs"),
               arg("sigma_obs"),
               arg("twin_fraction")
             ) )

       .def("twin_with_twin_fraction", twin_with_twin_fraction,
             ( arg("i_obs"),
               arg("sigma_obs"),
               arg("twin_fraction")
             ) )

       .def("detwin_with_model_data", detwin_with_model_data,
             ( arg("i_obs"),
               arg("sigma_obs"),
               arg("f_model"),
               arg("twin_fraction")
             ) )

       .def("detwin_with_abs_model_data", detwin_with_abs_model_data,
             ( arg("i_obs"),
               arg("sigma_obs"),
               arg("f_model"),
               arg("twin_fraction")
             ) )
       .def("obs_to_twin_obs", &w_t::obs_to_twin_obs)

       .def("obs_to_calc", &w_t::obs_to_calc )

       .def("obs_to_twin_calc", &w_t::obs_to_twin_calc)

       .def("calc_to_twin_calc", &w_t::calc_to_twin_calc)

         ;
     }
   };



   struct single_twin_likelihood_wrappers
   {
     typedef single_twin_likelihood<double> w_t;
     static void wrap()
     {
       using namespace boost::python;
       class_<w_t>("single_twin_likelihood", no_init)
         .def(init<
              double const&, double const&,
              double const&, double const&,
              double const&, double const&,
              double const&, double const&,
              bool   const&, bool   const&,
              double const&, double const&,
              double const&, int    const&
              >
              ((arg("i_obs1"),  arg("s_obs1"),
                arg("i_obs2"),  arg("s_obs2"),
                arg("f_calc1"), arg("f_calc2"),
                arg("eps1"),    arg("eps2"),
                arg("centric1"),arg("centric2"),
                arg("alpha"),   arg("beta"),
                arg("twin_fraction"), arg("n_quad")
                ))
              )
         .def("log_p" , &w_t::log_p)
         .def("d_log_p_d_f", &w_t::d_log_p_d_f)
         .def("dd_log_p_dd_f", &w_t::dd_log_p_dd_f)
         .def("num_integrate", &w_t::num_integrate)
         .def("laplace_integrate", &w_t::laplace_integrate)
         ;
     }


   };









  }}} // namespace cctbx::xray

  namespace boost_python {

    void wrap_twin_targets()
    {
      twin_targets::boost_python::twin_completion_wrappers::wrap();
      twin_targets::boost_python::least_squares_hemihedral_twinning_on_i_wrappers::wrap();
      twin_targets::boost_python::least_squares_hemihedral_twinning_on_f_wrappers::wrap();
      twin_targets::boost_python::hemihedral_r_values_wrappers::wrap();
      twin_targets::boost_python::hemihedral_detwinner_wrappers::wrap();
      twin_targets::boost_python::single_twin_likelihood_wrappers::wrap();
    }

  }

}}
