/*
 * ASTInspector.cpp
 *
 *  Created on: Dec 17, 2012
 *      Author: att
 */

#include "ASTInspector.h"

ASTInspector::ASTInspector()
	:  number_of_visits(0), depth_level(0)
{

}

ASTInspector::~ASTInspector()
{

}

unsigned int ASTInspector::how_many_occurences_of(std::string type)
{
	return this->occurences[type];
}

void ASTInspector::visit(Visitable * node)
{
	number_of_visits++;
	if(node->type == "ConstantExpression")
	{
		visit(static_cast<ConstantExpression *>(node));
	}
	else if (node->type == "BasicExpression")
	{
		visit(static_cast<BasicExpression *>(node));
	}
	else if (node->type == "UnaryExpression")
	{
		visit(static_cast<UnaryExpression *>(node));
	}
	else if (node->type == "PostfixExpression")
	{
		visit(static_cast<PostfixExpression *>(node));
	}
	else if (node->type == "WhileNode")
	{
		visit(static_cast<WhileNode *>(node));
	}
	else if (node->type == "ProgramNode")
	{
		visit(static_cast<ProgramNode *>(node));
	}
	else if (node->type == "BasicStatement")
	{
		visit(static_cast<BasicStatement *>(node));
	}
	else if (node->type == "DeclarationStatement")
	{
		visit(static_cast<DeclarationStatement *>(node));
	}
	else if (node->type == "ImportStatement")
	{
		visit(static_cast<ImportStatement *>(node));
	}
	else if (node->type == "VariableExpression")
	{
		visit(static_cast<VariableExpression *>(node));
	}
	else if (node->type == "Assignment")
	{
		visit(static_cast<Assignment *>(node));
	}
	else if (node->type == "IfNode")
	{
		visit(static_cast<IfNode *>(node));
	}
	else if (node->type == "RepeatStatement")
	{
		visit(static_cast<RepeatStatement *>(node));
	}
	else if (node->type == "IncorrectExpression")
	{
		visit(static_cast<IncorrectExpression *>(node));
	}

	else
	{
		std::cout << "Visiting unknown node" << std::endl;
	}
}

void ASTInspector::visit(ConstantExpression * constant_expression)
{
	this->occurences["ConstantExpression"]++;
	depth_level++;
	std::string visit_notes = "";
	visit_notes += "Constant expression of value:\n";
	visit_notes += constant_expression->value.repr() + "\n";

	visit_notes += "End of ConstantExpression";
	write_report(visit_notes);
	depth_level--;
}

void ASTInspector::forget_everything()
{
	depth_level = 0;
	inspection_report = "";
	occurences.clear();
	number_of_visits = 0;
}

void ASTInspector::visit(BasicExpression * basic_expression)
{
	this->occurences["BasicExpression"]++;
	depth_level++;
	std::string visit_notes = "";
	visit_notes += "Basic expression:\n";
	visit_notes += "Executing " + basic_expression->get_operator() + " on:\n";
	write_report(visit_notes);

	basic_expression->children[0]->accept(this);
	basic_expression->children[1]->accept(this);

	write_report("End of BasicExpression\n");
	depth_level--;
}

void ASTInspector::visit(UnaryExpression * unary_expression)
{
	this->occurences["UnaryExpression"]++;
	depth_level++;
	std::string visit_notes = "";
	visit_notes += "Unary expression:\n";
	visit_notes += "Executing " + unary_expression->get_operator() + " on:\n";
	write_report(visit_notes);
	unary_expression->children[0]->accept(this);

	write_report("End of UnaryExpression\n");
	depth_level--;
}

void ASTInspector::visit(PostfixExpression * postfix_expression)
{
	this->occurences["PostfixExpression"]++;
	depth_level++;
	std::string visit_notes = "";
	visit_notes += "Postfix expression:\n";
	if(postfix_expression->operation == "call")
	{
		visit_notes += postfix_expression->name;
		visit_notes += " operating on arguments:\n";

		write_report(visit_notes);
		for(auto argument : postfix_expression->arguments)
		{
			argument->accept(this);
		}
	}
	else if (postfix_expression->operation == "access")
	{
		visit_notes += "Accessing " + postfix_expression->name;
		visit_notes += "by index:\n";
		write_report(visit_notes);
		postfix_expression->access_index_expr->accept(this);
	}


	write_report("End of postfix expression\n");
	depth_level--;
}


void ASTInspector::visit(WhileNode * while_node)
{
	this->occurences["WhileNode"]++;
	depth_level++;
	std::string visit_notes = "";
	visit_notes += "WhileNode:\n";
	write_report(visit_notes);

	for(auto child: while_node->children)
	{
		child->accept(this);
	}

	while_node->body->accept(this);


	write_report("End of while node\n");
	depth_level--;
}


void ASTInspector::visit(ProgramNode * program)
{
	this->occurences["ProgramNode"]++;
	depth_level++;
	std::string visit_notes = "";
	visit_notes += "ProgramNode:\n";
	write_report(visit_notes);

	for(auto child: program->children) child->accept(this);
	write_report("End of ProgramNode\n");
	depth_level--;
}

void ASTInspector::visit(BasicStatement * basic_statement)
{
	this->occurences["BasicStatement"]++;
	depth_level++;
	std::string visit_notes = "";
	visit_notes += "BasicStatement:\n";

	write_report(visit_notes);

	for(auto child: basic_statement->children)
	{
		child->accept(this);
	}

	write_report("End of BasicStatement\n");
	depth_level--;
}

void ASTInspector::visit(DeclarationStatement * declaration_statement)
{
	this->occurences["DeclarationStatement"]++;
	depth_level++;
	std::string visit_notes = "";
	visit_notes += "DeclarationStatement:\n";
	visit_notes += "It declares: " + declaration_statement->name + " to be:\n";
	if(!declaration_statement->is_function )
	{
		visit_notes += declaration_statement->right_value.repr();
		write_report(visit_notes);
	}
	else
	{
		visit_notes += "function";
		write_report(visit_notes);
		declaration_statement->body->accept(this);
	}
	write_report("End of DeclarationStatement\n");
	depth_level--;
}

void ASTInspector::visit(Assignment * assignment)
{
	this->occurences["Assignment"]++;
	depth_level++;
	std::string visit_notes = "";
	visit_notes += "Assignment:\n";

	visit_notes += "It assigns to: " + assignment->name + " expression:\n";
	write_report(visit_notes);

	assignment->children[1]->accept(this);

	write_report("End of DeclarationStatement\n");
	depth_level--;
}

void ASTInspector::visit(IfNode * if_node)
{
	this->occurences["IfNode"]++;
		depth_level++;
		std::string visit_notes = "";
		visit_notes += "IfNode:\n";
		visit_notes += "checks condition:";
		write_report(visit_notes);

		if_node->condition()->accept(this);

		write_report("if above is true, it executes:");

		if_node->then_block()->accept(this);
		if(if_node->is_else)
		{
			write_report("if it's not, it executes:");
			if_node->else_block()->accept(this);
		}


		write_report("End of IfNode\n");
		depth_level--;
}

void ASTInspector::visit(IncorrectExpression * incorrect_expression)
{
	this->occurences["IncorrectExpression"]++;
		depth_level++;
		std::string visit_notes = "";
		visit_notes += "IncorrectExpression:\n";

		visit_notes += incorrect_expression->error_message;
		write_report(visit_notes);

		write_report("End of IncorrectExpression\n");
		depth_level--;
}

void ASTInspector::visit(ImportStatement * import)
{
	this->occurences["ImportStatement"]++;
		depth_level++;
		std::string visit_notes = "";
		visit_notes += "ImportStatement:\n";

		visit_notes += "It imports " + import->name_of_module + " and import is:\n";
		if(import->correctly_imported) visit_notes += "success\n";
		else visit_notes += "failure\n";
		visit_notes += "End of ImportStatement";
		write_report(visit_notes);
		depth_level--;
}

void ASTInspector::visit(RepeatStatement * repeat_statement)
{
	this->occurences["RepeatStatement"]++;
	depth_level++;
	std::string visit_notes = "";
	visit_notes += "RepeatStatement:\n";

	visit_notes += "It executes " + to_string(repeat_statement->how_many_times_expr->evaluate().integer) + "times\n";
	write_report(visit_notes);
	repeat_statement->body->accept(this);
	write_report("End of RepeatStatement\n");
	depth_level--;
}

void ASTInspector::visit(VariableExpression * variable)
{
	this->occurences["VariableExpression"]++;
	depth_level++;
	std::string visit_notes = "";
	visit_notes += "VariableExpression:\n";

	visit_notes += "Name: " + variable->name + "\n";
	visit_notes += "Currently has a value:\n";
	visit_notes += variable->evaluate().repr();
	write_report(visit_notes);

	write_report("End of VariableExpression\n");
	depth_level--;
}


void ASTInspector::write_report(std::string visit_notes)
{
	std::istringstream stream(visit_notes);
	std::string line;
	std::string correct_indentation = compute_indent();

	while(std::getline(stream, line)) {
		inspection_report += correct_indentation + line + "\n";
	}
}

std::string ASTInspector::compute_indent()
{
	std::string indentation = "";
	std::string basic_indent = "    ";
	for(unsigned int i = 0; i < depth_level; i++)
	{
		indentation += basic_indent;
	}
	return indentation;
}
