#include <chrono>
#include <cstdlib>
#include <functional>
#include <future>
#include <iostream>
#include <memory>
#include <string>
#include <thread>
#include <utility>
#include <vector>


class when {
public:
    template <class Predicate, class Callable, typename... Args>
    when(Predicate p, Callable c, Args... args)
    {
        std::thread thread(when_thread_entry<Predicate, Callable, Args...>(p, c, args...));
        thread.detach();
    }
private:
    template <class Predicate, class Callable, typename... Args>
    struct when_thread_entry {
        when_thread_entry(Predicate p, Callable c, Args... args) : predicate(p), function(c), tuple(args...)
        {
            // Do nothing.
        }
        
        void operator()()
        {
            while (!(predicate()))
                std::this_thread::yield();
            call_function();
        }
    private:
        template <int... Indices>
        struct index {};
        template <int N, int... Indices>
        struct gen_seq : gen_seq<N - 1, N - 1, Indices...> {};
        template <int... Indices>
        struct gen_seq<0, Indices...> : index<Indices...> {};

        Predicate predicate;
        Callable function;
        std::tuple<Args...> tuple;

        template <int... Indices>
        void call_function(index<Indices...>)
        {
            function(std::get<Indices>(tuple)...);
        }

        void call_function()
        {
            call_function(gen_seq<sizeof...(Args)>{});
        }
    };
};

struct timeout_predicate {
    timeout_predicate(std::time_t duration) : end(curtime() + duration)
    {
        // Do nothing.
    }

    bool operator()()
    {
        return (curtime() >= end);
    }
private:
    std::time_t end;

    std::time_t curtime()
    {
        return std::chrono::system_clock::to_time_t(std::chrono::system_clock::now());
    }
};

class after : public when {
public:
    template <class Callable, typename... Args>
    after(std::time_t timeout, Callable c, Args... args) : when(timeout_predicate(timeout), c, args...)
    {
        // Do nothing.
    }
};

struct string_printer {
    string_printer(std::ostream& stream) : m_stream(stream)
    {
        // Do nothing.
    }

    void operator()(const std::string& string)
    {
        m_stream << string;
    }
private:
    std::ostream& m_stream;
};

struct stream_flusher {
    stream_flusher(std::ostream& stream) : m_stream(stream)
    {
        // Do nothing.
    }

    void operator()()
    {
        m_stream << std::flush;
    }
private:
    std::ostream& m_stream;
};

struct program_exiter {
    void operator()(int exit_code = EXIT_SUCCESS)
    {
        std::exit(exit_code);
    }
};

int main()
{
    string_printer printer(std::cout);
    stream_flusher flusher(std::cout);
    program_exiter exiter;
    after( 9, flusher);
    after( 7, printer, "hello, world\n");
    after( 3, printer, "Message: ");
    after( 5, flusher);
    after(10, exiter, EXIT_SUCCESS);
    while (true)
        ;
}