#ifndef SCANNER_HPP
#define SCANNER_HPP

#include <cstdlib>
#include <stdexcept>
#include <tuple>
#include <type_traits>

template <typename T>
struct static_parser;

template <template <typename> class Parser = static_parser>
class scanner
{
    const char* buf_;

public:
    scanner(const char* over)
        : buf_(over)
    {
        if (buf_ == 0) //  gcc 5.1 doesn't know nullptr
            throw std::logic_error("Invalid nullpointer");
    }

    template <typename T0>
    T0 scan(const char* format)
    {
        auto r = scan_impl<T0>(format);

        while (*buf_ && *format)
        {
            if (*format == '%' && *++format != '%')
            {
                throw std::runtime_error("Unexpected % in format string.");
            }

            if (*buf_++ != *format++)
                throw std::runtime_error("Format string differs from buffer.");
        }

        if (*format)
            throw std::runtime_error("Format string is longer than buffer.");

        return r;
    }

    template <typename T0, typename T1>
    std::tuple<T0, T1> scan(const char* format)
    {
        auto r0 = scan_impl<T0>(format);
        return std::make_tuple(r0, scan<T1>(format));
    }

    template <typename T0, typename T1, typename T2>
    std::tuple<T0, T1, T2> scan(const char* format)
    {
        auto r0 = scan_impl<T0>(format);
        auto t = scan<T1, T2>(format);
        return std::make_tuple(r0, std::get<0>(t), std::get<1>(t));
    }

    template <typename T0, typename T1, typename T2, typename T3>
    std::tuple<T0, T1, T2, T3> scan(const char* format)
    {
        auto r0 = scan_impl<T0>(format);
        auto t = scan<T1, T2, T3>(format);
        return std::make_tuple(r0, std::get<0>(t), std::get<1>(t), std::get<2>(t));
    }

    template <typename T0, typename T1, typename T2, typename T3, typename T4>
    std::tuple<T0, T1, T2, T3, T4> scan(const char* format)
    {
        auto r0 = scan_impl<T0>(format);
        auto t = scan<T1, T2, T3, T4>(format);
        return std::make_tuple(r0, std::get<0>(t), std::get<1>(t), std::get<2>(t), std::get<3>(t));
    }

    template <typename T0, typename T1, typename T2, typename T3, typename T4, typename T5>
    std::tuple<T0, T1, T2, T3, T4, T5> scan(const char* format)
    {
        auto r0 = scan_impl<T0>(format);
        auto t = scan<T1, T2, T3, T4, T5>(format);
        return std::make_tuple(r0, std::get<0>(t), std::get<1>(t), std::get<2>(t), std::get<3>(t), std::get<4>(t));
    }

private:
    template <typename T0>
    T0 scan_impl(const char*& format)
    {
        while (*buf_ && *format)
        {
            if (*format == '%' && *++format != '%')
                return Parser<T0>::parse(*this, buf_);

            if (*buf_++ != *format++)
                throw std::runtime_error("Unexpected character.");
        }
        throw std::runtime_error("Unexpected end.");
    }
};

template <typename T>
struct static_parser
{
    static T parse(scanner<>& s, const char*& buf)
    { // dummy so the compiler can only use static_assert if function is instanciated
        const bool is_ = std::is_pod<T>::value;
        const bool or_ = !std::is_pod<T>::value;
        static_assert(is_ || or_, "No parsing function for this type.");
    }
};

template <char C>
class delim
{
    std::string data_;

public:
    delim(std::string data)
        : data_(std::move(data))
    {}

    operator std::string() const
    {
        return data_;
    }
};

template <char C>
struct static_parser<delim<C>>
{
    static delim<C> parse(scanner<>&, const char*& buf)
    {
        auto begin = buf;
        while (*buf && *buf != C)
            ++buf;
        return std::string(begin, buf);
    }
};

template <>
std::string static_parser<std::string>::parse(scanner<>&, const char*& buf)
{
    while (std::isspace(*buf))
        ++buf;

    auto begin = buf;
    while (*buf && !std::isspace(*buf))
        ++buf;

    return std::string(begin, buf);
}

// Es fehlen noch: signed char, unsigned char, long long, unsigned long long, long double

template <>
char static_parser<char>::parse(scanner<>&, const char*& buf)
{
    if (*buf != '\0')
        return *buf++;
    throw std::runtime_error("End of buffer found, could not parse character.");
}

template <>
long static_parser<long>::parse(scanner<>&, const char*& buf)
{
    char* p;
    auto r = std::strtol(buf, &p, 10);
    if (r == 0 && p == buf)
        throw std::runtime_error("Failed to parse long.");
    buf = p;
    return r;
}

template <>
unsigned long static_parser<unsigned long>::parse(scanner<>&, const char*& buf)
{
    char* p;
    auto r = std::strtoul(buf, &p, 10);
    if (r == 0 && p == buf)
        throw std::runtime_error("Failed to parse unsigned long.");
    buf = p;
    return r;
}

template <>
int static_parser<int>::parse(scanner<>& s, const char*& buf)
{ // Overflow?
    return static_parser<long>::parse(s, buf);
}

template <>
unsigned static_parser<unsigned>::parse(scanner<>& s, const char*& buf)
{ // Overflow?
    return static_parser<unsigned long>::parse(s, buf);
}

template <>
double static_parser<double>::parse(scanner<>&, const char*& buf)
{
    char* p;
    auto r = std::strtod(buf, &p);
    if (r == 0.0 && p == buf)
        throw std::runtime_error("Failed to parse double.");
    buf = p;
    return r;
}

template <>
float static_parser<float>::parse(scanner<>& s, const char*& buf)
{ // Overflow?
    return static_cast<float>(static_parser<double>::parse(s, buf));
}

#endif

#include <iostream>

class person
{
    std::string name_;
    unsigned age_;

public:
    person(std::string name, unsigned age)
        : name_(std::move(name))
        , age_(age)
    {}

    person(person&& p)
        : name_(std::move(p.name_))
        , age_(p.age_)
    {}

    const std::string& name() const
    {
        return name_;
    }

    unsigned age() const
    {
        return age_;
    }

private:
    person(const person&);
    person& operator = (const person&);
};


template <>
person static_parser<person>::parse(scanner<>& s, const char*&)
{
    auto t = s.scan<delim<'\n'>, int>("name: %""\nage: %\n");
    return person(std::get<0>(t), std::get<1>(t));
}

int main()
{
    try
    {
        scanner<> s("name: John Doe\n""age: 27\n");
        auto p = s.scan<person>("%");
        std::cout << "Hello, " << p.name() << '\n';
        if (p.age() <= 30)
            std::cout << "What? You are already " << p.age() << " years old? You look younger.\n";
        else
            std::cout << "What? You are only " << p.age() << " years old? You look older.\n";
    }
    catch (const std::runtime_error& e)
    {
        std::cerr << e.what() << '\n';
    }
} 