#pragma once

#include "tokenizer.h"

#include <yandex/maps/wiki/filters/parsed_expression.h>
#include <yandex/maps/wiki/revision/filters.h>

#include <pqxx/pqxx>

#include <boost/optional.hpp>
#include <vector>
#include <memory>

namespace maps {
namespace wiki {
namespace filters {

const std::string CURRENT_LANG_VERSION = "1.0";

class Node
{
public:
    virtual ~Node() { }

    virtual std::string viewFilterClause(pqxx::transaction_base& txn, const std::string& viewAlias) const = 0;
    virtual bool hasServiceAttrsClause() const = 0;
    virtual revision::filters::ProxyFilterExpr revisionFilter() const = 0;
};

typedef std::unique_ptr<Node> NodePtr;

class Parser
{
public:
    Parser(const std::string& text);

    NodePtr parse();

private:
    NodePtr parseOrExpr();
    NodePtr parseAndExpr();
    NodePtr parseNotExpr();
    NodePtr parseTerm();
    NodePtr parseClause();

    std::vector<std::string> parseValues(
        boost::optional<Token::Type> expectedValueType);
    std::string parseVal(boost::optional<Token::Type> expectedValueType);

    bool match(Token::Type type);
    bool match(Token::Type type, const std::string& str);

    void throwUnexpected();

    template<typename... Args>
    void expect(Args&&... args)
    {
        if (!match(std::forward<Args>(args)...)) {
            throwUnexpected();
        }
    }

    void advance();
    void consume(Token::Type type, const std::string& str);

private:
    std::list<Token> tokens_;
    std::list<Token>::const_iterator curToken_;
};

} // namespace filters
} // namespace wiki
} // namespace maps
