#include <exception>
#include <iomanip>
#include <iostream>
#include <stdexcept>
#include <string>
#include <tuple>
#include <vector>

template <std::size_t... Indices>
struct indices {};

template <std::size_t Count, std::size_t... Indices>
struct get_indices : public get_indices<Count - 1, Count - 1, Indices...> {};

template <std::size_t... Indices>
struct get_indices<0, Indices...> : public indices<Indices...> {};

template <class TupleT, class Fn, std::size_t Index>
static void do_get(const TupleT& tuple, Fn fn)
{
    fn(std::get<Index>(tuple));
}

template <class TupleT, class Fn, std::size_t... Indices>
static void tuple_get(const TupleT& tuple, std::size_t index, Fn fn, indices<Indices...>)
{
    using FunT = void(const TupleT&, Fn);
    static constexpr FunT* getters[] = { &do_get<TupleT, Fn, Indices>... };
    return (getters[index])(tuple, fn);
}

template <class Fn, typename... ArgsT>
void tuple_get(const std::tuple<ArgsT...>& tuple, std::size_t index, Fn fn)
{
    if (index >= sizeof...(ArgsT))
        throw std::logic_error("Cannot index past the end of tuple");
    return tuple_get<std::tuple<ArgsT...>, Fn>(tuple, index, fn, get_indices<sizeof...(ArgsT)>());
}

enum weekday { monday, tuesday, wednesday, thursday, friday, saturday, sunday };

static std::ostream& operator<<(std::ostream& stream, weekday wd)
{
    switch (wd) {
        case    monday: stream <<    "Monday"; break;
        case   tuesday: stream <<   "Tuesday"; break;
        case wednesday: stream << "Wednesday"; break;
        case  thursday: stream <<  "Thursday"; break;
        case    friday: stream <<    "Friday"; break;
        case  saturday: stream << "Satudrday"; break;
        case    sunday: stream <<    "Sunday"; break;
    }
    return stream;
}

struct print_tuple_value {
    template <typename T>
    void operator()(const T& value)
    {
        std::cout << value;
    }

    void operator()(weekday day)
    {
        std::cout << std::setw(10) << std::setfill(' ') << day;
    }

    void operator()(int time)
    {
        std::cout << std::setw(4) << std::setfill('0') << time;
    }
};

template <class TupleT>
std::size_t tuple_size(TupleT)
{
    return std::tuple_size<TupleT>::value;
}

int main()
{
    std::vector<std::tuple<weekday, int, std::string>> todo_list = {
        std::make_tuple(   monday,  900,            "purchase meat"),
        std::make_tuple(  tuesday, 1400,                "beat meat"),
        std::make_tuple(wednesday, 1100,          "make sandwiches"),
        std::make_tuple( thursday, 1200,           "eat sandwiches"),
        std::make_tuple(   friday, 1200,           "reinvent wheel"),
        std::make_tuple( saturday,  800,                "post cats"),
        std::make_tuple(   sunday, 2200, "post sinks (mods asleep)")
    };
    std::cout << "To-do list:\n";
    for (std::size_t j = 0; j < todo_list.size(); ++j) {
        std::cout.put('\t');
        for (std::size_t i = 0; i < tuple_size(todo_list[j]); ++i) {
            tuple_get(todo_list[j], i, print_tuple_value());
            std::cout.put(' ');
        }
        std::cout.put('\n');
    }
    return 0;
}
