#ifndef PORYGONLANG_PARSER_HPP
#define PORYGONLANG_PARSER_HPP

#include <utility>
#include <vector>
#include "ParsedStatements/ParsedStatement.hpp"
#include "../Script.hpp"

namespace Porygon::Parser {
    enum class OperatorPrecedence {
        No,
        LogicalOr,
        LogicalAnd,
        Equality,
        Additive,
        Multiplication,
        Unary,
    };

    class Parser {
        vector<const Token *> _tokens;
        unsigned int _position;
        Porygon::Script *ScriptData;

        const Token *Peek();

        const Token *PeekAt(int offset);

        const Token *Next();

        // Statements

        ParsedStatement *ParseStatement(const Token *current);
        ParsedStatement *ParseVariableAssignment(const Token *current);
        ParsedStatement *ParseIndexAssignment(ParsedExpression *indexer);
        ParsedBlockStatement *ParseBlock(const vector<TokenKind> &endTokens, const vector<const ParsedStatement *> &openStatements = {});
        ParsedStatement *ParseFunctionDeclaration(const Token *current);
        ParsedStatement *ParseReturnStatement(const Token *current);
        ParsedStatement *ParseIfStatement(const Token *current);
        ParsedStatement *ParseForStatement();
        ParsedStatement *ParseNumericForStatement(const Token *current);
        ParsedStatement *ParseGenericForStatement(const Token *current);
        ParsedStatement *ParseWhileStatement(const Token *current);

        // Expressions

        ParsedExpression *ParseExpression(const Token *current);
        ParsedExpression *ParseBinaryExpression(const Token *current, OperatorPrecedence parentPrecedence);
        ParsedExpression *ParsePrimaryExpression(const Token *current);
        ParsedExpression *ParseParenthesizedExpression(const Token *current);
        ParsedExpression *ParseFunctionCallExpression(ParsedExpression *functionExpression);
        ParsedExpression *ParseIndexExpression(ParsedExpression *indexingExpression);
        ParsedExpression *ParsePeriodIndexExpression(ParsedExpression *indexingExpression);
        ParsedExpression *ParseTableExpression(const Token *current);

    public:
        ParsedScriptStatement *Parse();

        explicit Parser(vector<const Token *> tokens, Porygon::Script *scriptData) : _tokens(std::move(tokens)), _position(0),
        ScriptData(scriptData){
        }

    };
}

#endif //PORYGONLANG_PARSER_HPP
