#include "parser.h"

#include <iostream>

namespace calc {

Parser::Parser(CalcLexer lex) : m_lex(lex) {
    std::cout << ">>> ";
    std::cout.flush();
}

Parser::Token Parser::lex() {
    while (true) {
        try {
            CalcLexer::Token orig = m_lex.nextToken();
            Token ret{calcParser_Symbol::T_EOF, nullptr};
            if (orig.type == CalcLexer::NUM) {
                ret = { calcParser_Symbol::T_NUM, std::make_unique<Number>(std::stod(orig.content)) };
            } else if (orig.type == CalcLexer::IDENT) {
                ret = { calcParser_Symbol::T_IDENT, std::make_unique<Var>(orig.content) };
            } else {
                ret = { static_cast<calcParser_Symbol>(orig.type), nullptr };
            }
            return ret;
        }
        catch (CalcLexer::NoMoreTokens) {
            return { calcParser_Symbol::T_EOF, nullptr };
        }
        catch (CalcLexer::NoMatch) {
            m_lex.skip(1);
            return { calcParser_Symbol::V_error, nullptr }; //Somewhat dependent on the generated code, but this should always give an error
        }
    }
}

Parser::Value Parser::error(Token, const std::vector<calcParser_Symbol>&) {
    return nullptr;
}

Parser::Value Parser::reduce_toplevel(std::deque<Token> subparts) {
    if (subparts.empty()) return nullptr;
    if (subparts[0].symbol == calcParser_Symbol::V_expr) {
        std::cout << "\033[3;34m   => " << subparts[0].value->eval(m_variables, m_functions) << "\033[0m" << std::endl;
    } else if (subparts[0].symbol == calcParser_Symbol::V_error) {
        std::cout << "\033[3;31m   => <syntax error>" << "\033[0m" << std::endl;
    }
    try {
        if (m_lex.peek() == '\n') {
            std::cout << ">>> ";
            std::cout.flush();
        }
    }
    catch (CalcLexer::NoMoreTokens) {}
    return nullptr;
}

Parser::Value Parser::reduce_binop(std::deque<Token> subparts) {
    std::string op;
    switch (subparts[1].symbol) {
        case calcParser_Symbol::T_PLUS:
            op = "+";
            break;
        case calcParser_Symbol::T_MINUS:
            op = "-";
            break;
        case calcParser_Symbol::T_TIMES:
            op = "*";
            break;
        case calcParser_Symbol::T_DIVIDE:
            op = "/";
            break;
        case calcParser_Symbol::T_EXPONENT:
            op = "^";
            break;
        case calcParser_Symbol::T_LT:
            op = "<";
            break;
        default:
            break;
    }
    return std::make_unique<Binop>(std::move(subparts[0].value), op, std::move(subparts[2].value));
}

Parser::Value Parser::reduce_expr_simple(std::deque<Token> subparts) {
    return std::move(subparts[0].value);
}

Parser::Value Parser::reduce_umin(std::deque<Token> subparts) {
    return std::make_unique<Unop>("-", std::move(subparts[1].value));
}

Parser::Value Parser::reduce_parenthesized(std::deque<Token> subparts) {
    return std::move(subparts[1].value);
}

Parser::Value Parser::reduce_functioncall(std::deque<Token> subparts) {
    auto args = dynamic_cast<FunctionArguments*>(subparts[2].value.get());
    subparts[2].value.release();
    return std::make_unique<FunctionCall>(dynamic_cast<Var*>(subparts[0].value.get())->getName(), std::unique_ptr<FunctionArguments>(args));
}

Parser::Value Parser::reduce_opt_arguments(std::deque<Token> subparts) {
    if (subparts.size()) {
        return std::move(subparts[0].value);
    } else {
        return std::make_unique<FunctionArguments>();
    }
}

Parser::Value Parser::reduce_arguments(std::deque<Token> subparts) {
    std::unique_ptr<FunctionArguments> args;
    if (subparts.size() > 1) {
        args = std::unique_ptr<FunctionArguments>(dynamic_cast<FunctionArguments*>(subparts[0].value.get()));
        subparts[0].value.release();
    } else {
        args = std::make_unique<FunctionArguments>();
    }
    args->push_back(std::move(subparts[subparts.size() - 1].value));
    return args;
}

Parser::Value Parser::reduce_functiondef(std::deque<Token> subparts) {
    std::string name = dynamic_cast<Var*>(subparts[1].value.get())->getName();
    std::deque<std::string> formalParams;
    FormalParameters* fp = dynamic_cast<FormalParameters*>(subparts[3].value.get());
    for (const std::unique_ptr<AST>& param : *fp) {
        formalParams.push_back(dynamic_cast<Var*>(param.get())->getName());
    }

    m_functions[name] = std::make_unique<Function>(std::move(formalParams), std::move(subparts[6].value));
    std::cout << "\033[3;32m   => Function " << name << " defined.\033[0m" << std::endl;
    return nullptr;
}

Parser::Value Parser::reduce_opt_idents(std::deque<Token> subparts) {
    if (subparts.size()) {
        return std::move(subparts[0].value);
    } else {
        return std::make_unique<FormalParameters>();
    }
}

Parser::Value Parser::reduce_idents(std::deque<Token> subparts) {
    std::unique_ptr<FormalParameters> args;
    if (subparts.size() > 1) {
        args = std::unique_ptr<FormalParameters>(dynamic_cast<FormalParameters*>(subparts[0].value.get()));
        subparts[0].value.release();
    } else {
        args = std::make_unique<FormalParameters>();
    }
    args->push_back(std::move(subparts[subparts.size() - 1].value));
    return args;
}

Parser::Value Parser::reduce_assign(std::deque<Token> subparts) {
    std::string name = dynamic_cast<Var*>(subparts[0].value.get())->getName();
    m_variables[name] = subparts[2].value->eval(m_variables, m_functions);
    std::cout << "\033[3;34m   => " << name << " = " << m_variables[name] << "\033[0m" <<std::endl;
    return nullptr;
}

} /* calc  */