#ifndef PARSE_HPP_
#define PARSE_HPP_

#include <optional>
#include <string>
#include <vector>

/**
 * @brief Various utilities for parsing things such as commands.
 */
namespace Parse
{
    using TokenIterator = std::vector<std::string>::const_iterator;

    /**
     * @brief Tokenize a command line, returning the vector of tokens.
     */
    std::vector<std::string> tokenize(const std::string &cmdline);

    // Each of the fromString() functions takes a string and an output reference, and parses a
    // specified type from the input string. They return true if the parsing was successful,
    // or false if it failed.
    //
    // For strings, it simply copies the input string to the output string and always reports
    // success.
    bool fromString(const std::string &str, std::string &out);
    bool fromString(const std::string &str, int &out);
    bool fromString(const std::string &str, bool &out);

    bool fromTokens(TokenIterator current, TokenIterator end);
    bool fromTokens(TokenIterator current, TokenIterator end, std::vector<std::string> &restOut);

    template<typename TNext, typename... TOthers>
    bool fromTokens(TokenIterator current, TokenIterator end, std::optional<TNext> &nextOut, TOthers&... others)
    {
        if (current == end)
        {
            nextOut = std::nullopt;
            return true;
        }

        TNext value;
        if (!fromString(*current, value))
        {
            return false;
        }

        nextOut = value;

        return fromTokens(current + 1, end, others...);
    }

    template<typename TNext, typename... TOthers>
    bool fromTokens(TokenIterator current, TokenIterator end, TNext &nextOut, TOthers&... others)
    {
        if (current == end)
        {
            return false;
        }

        if (!fromString(*current, nextOut))
        {
            return false;
        }

        return fromTokens(current + 1, end, others...);
    }
}

#endif