#include "calc.h"

/*************************************************************
  Pour tester le programme, vous pouvez ou : 
    - utiliser les exemples dans Makefile
    - taper sur la ligne de commande : 
           ./calc.exe  6 7 + 
           ./calc.exe 6 7 '*' 
        (il faut mettre * entre les quotes car c'est un caractère spéciale) 
**************************************************************/

int main(int in_arg_count, char ** in_args)
{
	ErrorCode error_code;
	Operand value;
	Expression expression;
	
	expression.element_count = 0;
	expression.elements = NULL;

	error_code = cl_arguments_to_expression(in_arg_count, in_args, &expression);
	if (error_code != SUCCESS)
	{
		printf("Invalid input\n");
		usage();
	}
	else
	{	
		error_code = evaluate(expression, &value);
		if (error_code != SUCCESS)
		{
			printf("Unable to calculate the input expression\n");
			usage();
		}
		else
		{
			printf("= %f\n", value);
		}
	}
	
	free(expression.elements);
	//	system("pause");
	return error_code;
}

void usage()
{
	printf("Specify an expression, with operand and operator separated by white spaces, as command-line arguments\n");
}

ErrorCode cl_arguments_to_expression(size_t in_arg_count, 
                                     char ** in_args, 
                                     Expression * out_expression)
{
	size_t i;
	ErrorCode error_code = SUCCESS;
	Element element;

	for (i = 1; i < in_arg_count; ++i) /* Skip the 1st element, which is the program name*/
	{
		error_code = string_to_element(in_args[i], &element);
		if (error_code != SUCCESS)
		{
			printf("Unknown token #%d: %s\n", i, in_args[i]);
			break;
		}
		else
		{
			out_expression->element_count = i;
			out_expression->elements = realloc(out_expression->elements, out_expression->element_count *sizeof(Element));
			out_expression->elements[out_expression->element_count - 1] = element;
		}
	}
	
	return error_code;
}

ErrorCode evaluate(Expression in_expression, 
                   Operand * out_value)
{
	size_t i;
	ElementStack stack;
	Element left_operand, right_operand, result;
	ErrorCode error_code = SUCCESS;
	
	empty(&stack);
	result.type = OperandType;
	
	if (in_expression.element_count == 0)
	{
		*out_value = 0.0;
		return SUCCESS;
	}
	
	for (i = 0; i < in_expression.element_count; ++i)
	{
		if (is_operator(in_expression.elements[i]))
		{
			error_code = pop(&stack, &right_operand);
			if (error_code != SUCCESS)
			{
				printf("Missing right operand for operator '%c'\n", in_expression.elements[i].data.operator);
				break;
			}
			
			error_code = pop(&stack, &left_operand);
			if (error_code != SUCCESS)
			{
				printf("Missing left operand for operator '%c'\n", in_expression.elements[i].data.operator);
				break;
			}
			
			error_code = unitary_evaluate(in_expression.elements[i].data.operator, 
			                              left_operand.data.operand, 
			                              right_operand.data.operand, 
			                              &(result.data.operand));
			if (error_code != SUCCESS)
			{
				break;
			}

			error_code = push(&stack, result);
			if (error_code != SUCCESS)
			{
				break;
			}
		}
		else if (is_operand(in_expression.elements[i]))
		{
			error_code = push(&stack, in_expression.elements[i]);
			if (error_code != SUCCESS)
			{
				break;
			}
		}
	}
	
	if (error_code == SUCCESS)
	{
		error_code = pop(&stack, &result);
		if (error_code != SUCCESS)
		{
			printf("Final result not found\n");
			return FAILURE;
		}
	}
	else
	{
		printf("Malformed expression\n");
		return error_code;
	}
	
  if (is_empty(stack) == FALSE)
  {
  	printf("Left-over found in the stack\n");
  	return FAILURE;
  }
	empty(&stack);

	*out_value = result.data.operand;
	return SUCCESS;
}


bool is_operand(Element in_element)
{
	return (in_element.type == OperandType);
}

bool is_operator(Element in_element)
{
	return (in_element.type == OperatorType);
}

ErrorCode string_to_element(char * in_string, 
                            Element * out_element)
{
	ErrorCode error_code;
	Operand operand;
	BinaryOperator operator;
	
	error_code = string_to_operand(in_string, &operand);
	if (error_code == SUCCESS)
	{
		out_element->type = OperandType;
		out_element->data.operand = operand;
		return SUCCESS;
	}
  
	error_code = string_to_operator(in_string, &operator);
	if (error_code == SUCCESS)
	{
		out_element->type = OperatorType;
		out_element->data.operator = operator;
		return SUCCESS;
	}
  
  return FAILURE;
}

ErrorCode string_to_operator(char * in_string, 
                             BinaryOperator * out_operator)
{
	if ( (strcmp(in_string, "+") == 0)
		|| (strcmp(in_string, "-") == 0)
		|| (strcmp(in_string, "*") == 0)
		|| (strcmp(in_string, "/") == 0) )
	{
		*out_operator = in_string[0];
		return SUCCESS;
	}
	else
	{
		return FAILURE;	
	}
}

ErrorCode string_to_operand(char * in_string, 
                            Operand * out_operand)
{
	char * control_ptr;

	control_ptr = NULL;
	*out_operand = strtod(in_string, &control_ptr);
	/* strtod converts string to double */
	if (*control_ptr != 0) /* not pointing to the end of the token --> token not a double */
	{
		return FAILURE;
	}
	
	return SUCCESS;
}


ErrorCode unitary_evaluate(BinaryOperator in_operator, 
			                     Operand in_left_operand, 
			                     Operand in_right_operand, 
			                     Operand * out_result)
{
	switch (in_operator)
	{
		case '+':
			*out_result = in_left_operand + in_right_operand;
			break;
		case '-':
			*out_result = in_left_operand - in_right_operand;
			break;
		case '*':
			*out_result = in_left_operand * in_right_operand;
			break;
		case '/':
			*out_result = in_left_operand / in_right_operand;
			break;
		default:
			printf("Unknown operator '%c'\n", in_operator);
			return FAILURE;
	};

	return SUCCESS;
}

