#pragma once

#include <relaxent/InsertIntoModel.hxx>
#include <relaxent/ModelCost.hxx>
#include <relaxent/UsageHeuristic.hxx>
#include <relaxent/GreedyHeuristic.hxx>

#include <disc/disc/PatternsetMinerGeneric.hxx>

namespace sd::disc
{

using IDesc = DefaultPatternsetMinerInterface;

struct IUDesc : IDesc
{
    template <typename C, typename Pattern, typename Config>
    static auto heuristic(C& c, Pattern& x, const Config&)
    {
        return heuristic_usage(c, x);
    }
    template <typename C, typename Config>
    static auto finish(C& c, const Config& cfg)
    {
        prune_set(c, cfg);
    }
};

struct IUCDesc : IUDesc
{
    template <typename C, typename Candidate, typename Config>
    static auto insert_into_model(C& c, Candidate& x, const Config& cfg)
    {
        return sd::disc::checked_insert(
            c, x, cfg, [](auto&&... args) { return IUDesc::objective(args...); });
    }
};

struct IUCNDesc : IUCDesc
{
    template <typename C, typename Pattern>
    static auto is_itemset_allowed(const C&, const Pattern&)
    {
        return true;
    }
};

struct IUCNNDesc : IUCDesc
{
    template <typename C, typename Candidate, typename Config>
    static auto insert_into_model(C& c, Candidate& x, const Config& cfg)
    {
        if (x.score == 1 || IUCDesc::heuristic(c, x, cfg) <= 0)
            return false;

        return checked_insert(
            c, x, cfg, [](auto&&... args) { return IUCDesc::objective(args...); });
    }

    template <typename C, typename Pattern, typename Config>
    static auto heuristic(C& c, Pattern& x, const Config& cfg)
    {
        auto g = IUCDesc::heuristic(c, x, cfg);
        return (g < 0 ? 0 : g) + 1;
    }

    template <typename C, typename Pattern>
    static auto is_itemset_allowed(const C&, const Pattern&)
    {
        return true;
    }
};


struct IUNDesc : IUDesc
{
    template <typename C, typename Pattern>
    static auto is_itemset_allowed(const C&, const Pattern&)
    {
        return true;
    }
};

struct ICDesc : IDesc
{
    template <typename C, typename Candidate, typename Config>
    static auto insert_into_model(C& c, Candidate& x, const Config& cfg)
    {
        return sd::disc::checked_insert(
            c, x, cfg, [](auto&&... args) { return IDesc::objective(args...); });
    }
};

struct ICNDesc : ICDesc
{
    template <typename C, typename Pattern>
    static auto is_itemset_allowed(const C&, const Pattern&)
    {
        return true;
    }
};


struct IGDesc : IDesc
{
    template <typename C, typename Pattern, typename Config>
    static auto heuristic(C& c, Pattern& x, const Config& cfg)
    {
        return sd::disc::actual_gain(c, x, cfg);
    }
};

struct ICNNDesc : ICDesc
{
    template <typename C, typename Candidate, typename Config>
    static auto insert_into_model(C& c, Candidate& x, const Config& cfg)
    {
        if (x.score == 1 && ICDesc::heuristic(c, x, cfg) <= 0)
            return false;
        return checked_insert(
            c, x, cfg, [](auto&&... args) { return ICDesc::objective(args...); });
    }
    template <typename C, typename Pattern, typename Config>
    static auto heuristic(C& c, Pattern& x, const Config& cfg)
    {
        auto g = ICDesc::heuristic(c, x, cfg);
        return (g < 0 ? 0 : g) + 1;
    }
    template <typename C, typename Pattern>
    static auto is_itemset_allowed(const C&, const Pattern&)
    {
        return true;
    }
};


struct IGNDesc : IGDesc
{
    template <typename C, typename Pattern>
    static auto is_itemset_allowed(const C&, const Pattern&)
    {
        return true;
    }
};

} // namespace sd::disc
