// graph-tool -- a general graph modification and manipulation thingy
//
// Copyright (C) 2006-2026 Tiago de Paula Peixoto <tiago@skewed.de>
//
// This program is free software; you can redistribute it and/or modify it under
// the terms of the GNU Lesser General Public License as published by the Free
// Software Foundation; either version 3 of the License, or (at your option) any
// later version.
//
// This program is distributed in the hope that it will be useful, but WITHOUT
// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
// FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
// details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.

#include "graph_maxent_sbm.hh"
#include "numpy_bind.hh"

using namespace std;
using namespace boost;
using namespace graph_tool;

void generate_maxent_sbm(GraphInterface& gi, std::any ab,
                         boost::python::object ors, boost::python::object oss,
                         boost::python::object omrs, std::any ain_theta,
                         std::any aout_theta, bool multi, bool self_loops,
                         std::any aw,
                         rng_t& rng)
{
    auto rs = get_array<int64_t, 1>(ors);
    auto ss = get_array<int64_t, 1>(oss);
    auto mrs = get_array<double, 1>(omrs);

    typedef vprop_map_t<int32_t> bmap_t;
    auto b = std::any_cast<bmap_t>(ab).get_unchecked();

    typedef vprop_map_t<double> dmap_t;
    auto in_theta = std::any_cast<dmap_t>(ain_theta).get_unchecked();
    auto out_theta = std::any_cast<dmap_t>(aout_theta).get_unchecked();
    if (multi)
    {
        auto eweights = hana::tuple_t<eprop_map_t<int64_t>, no_weight_t>;

        if (!aw.has_value())
            aw = no_weight_t();

        run_action<>()
            (gi,
             [&](auto& g, auto w)
             {
                 gen_maxent_sbm<true>(g, b, rs, ss, mrs, in_theta,
                                      out_theta, self_loops, w, rng);
             }, eweights)(aw);
    }
    else
    {
        run_action<>()
            (gi,
             [&](auto& g)
             {
                 gen_maxent_sbm<false>(g, b, rs, ss, mrs, in_theta,
                                       out_theta, self_loops,
                                       no_weight_t(), rng);
             })();
    }
}

SBMFugacities get_sbm_fugacities(boost::python::object ors,
                                 boost::python::object oss,
                                 boost::python::object oers,
                                 boost::python::object odegs_in,
                                 boost::python::object odegs_out,
                                 boost::python::object ob, bool directed,
                                 bool multigraph, bool self_loops)
{
    auto rs = get_array<int64_t,1>(ors);
    auto ss = get_array<int64_t,1>(oss);
    auto ers = get_array<double,1>(oers);
    auto degs_in = get_array<double,1>(odegs_in);
    auto degs_out = get_array<double,1>(odegs_out);
    auto b = get_array<int32_t,1>(ob);
    return SBMFugacities(rs, ss, ers, degs_in, degs_out, b, directed,
                         multigraph, self_loops);
}

using namespace boost::python;

#define __MOD__ generation
#include "module_registry.hh"
REGISTER_MOD
([]
 {
     def("get_sbm_fugacities", &get_sbm_fugacities);
     def("gen_maxent_sbm", &generate_maxent_sbm);

     class_<SBMFugacities>("SBMFugacities", no_init)
         .def("pack", &SBMFugacities::pack)
         .def("unpack", &SBMFugacities::unpack)
         .def("get_f", &SBMFugacities::get_f)
         .def("get_diff", &SBMFugacities::get_diff)
         .def("new_x", &SBMFugacities::new_x)
         .def("norm", &SBMFugacities::norm)
         .def("export_args",
              +[](SBMFugacities& state, python::object ors, python::object oss,
                  python::object omrs, python::object odegs_in,
                  python::object odegs_out, python::object otheta_in,
                  python::object otheta_out, python::object ob)
               {
                   auto rs = get_array<int64_t,1>(ors);
                   auto ss = get_array<int64_t,1>(oss);
                   auto mrs = get_array<double,1>(omrs);
                   auto degs_in = get_array<double,1>(odegs_in);
                   auto degs_out = get_array<double,1>(odegs_out);
                   auto theta_in = get_array<double,1>(otheta_in);
                   auto theta_out = get_array<double,1>(otheta_out);
                   auto b = get_array<int32_t,1>(ob);
                   state.export_args(rs, ss, mrs, degs_in, degs_out, theta_in,
                                    theta_out, b);
               });
 });
