#ifndef TILE_BASE_FUTURE_IMPLS_H
#define TILE_BASE_FUTURE_IMPLS_H

#pragma once

#include "tile/base/optional.h"
#include <chrono>
#include <type_traits>
#include <utility>

namespace tile {
namespace future {
namespace detail {
template<std::size_t I, typename F>
void
for_each_indexed_impl(F &&)
{}

template<std::size_t I, typename F, typename T>
void
for_each_indexed_impl(F &&f, T &&t)
{
    f(std::forward<T>(t), std::integral_constant<std::size_t, I>());
}

template<std::size_t I, typename F, typename T, typename... Ts>
void
for_each_indexed_impl(F &&f, T &&t, Ts &&...ts)
{
    f(std::forward<T>(t), std::integral_constant<std::size_t, I>());

    for_each_indexed_impl<I + 1>(std::forward<F>(f), std::forward<Ts>(ts)...);
}

template<template<typename...> class TT, typename T>
struct is_template_t : std::false_type {};

template<template<typename...> class TT, typename... T>
struct is_template_t<TT, TT<T...>> : std::true_type {};

template<typename T>
struct is_duration {
    static constexpr bool value = is_template_t<std::chrono::duration, T>::value;
};

template<typename T>
struct is_time_point {
    static constexpr bool value = is_template_t<std::chrono::time_point, T>::value;
};

static_assert(is_duration<std::chrono::steady_clock::duration>::value, "");
static_assert(!is_duration<std::chrono::steady_clock::time_point>::value, "");
static_assert(!is_time_point<std::chrono::steady_clock::duration>::value, "");
static_assert(is_time_point<std::chrono::steady_clock::time_point>::value, "");

template<typename T>
struct optional_or_bool {
    using type = std::optional<T>;
};

template<>
struct optional_or_bool<void> {
    using type = bool;
};

template<typename T>
using optional_or_bool_t = typename optional_or_bool<T>::type;

}// namespace detail

template<typename F, typename... Ts>
void
for_each_indexed(F &&f, Ts &&...ts)
{
    detail::for_each_indexed_impl<0>(std::forward<F>(f), std::forward<Ts>(ts)...);
}

}// namespace future
}// namespace tile

#endif// TILE_BASE_FUTURE_IMPLS_H
