#include "calculator.hpp"

#include <algorithm>
#include <cmath>
#include <iostream>
#include <sstream>
#include <string>
#include <vector>

// Tokens implementation

// You need to fill these methods with the appropriate code for it to work as described in the project description.
// Exmaples of ADD and Eof is given.
size_t Tokens::ADD(const std::string &str, size_t index, const char **bad) { 
  *bad = &str[index];
  if (index + 1 > str.size()) return -1;
  return str[index] == '+' ? 1 : -1;
}

size_t Tokens::SUB(const std::string &str, size_t index, const char **bad) { 
  // WRITEME
}

size_t Tokens::MUL(const std::string &str, size_t index, const char **bad) { 
  // WRITEME
}

size_t Tokens::DIV(const std::string &str, size_t index, const char **bad) { 
  // WRITEME
}

size_t Tokens::Semicolon(const std::string &str, size_t index, const char **bad) { 
  // WRITEME
}

size_t Tokens::Colon(const std::string &str, size_t index, const char **bad) { 
  // WRITEME
}

size_t Tokens::LPAREN(const std::string &str, size_t index, const char **bad) { 
  // WRITEME
}

size_t Tokens::RPAREN(const std::string &str, size_t index, const char **bad) { 
  // WRITEME
}

size_t Tokens::QSMark(const std::string &str, size_t index, const char **bad) { 
  // WRITEME
}

size_t Tokens::INTEGER(const std::string &str, size_t index, const char **bad) { 
  // WRITEME
}

size_t Tokens::Eol(const std::string &str, size_t index, const char **bad) { 
  // WRITEME
}

size_t Tokens::Eof(const std::string &str, size_t index, const char **bad) {
  *bad = nullptr;
  return str.size() == index ? 0 : -1;
}



// Scanner implementation

// You can modify this constructor, although it is not neccessary.
Scanner::Scanner() : Scanner("") {
  std::stringstream expressions;
  std::string line;
  while (std::getline(std::cin, line)) {
    expressions << line << '\n';
  }
  expression_list_ = expressions.str();
  expression_list_.erase(expression_list_.size() - 1);
};

Scanner::Scanner(std::string expression_list)
    : line_(1),
      value_(0),
      index_(0),
      expression_list_(std::move(expression_list)) {}


// You need to fill these methods with the appropriate code for it to work as described in the project description.
Token Scanner::nextToken() {
  while (expression_list_[index_] == ' ' || expression_list_[index_] == '\t')
    ++index_;
  const char *bad = nullptr;
  for (auto it = token_funcs_.begin(); it != token_funcs_.end(); ++it) {
    const char *curr_bad = nullptr;
    size_t length = it->second(expression_list_, index_, &curr_bad);
    if (curr_bad > bad) bad = curr_bad;
    // I am a placeholder. Change me into something that can actually decypher the next token without consuming it. Also, if I encounter an invalid input, I can throw the Error using methods defined in helpers.hpp
    //
    // WRITEME
  }
  // WRITEME
}

void Scanner::eatToken(Token toConsume) {
  // WRITEME
}

int Scanner::lineNumber() { return line_; }

int Scanner::getIntegerValue() { return value_; }



// Parser implementation

// You can modify this constructor, although it is not neccessary.
Parser::Parser(bool eval) : evaluate(eval) {}

void Parser::parse() {
  std::vector<int> expressions = Start();
  if (evaluate) {
    // output the evaluate result
    for (int i = 0; i < expressions.size(); ++i) {
      std::cout << expressions[i];
      if (i + 1 < expressions.size()) {
        std::cout << std::endl;
      }
    }
  }
}



// You need to fill these methods with the appropriate code for it to work as described in the project description. 
// Implement a recursive descent parser starting from me. Add other methods for different recursive steps.
// Depending on how you designed your grammar, how many levels of operator precedence you need, etc., you might end up with more or less steps in the process.
// The example of Start() is given
std::vector<int> Parser::Start() {
  std::vector<int> result;
  switch (FilterTokens(scanner)) {
    case T_INTEGER:
    case T_LPAREN:
      // Start -> ExpressionList
      result = ExpressionList();
      break;
      
    default:
      parseError(scanner.lineNumber(), scanner.nextToken());
      break;
  }
  return result;
}

// You will need to add more methods for this to work. Don't forget to also define them in calculator.hpp!
//
// WRITEME