#pragma once
#include <modern-qt/layout/group.hh>
#include <modern-qt/utility/wrapper/layout.hh>
#include <ranges>

namespace creeper::mutual_exclusion_group::internal {

template <auto f, typename W>
concept switch_function_trait = std::invocable<decltype(f), W&, bool>;

template <layout_trait T, widget_trait W, auto switch_function>
    requires switch_function_trait<switch_function, W>
struct MutualExclusionGroup : public Group<T, W> {
    using Group<T, W>::Group;

public:
    auto switch_widgets(std::size_t index) const noexcept {
        for (auto [index_, w] : std::views::enumerate(this->widgets)) {
            switch_function(*w, index_ == index);
        }
    }
    auto switch_widgets(W* widget) const noexcept {
        for (auto w_ : this->widgets) {
            switch_function(*w_, w_ == widget);
        }
    }

    auto make_signal_injection(auto signal) const noexcept -> void {
        for (auto widget : this->widgets) {
            QObject::connect(widget, signal, [this, widget] { switch_widgets(widget); });
        }
    }
};

constexpr inline auto checked_switch_function  = [](auto& w, bool on) { w.set_checked(on); };
constexpr inline auto opened_switch_function   = [](auto& w, bool on) { w.set_opened(on); };
constexpr inline auto selected_switch_function = [](auto& w, bool on) { w.set_selected(on); };

constexpr inline auto no_action_as_token = [](auto& w, bool on) { };

}
namespace creeper::mutual_exclusion_group::pro {

struct TokenContext { };
using Token = common::Token<TokenContext>;

template <typename Signal>
struct SignalInjection : Token {
    Signal signal;

    explicit SignalInjection(Signal signal) noexcept
        : signal { signal } { }

    auto apply(auto& self) const noexcept -> void {
        self.make_signal_injection(signal); //
    }
};

template <typename T>
concept trait = std::derived_from<T, Token> || group::pro::trait<T>;

CREEPER_DEFINE_CHECKER(trait);
using namespace group::pro;
}
namespace creeper {

template <layout_trait T, widget_trait W, auto switch_function>
    requires mutual_exclusion_group::internal::switch_function_trait<switch_function, W>
using MutualExclusionGroup =
    mutual_exclusion_group::internal::MutualExclusionGroup<T, W, switch_function>;

template <layout_trait T, widget_trait W>
using CheckGroup = Declarative<
    MutualExclusionGroup<T, W, mutual_exclusion_group::internal::checked_switch_function>,
    CheckerOr<mutual_exclusion_group::pro::checker, typename T::Checker>>;
namespace check_group = mutual_exclusion_group;

template <layout_trait T, widget_trait W>
using OpenGroup = Declarative<
    MutualExclusionGroup<T, W, mutual_exclusion_group::internal::opened_switch_function>,
    CheckerOr<mutual_exclusion_group::pro::checker, typename T::Checker>>;
namespace open_group = mutual_exclusion_group;

template <layout_trait T, widget_trait W>
using SelectGroup = Declarative<
    MutualExclusionGroup<T, W, mutual_exclusion_group::internal::selected_switch_function>,
    CheckerOr<mutual_exclusion_group::pro::checker, typename T::Checker>>;
namespace select_group = mutual_exclusion_group;

namespace internal {
    inline auto use_mutual_exclusion_group_namespace() {
        std::ignore = check_group::pro::Token {};
        std::ignore = open_group::pro::Token {};
        std::ignore = select_group::pro::Token {};
    }
}

}
