#include "Parser.h"

Parser::Parser(ContextManager * context)
{
	this->context_manager = context;
	error_message = "***ERRORS DURING PARSING***\n";
	position_in_stream = 0;
	in_statement = false;
	program = static_cast<ProgramNode *>(tree.root);
}


Parser::~Parser()
{

}

void Parser::erase_all()
{
	error_message = "***ERRORS DURING PARSING***\n";
	position_in_stream = 0;
	in_statement = false;
	this->token_stream = vector<Token>();
	delete program;
	program = new ProgramNode();
}

string Parser::show_tokens()
{
	string tokens = "";
	for (unsigned int i = 0; i < token_stream.size(); i++)
	{
		tokens += token_stream[i].value + " ";
	}
	return tokens;
}

void Parser::add_tokens(vector<Token> tokens)
{
	for (unsigned int i = 0; i < tokens.size(); i++)
	{
		token_stream.push_back(tokens[i]);
	}
}


void Parser::error(string s)
{
	error_message += s ;
}

bool Parser::read_next()
{
	if(position_in_stream < token_stream.size())
	{
		current_token = token_stream[position_in_stream];
		tok_value = current_token.get_value();
		position_in_stream++;
		return true;
	}
	else
	{
		current_token = Token(t_invalid_token, "");
		tok_value = current_token.get_value();
		return false;
	}
}

ProgramNode * Parser::interpret()
{
	read_next();
	while(tok_value!= "") program->add_statement(statement());
	return program;
}

bool Parser::peek(string s) {return tok_value == s;}

bool Parser::peek_one_ahead(string s)
{
	if(position_in_stream < token_stream.size() - 1)
	{
		if(s == token_stream[position_in_stream].value)
			return true;
	}
	return false;
}

bool Parser::accept(string s)
{
	if(peek(s))
	{
		read_next();
		return true;
	}
	else return false;
}

bool Parser::expect(string s)
{
	if(!accept(s))
	{
		string error_message = "Error: expected ";
		error_message += s;
		error_message += " but received: " + tok_value + "\n";
		error(error_message);
		return false;
	}
	else return true;
}

bool Parser::is_type()
{
	if(tok_value == "def" || tok_value == "string" || tok_value == "num")
	{
		read_next();
		return true;
	}
	else return false;
}



ASTStatement * Parser::statement()
{
	if(accept("{"))
	{
		BasicStatement * stat = new BasicStatement();
		while(!accept("}")) stat->children.push_back(statement());
		return stat;
	}
	else if(is_type())
	{
		DeclarationStatement * declaration = new DeclarationStatement(context_manager);
		std::string identifier = tok_value;
		read_next();
		declaration->add_name(identifier);
		if(accept("="))
		{
			ASTExpression * ae = expr();
			declaration->add_right_value(ae);
			accept(";");
		}
		if(expect("("))
		{
			int argc = 0;
			while(tok_value != ")")
			{
				argc++;
				is_type();
				declaration->add_argument(tok_value);
				read_next();
				if(peek(")")) break;
				expect(",");
			}
			expect(")");
			if(!accept(";")) declaration->add_body(statement());
		}
		return declaration;
	}
	else if(accept("array"))
	{
		DeclarationStatement * declaration = new DeclarationStatement(context_manager);
				std::string identifier = tok_value;
				read_next();
				declaration->add_name(identifier);
				expect("[");
				declaration->add_array_size(expr());
				expect("]");
				accept(";");
				return declaration;
	}
	else if(accept("if"))
	{
		IfNode * ifStatement = new IfNode();
		ifStatement->add_condition(expr());
		ifStatement->add_body(statement());
		if(accept("else")) ifStatement->add_else_block(statement());
		return ifStatement;
	}
	else if(accept("repeat"))
	{
		RepeatStatement * repeat = new RepeatStatement(expr(), statement());
		return repeat;
	}
	else if(accept("import"))
	{
		auto name_expression = expr()->execute();
		cout << name_expression.repr() << endl;
		if(name_expression.type == SenchaObject::string_literal)
		{
			std::string name_of_import = name_expression.text;
			ImportStatement * import = new ImportStatement(name_of_import, context_manager);
			accept(";");
			return import;
		}
		else if (name_expression.name != "")
		{
			std::string name_of_import = name_expression.name;
			ImportStatement * import = new ImportStatement(name_of_import, context_manager);
			accept(";");
			return import;
		}
		else
		{
			string error_message = "ERROR: invalid import\n";
			error(error_message);
			BasicStatement * stat = new BasicStatement();
			stat->add_expression(new IncorrectExpression(error_message));
			accept(";");
			return stat;
		}
	}
	else if(accept("while"))
	{
		WhileNode * while_node = new WhileNode(expr(), statement());
		return while_node;
	}
	else if(accept("return"))
	{
		BasicStatement * stat = new BasicStatement();
		if(!peek(";")) stat->add_expression(expr());
		expect(";");
		return stat;
	}
	else
	{
		BasicStatement * stat = new BasicStatement();
		stat->add_expression(expr());
		while(!expect(";") && tok_value != "") read_next();
		return stat;
	}
}

ASTExpression * Parser::prim_expr()
{
	if(current_token.get_type() == t_integer)
	{
		ConstantExpression * ce = new ConstantExpression(std::atoi(tok_value.c_str()));
		read_next();
		return ce;
	}
	else if(current_token.get_type() == t_float)
	{
		ConstantExpression * ce = new ConstantExpression(std::atof(tok_value.c_str()));
		read_next();
		return ce;
	}
	else if(current_token.get_type() == t_literal)
	{
		ConstantExpression * ce = new ConstantExpression(tok_value);
		read_next();
		return ce;
	}
	else if(current_token.get_type() == t_keyword)
	{
		ConstantExpression * ce;
		if(tok_value == "true")
		{
			ce = new ConstantExpression( SenchaObject(true));
			read_next();
		}
		else if (tok_value == "false")
		{
			ce = new ConstantExpression(SenchaObject(false));
			read_next();
		}
		return ce;
	}
	else if(current_token.get_type() == t_symbol)
	{
		string name = current_token.value;
		VariableExpression * ve = new VariableExpression(name, context_manager);
		read_next();
		return ve;
	}
	else if(accept("("))
	{
		ASTExpression * expression = expr();
		expect(")");
		return expression;
	}
	else
	{
		string error_message = "ERROR: unexpected primary expression " + tok_value + "\n";
		error(error_message);
		read_next();
		return new IncorrectExpression(error_message);
	}
}

ASTExpression * Parser::postfix_expr()
{
	string name = tok_value;
	if(current_token.get_type() == t_symbol)
	{
		if(peek_one_ahead("("))
		{
			read_next();
			read_next();
			PostfixExpression * function_call = new PostfixExpression(name, context_manager);
			if(!accept(")"))
			{
				function_call->add_argument(expr());
				while(accept(",")) function_call->add_argument(expr());
				expect(")");
			}
			return function_call;
		}
		if(peek_one_ahead("["))
				{
					read_next();
					read_next();
					PostfixExpression * access = new PostfixExpression(name, context_manager, expr());
					accept("]");
					return access;
				}
	}
	return prim_expr();
}

ASTExpression * Parser::unary_expr()
{
	if(peek("-") || peek("!"))
	{
		string oper = tok_value;
		read_next();
		return new UnaryExpression(unary_expr(), oper);
	}
	else return postfix_expr();
}

ASTExpression * Parser::mul_expr()
{
	ASTExpression * left = unary_expr();
	if(peek("*") || peek("/"))
	{
		string oper = tok_value;
		read_next();
		return new BasicExpression(left, mul_expr(), oper);
	}
	else return left;
}

ASTExpression * Parser::add_expr()
{
	ASTExpression * left = mul_expr();
	if(peek("+") || peek("-"))
	{
		string oper = tok_value;
		read_next();
		return new BasicExpression(left, add_expr(), oper);
	}
	else return left;
}

ASTExpression * Parser::rel_expr()
{
	ASTExpression * left = add_expr();
	if(peek("<") || peek(">") || peek("<=") || peek(">="))
	{
		string oper = tok_value;
		read_next();
		return new BasicExpression(left, rel_expr(), oper);
	}
	else return left;
}

ASTExpression * Parser::eq_expr()
{
	ASTExpression * left = rel_expr();
	if(peek("==") || peek("!="))
	{
		string oper = tok_value;
		read_next();
		return new BasicExpression(left, eq_expr(), oper);
	}
	else return left;
}

ASTExpression * Parser::log_expr()
{
	ASTExpression * left = eq_expr();
	if(peek("and") || peek("or"))
	{
		string oper;
		if(accept("and")) oper = "&&";
		else if(accept("or")) oper = "||";
		ASTExpression * right = log_expr();
		return new BasicExpression(left, right, oper);
	}
	else return left;
}

ASTExpression * Parser::expr()
{
	string name = tok_value;
	ASTExpression * left = log_expr();
	if(accept("="))
	{
		ASTExpression * right = expr();
		std::string name_of_left_side = left->evaluate().name;
		if(name_of_left_side != "")
			name = name_of_left_side;
		Assignment * assignment = new Assignment( context_manager, name, left, right);
		return assignment;
	}
	else return left;
}
