/**
 * projectM -- Milkdrop-esque visualisation SDK
 * Copyright (C)2003-2004 projectM Team
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 * See 'LICENSE.txt' included within this release
 *
 */

#include "wipemalloc.h"

#include "Expr.hpp"
#include <cassert>

#include <iostream>
#include "Eval.hpp"

float GenExpr::eval_gen_expr ( int mesh_i, int mesh_j )
{
	float l;

	assert ( item );
	switch ( this->type )
	{
		case VAL_T:
			return ( ( ValExpr* ) item )->eval_val_expr ( mesh_i, mesh_j );
		case PREFUN_T:
			l = ( ( PrefunExpr * ) item )->eval_prefun_expr ( mesh_i, mesh_j );
			//if (EVAL_DEBUG) DWRITE( "eval_gen_expr: prefix function return value: %f\n", l);
			return l;
		case TREE_T:
			return ( ( TreeExpr* ) ( item ) )->eval_tree_expr ( mesh_i, mesh_j );
		default:
			return EVAL_ERROR;
	}

}

/* Evaluates functions in prefix form */
float PrefunExpr::eval_prefun_expr ( int mesh_i, int mesh_j )
{


	assert ( func_ptr );

	float * arg_list = new float[this->num_args];

	assert(arg_list);

	//printf("numargs %d", num_args);

	/* Evaluate each argument before calling the function itself */
	for ( int i = 0; i < num_args; i++ )
	{
		arg_list[i] = expr_list[i]->eval_gen_expr ( mesh_i, mesh_j );
		//printf("numargs %x", arg_list[i]);


	}
	/* Now we call the function, passing a list of
	   floats as its argument */

	const float value = ( func_ptr ) ( arg_list );

	delete[](arg_list);
	return value;
}


/* Evaluates a value expression */
float ValExpr::eval_val_expr ( int mesh_i, int mesh_j )
{


	/* Value is a constant, return the float value */
	if ( type == CONSTANT_TERM_T )
	{
		return ( term.constant );
	}

	/* Value is variable, dereference it */
	if ( type == PARAM_TERM_T )
	{
		switch ( term.param->type )
		{

			case P_TYPE_BOOL:


				return ( float ) ( * ( ( bool* ) ( term.param->engine_val ) ) );
			case P_TYPE_INT:


				return ( float ) ( * ( ( int* ) ( term.param->engine_val ) ) );
			case P_TYPE_DOUBLE:

				
				if ( term.param->matrix_flag | ( term.param->flags & P_FLAG_ALWAYS_MATRIX ) )
				{

					/* Sanity check the matrix is there... */
					assert ( term.param->matrix != NULL );

					/// @slow boolean check could be expensive in this critical (and common) step of evaluation
					if ( mesh_i >= 0 )
					{
						if ( mesh_j >= 0 )
						{
							return ( ( ( float** ) term.param->matrix ) [mesh_i][mesh_j] );
						}
						else
						{
							return ( ( ( float* ) term.param->matrix ) [mesh_i] );
						}
					}
					//assert(mesh_i >=0);
				}
				//std::cout << term.param->name << ": " << (*((float*)term.param->engine_val)) << std::endl;
				return * ( ( float* ) ( term.param->engine_val ) );			
			default:
				return EVAL_ERROR;
		}
	}
	/* Unknown type, return failure */
	return PROJECTM_FAILURE;
}

/* Evaluates an expression tree */
float TreeExpr::eval_tree_expr ( int mesh_i, int mesh_j )
{

	float left_arg, right_arg;

	/* A leaf node, evaluate the general expression. If the expression is null as well, return zero */
	if ( infix_op == NULL )
	{
		if ( gen_expr == NULL )
			return 0;
		else
			return gen_expr->eval_gen_expr ( mesh_i, mesh_j );
	}

	/* Otherwise, this node is an infix operator. Evaluate
	   accordingly */

	assert(left);
	left_arg = left->eval_tree_expr ( mesh_i, mesh_j );

	assert(right);
	right_arg = right->eval_tree_expr ( mesh_i, mesh_j );


	switch ( infix_op->type )
	{
		case INFIX_ADD:
			return ( left_arg + right_arg );
		case INFIX_MINUS:
			return ( left_arg - right_arg );
		case INFIX_MULT:
			return ( left_arg * right_arg );
		case INFIX_MOD:
			if ( ( int ) right_arg == 0 )
			{
				return PROJECTM_DIV_BY_ZERO;
			}
			return ( ( int ) left_arg % ( int ) right_arg );
		case INFIX_OR:
			return ( ( int ) left_arg | ( int ) right_arg );
		case INFIX_AND:
			return ( ( int ) left_arg & ( int ) right_arg );
		case INFIX_DIV:
			if ( right_arg == 0 )
			{
				return MAX_DOUBLE_SIZE;
			}
			return ( left_arg / right_arg );
		default:
			return EVAL_ERROR;
	}

	return EVAL_ERROR;
}

/* Converts a float value to a general expression */
GenExpr * GenExpr::const_to_expr ( float val )
{

	GenExpr * gen_expr;
	ValExpr * val_expr;
	Term term;

	term.constant = val;

	if ( ( val_expr = new ValExpr ( CONSTANT_TERM_T, &term ) ) == NULL )
		return NULL;

	gen_expr = new GenExpr ( VAL_T, ( void* ) val_expr );

	if ( gen_expr == NULL )
	{
		delete val_expr;
	}

	return gen_expr;
}

/* Converts a regular parameter to an expression */
GenExpr * GenExpr::param_to_expr ( Param * param )
{

	GenExpr * gen_expr = NULL;
	ValExpr * val_expr = NULL;
	Term term;

	if ( param == NULL )
		return NULL;

	/* This code is still a work in progress. We need
	   to figure out if the initial condition is used for
	   each per frame equation or not. I am guessing that
	   it isn't, and it is thusly implemented this way */

	/* Current guess of true behavior (08/01/03) note from carm
	   First try to use the per_pixel_expr (with cloning)
	   If it is null however, use the engine variable instead. */

	/* 08/20/03 : Presets are now objects, as well as per pixel equations. This ends up
	   making the parser handle the case where parameters are essentially per pixel equation
	   substitutions */


	term.param = param;
	if ( ( val_expr = new ValExpr ( PARAM_TERM_T, &term ) ) == NULL )
		return NULL;

	if ( ( gen_expr = new GenExpr ( VAL_T, ( void* ) val_expr ) ) == NULL )
	{
		delete val_expr;
		return NULL;
	}
	return gen_expr;
}

/* Converts a prefix function to an expression */
GenExpr * GenExpr::prefun_to_expr ( float ( *func_ptr ) ( void * ), GenExpr ** expr_list, int num_args )
{

	GenExpr * gen_expr;
	PrefunExpr * prefun_expr;

	prefun_expr = new PrefunExpr();

	if ( prefun_expr == NULL )
		return NULL;

	prefun_expr->num_args = num_args;
	prefun_expr->func_ptr = ( float ( * ) ( void* ) ) func_ptr;
	prefun_expr->expr_list = expr_list;

	gen_expr = new GenExpr ( PREFUN_T, ( void* ) prefun_expr );

	if ( gen_expr == NULL )
		delete prefun_expr;

	return gen_expr;
}

/* Creates a new tree expression */
TreeExpr::TreeExpr ( InfixOp * _infix_op, GenExpr * _gen_expr, TreeExpr * _left, TreeExpr * _right ) :
		infix_op ( _infix_op ), gen_expr ( _gen_expr ),
	left ( _left ), right ( _right ) {}


/* Creates a new value expression */
ValExpr::ValExpr ( int _type, Term * _term ) :type ( _type )
{


	//val_expr->type = _type;
	term.constant = _term->constant;
	term.param = _term->param;

	//return val_expr;
}

/* Creates a new general expression */

GenExpr::GenExpr ( int _type, void * _item ) :type ( _type ), item ( _item ) {}

/* Frees a general expression */
GenExpr::~GenExpr()
{

	switch ( type )
	{
		case VAL_T:
			delete ( ( ValExpr* ) item );
			break;
		case PREFUN_T:
			delete ( ( PrefunExpr* ) item );
			break;
		case TREE_T:
			delete ( ( TreeExpr* ) item );
			break;
	}
}

/* Frees a function in prefix notation */
PrefunExpr::~PrefunExpr()
{

	int i;

	/* Free every element in expression list */
	for ( i = 0 ; i < num_args; i++ )
	{
		delete expr_list[i];
	}
	free ( expr_list );
}

/* Frees values of type VARIABLE and CONSTANT */
ValExpr::~ValExpr()
{}

/* Frees a tree expression */
TreeExpr::~TreeExpr()
{

	/* free left tree */
	if ( left != NULL )
	{
		delete left;
	}

	/* free general expression object */
	if ( gen_expr != NULL )
	{
		delete gen_expr;
	}

	/* Note that infix operators are always
	   stored in memory unless the program
	   exits, so we don't remove them here */

	/* free right tree */
	if ( right != NULL )
	{
		delete right;
	}
}

/* Initializes an infix operator */
InfixOp::InfixOp ( int type, int precedence )
{

	this->type = type;
	this->precedence = precedence;
}



PrefunExpr::PrefunExpr() {}
