#pragma once

#include "value-templates.hpp"

#include <algorithm>
#include <iterator>
#include <type_traits>
#include <vector>

namespace functools
{

template<typename seq_, typename = void>
struct reserver_
{
    static inline void maybe_reserve_space(seq_&, unsigned)
    {
        //qDebug() << "nada";
    }
};

template<typename seq_>
struct reserver_<seq_, decltype(std::declval<seq_>().reserve(0u), (void)0)>
{
    static inline void maybe_reserve_space(seq_& seq, unsigned sz)
    {
        seq.reserve(sz);
    }
};

template<typename seq_>
inline void maybe_reserve_space(seq_& seq, unsigned sz)
{
    reserver_<seq_, void>::maybe_reserve_space(seq, sz);
}

} // ns

template<typename t, t value_>
struct constant final
{
    using type = t;
    constexpr type operator()() const noexcept
    {
        return value_;
    }
    static constexpr type value = value_;

    constant() = delete;
};

template<typename seq_, typename F>
auto map(F&& fun, const seq_& seq)
{
    using value_type = std::decay_t<typename std::iterator_traits<decltype(std::begin(seq))>::value_type>;
    using ret_type = std::decay_t<decltype(fun(std::declval<value_type>()))>;

    std::vector<ret_type> ret;
    auto it = std::back_inserter(ret);

    for (const auto& elt : seq)
        it = fun(elt);

    return ret;
}

template<typename seq_, typename F>
auto remove_if_not(F&& fun, const seq_& seq)
{
    using namespace functools;

    using seq_type = std::decay_t<seq_>;
    using value_type = std::decay_t<typename std::iterator_traits<decltype(std::begin(seq))>::value_type>;
    using fun_ret_type = decltype(fun(std::declval<value_type>()));
    static_assert(is_convertible_v<fun_ret_type, bool>, "must return bool");

    seq_type ret;
    maybe_reserve_space(ret, seq.size());

    std::back_insert_iterator<seq_type> it = std::back_inserter(ret);

    for (const value_type& elt : seq)
        if (fun(elt))
            it = elt;

    return ret;
}