#pragma once

#include <disc/disc/Composition.hxx>
#include <disc/disc/PatternsetResult.hxx>
#include <disc/utilities/UniversalIntEncoding.hxx>
#include <relaxent/IntersectingFactorization.hxx>

#include <cstddef>

namespace sd::disc
{

template <typename dist_t>
auto count_itemsets_in_factors(const dist_t& dist)
{
    size_t dof = 0;
    for (const auto& f : dist.factors)
    {
        dof += f.factor.itemsets.size();
    }
    return dof;
}

namespace bic
{
template <typename Trait>
auto encode_model_bic(const Composition<Trait>& c);
}
template <typename Trait, typename Config>
auto encode_model_intersecting_factors_bic(const Composition<Trait>& c, const Config&)
{

    return bic::encode_model_bic(c);

    // using float_type = typename Trait::float_type;

    // const auto n = c.data.size();
    // const auto k = c.data.num_components();
    // const auto s = c.summary.size();
    // const auto tr =
    //     std::accumulate(begin(c.models), end(c.models), size_t(), [&](auto acc, const auto&
    //     x) {
    //         return acc + count_itemsets_in_factors(x.model);
    //     });

    // const auto       m  = s - c.data.dim;
    // const float_type df = tr + m * k;

    // return df * std::log2(n) / 2.;
}

template <typename Trait, typename Config>
auto encode_model_intersecting_factors_bic(const PatternsetResult<Trait>& c, const Config&)
{
    typename Trait::float_type dof = count_itemsets_in_factors(c.model.model);
    return dof * std::log2(c.data.size()) / 2;
}

template <typename C, typename Config, typename A, typename B, typename W>
auto encode_model(const C&      c,
                  const Config& cfg,
                  const IntersectingFactorsDistribution<A, B, W>&)
{
    return encode_model_intersecting_factors_bic(c, cfg);
}

template <typename X, typename A, typename B, typename C>
auto additional_cost_bic(const X&                                        x,
                         size_t                                          component_size,
                         const IntersectingFactorsDistribution<A, B, C>& m)
{
    const auto [s, w] = size_and_width_of_new_factor_after_insert(m.model, x);

    // if(s > p.max_factor_size || w > p.max_factor_width)
    //     return std::numeric_limits<result_type>::max();
    // else
    return s * std::log2(component_size) / 2;
}

template <typename A, typename B, typename C>
auto additional_cost_mdl(size_t support, const IntersectingFactorsDistribution<A, B, C>& m)
{
    return universal_code(support) + std::log2(m.model.factors.size() + 1);
}

} // namespace sd::disc

#include <disc/disc/Encoding.hxx>