/* $Id$ 
 * BuiltinFunction: inline implementation of a number of builtin functions like
 *                  operators.
 *
 * Copyright (C) 2008-2009 FAUmachine Team <info@faumachine.org>.
 * This program is free software. You can redistribute it and/or modify it
 * under the terms of the GNU General Public License, either version 2 of
 * the License, or (at your option) any later version. See COPYING.
 */


#ifndef __BUILTIN_FUNCTION_HPP_INCLUDED
#define __BUILTIN_FUNCTION_HPP_INCLUDED

#include <cassert>
#include <list>
#include "frontend/misc/BuiltinSymbolTable.hpp"
#include "frontend/ast/ConstInteger.hpp"
#include "frontend/ast/TypeDeclaration.hpp"

namespace ast {

//! helper class for math of arbitrary universal types.
class UniversalMath {
public:
	//! calculate absolute value
	/** @param numeric value, for which abs should get calculated.
	 *  @return absolute value.
	 */
	template <typename T>
	static T
	abs(T value) {
		if (value < 0) {
			return -value;
		}

		return value;
	}
};

/** any builtin function should derive from this class. */
class BuiltinFunction {
public:
	//! virtual dummy d'tor.
	virtual ~BuiltinFunction() {}

	/** execute the builtin returning the result.
	 *  @param args arguments for the builtin function.
	 *  @return result of the builtin */
	virtual Expression* execute(std::list<Expression*> args) const = 0;
};

//! any unary operand should implement this class
/** Template parameter C: type of const node.
 *  Template parameter V: value type of const node.
 */
template <typename C, typename V>
class BuiltinUnop : public BuiltinFunction {
public:
	/** execute the builtin returning the result.
	 *  @param args arguments for the builtin function.
	 *  @return result of the builtin */
	virtual Expression* execute(std::list<Expression*> args) const {
		assert(args.size() == 1);

		Expression *f = args.front();
		C *constNode = dynamic_cast<C*>(f);
		assert(constNode != NULL);
		
		V result = this->executeUnop(constNode->value);
		C *rNode = new C(result, f->location);
		rNode->type = f->type;

		return rNode;
	}

protected:
	/** execute the unary operator.
	 *  @param operand operand value.
	 *  @return value of the builtin.
	 */
	virtual V executeUnop(V operand) const = 0;
};

//! any binary operand with same parameter should dervive from this class
/** Template parameter C: type of const node.
 *  Template parameter V: value type of const node.
 */
template <typename C, typename V>
class BuiltinBinOpSameType : public BuiltinFunction {
public:
	/** execute the builtin returning the result.
	 *  @param args arguments for the builtin function.
	 *  @return result of the builtin */
	virtual Expression* execute(std::list<Expression*> args) const {
		assert(args.size() == 2);

		Expression *l = args.front();
		C *left = dynamic_cast<C*>(l);
		Expression *r = args.back();
		C *right = dynamic_cast<C*>(r);
		assert(left != NULL);
		assert(right != NULL);
		
		V result = this->executeBinOp(left->value, right->value);
		C *resNode = new C(result, left->location);
		resNode->type = left->type;
		return resNode;
	}

protected:
	/** execute the binary operator
	 *  @param left left operand
	 *  @param right right operand
	 *  @return value of the builtin.
	 */
	virtual V executeBinOp(V left, V right) const = 0;
};

//! any binary operand with same parameter resulting in std.std.bool use this
/** Template parameter C: type of const node.
 *  Template parameter V: value type of const node.
 */
template <typename C, typename V>
class BoolBinOpSameType : public BuiltinFunction {
public:
	/** c'tor
	 *  @param symTab SymbolTable instance (to look up std.std.boolean).
	 */
	BoolBinOpSameType(SymbolTable &symTab) : st(symTab) {}

	/** execute the builtin returning the result.
	 *  @param args arguments for the builtin function.
	 *  @return result of the builtin */
	virtual Expression* execute(std::list<Expression*> args) const {
		assert(args.size() == 2);

		Expression *l = args.front();
		C *left = dynamic_cast<C*>(l);
		Expression *r = args.back();
		C *right = dynamic_cast<C*>(r);
		assert(left != NULL);
		assert(right != NULL);
		
		bool result = this->executeBinOp(left->value, right->value);
		universal_integer resVal = VHDL_FALSE;
		if (result) {
			resVal = VHDL_TRUE;
		}

		ConstInteger *resNode = 
			new ConstInteger(resVal, left->location);
		TypeDeclaration *boolean = st.getStdStandardType("boolean");
		resNode->type = 
			new SubtypeIndication(boolean, boolean->location);
		return resNode;
	}

protected:
	/** execute the binary operator
	 *  @param left left operand
	 *  @param right right operand
	 *  @return value of the builtin.
	 */
	virtual bool executeBinOp(V left, V right) const = 0;

private:
	SymbolTable &st;
};


//! unary minus
template <typename C, typename V>
class UnaryMinus : public BuiltinUnop<C, V> {
private:
	/** execute the builtin returning the result.
	 *  @return result of this builtin.
	 */
	virtual V executeUnop(V operand) const {
		return - operand;
	}
};

//! unary plus (identity)
template <typename C, typename V>
class UnaryPlus : public BuiltinUnop<C, V> {
private:
	/** execute the builtin returning the result.
	 *  @return result of this builtin.
	 */
	virtual V executeUnop(V operand) const {
		return operand;
	}
};

//! unary absolute value
template <typename C, typename V>
class UnaryAbs : public BuiltinUnop<C, V> {
private:
	/** execute the builtin returning the result.
	 *  @return result of this builtin.
	 */
	virtual V executeUnop(V operand) const {
		if (operand < 0) {
			return - operand;
		}

		return operand;
	}
};

//! binary plus
template <typename C, typename V>
class BinaryPlus : public BuiltinBinOpSameType<C, V> {
private:
	/** execute the binary operator
	 *  @param left left operand
	 *  @param right right operand
	 *  @return value of the builtin.
	 */
	virtual V executeBinOp(V left, V right) const {
		// FIXME range check!
		return left + right;
	}
}; 

//! binary minus
template <typename C, typename V>
class BinaryMinus : public BuiltinBinOpSameType<C, V> {
private:
	/** execute the binary operator
	 *  @param left left operand
	 *  @param right right operand
	 *  @return value of the builtin.
	 */
	virtual V executeBinOp(V left, V right) const {
		// FIXME range check!
		return left - right;
	}
};

//! binary multiplication
template <typename C, typename V>
class BinaryMult : public BuiltinBinOpSameType<C, V> {
private:
	/** execute the binary operator
	 *  @param left left operand
	 *  @param right right operand
	 *  @return value of the builtin.
	 */
	virtual V executeBinOp(V left, V right) const {
		// FIXME range check!
		return left * right;
	}
};

//! binary division
template <typename C, typename V>
class BinaryDiv : public BuiltinBinOpSameType<C, V> {
private:
	/** execute the binary operator
	 *  @param left left operand
	 *  @param right right operand
	 *  @return value of the builtin.
	 */
	virtual V executeBinOp(V left, V right) const {
		return left / right;
	}
};

//! binary modulo (only for integer types)
class BinaryMod : 
	public BuiltinBinOpSameType<ConstInteger, universal_integer> {
private:
	/** execute the binary operator
	 *  @param left left operand
	 *  @param right right operand
	 *  @return value of the builtin.
	 */
	virtual universal_integer 
	executeBinOp(universal_integer left, universal_integer right) const {
		//lrm, p.105: A = B * N + (A mod B)
		//            sign of B, abs(A mod B) < abs(B)
		//
		//	5 % 4
		//	4 * N + (mod)
		//	4 * 1 + 1
		//
		//	5 % -4
		//	-4 * -2 + (-3)
		//
		//	-5 % 4 
		//      4 * -2 + 3
		//  
		//      -5 % -4
		//      -4 * 1 + (-1)
		// FIXME: right == 0 -> error
		assert(right != 0);

		if ((left > 0) && (right < 0)) {
			universal_integer tmp = 
				UniversalMath::abs(
					left % UniversalMath::abs(right));
			return right + tmp;
		}

		if ((left < 0) && (right > 0)) {
			universal_integer tmp = 
				UniversalMath::abs(
					UniversalMath::abs(left) % right);
			return right - tmp;
		}

		universal_integer tmp = 
			UniversalMath::abs(
				UniversalMath::abs(left) % 
					UniversalMath::abs(right));
		if (right < 0) {
			return -tmp;
		}

		return tmp;
	}
};

//! binary remainder (only for integer types)
class BinaryRem : 
	public BuiltinBinOpSameType<ConstInteger, universal_integer> {
private:
	/** execute the binary operator
	 *  @param left left operand
	 *  @param right right operand
	 *  @return value of the builtin.
	 */
	virtual universal_integer 
	executeBinOp(universal_integer left, universal_integer right) const {
		//lrm, p. 104: A = (A/B) ∗ B +(A rem B)
		//             A rem B has the sign of A and 
		//             a abs. value less than abs (B)
		// FIXME right == 0 -> error
		assert(right != 0);
		
		return left - ((left / right) * right);
	}
};

//! universal equal operator
template <typename C, typename V>
class BoolEqual : public BoolBinOpSameType<C, V> {
public:
	//! c'tor
	/** @param symTab SymbolTable instance 
	 */
	BoolEqual(SymbolTable &symTab) : BoolBinOpSameType<C, V>(symTab) {}

private:
	/** execute the binary operator
	 *  @param left left operand
	 *  @param right right operand
	 *  @return value of the builtin.
	 */
	virtual bool
	executeBinOp(V left, V right) const {
		return left == right;
	}
};

//! universal not equal operator
template <typename C, typename V>
class BoolNotEqual : public BoolBinOpSameType<C, V> {
public:
	//! c'tor
	/** @param symTab SymbolTable instance 
	 */
	BoolNotEqual(SymbolTable &symTab) : BoolBinOpSameType<C, V>(symTab) {}

private:
	/** execute the binary operator
	 *  @param left left operand
	 *  @param right right operand
	 *  @return value of the builtin.
	 */
	virtual bool
	executeBinOp(V left, V right) const {
		return left != right;
	}
};

//! universal less than operator
template <typename C, typename V>
class BoolLess : public BoolBinOpSameType<C, V> {
public:
	//! c'tor
	/** @param symTab SymbolTable instance 
	 */
	BoolLess(SymbolTable &symTab) : BoolBinOpSameType<C, V>(symTab) {}

private:
	/** execute the binary operator
	 *  @param left left operand
	 *  @param right right operand
	 *  @return value of the builtin.
	 */
	virtual bool
	executeBinOp(V left, V right) const {
		return left < right;
	}
};

//! universal less or equal
template <typename C, typename V>
class BoolLessOrEqual : public BoolBinOpSameType<C, V> {
public:
	//! c'tor
	/** @param symTab SymbolTable instance 
	 */
	BoolLessOrEqual(
		SymbolTable &symTab
		) : 	BoolBinOpSameType<C, V>(symTab) {}

private:
	/** execute the binary operator
	 *  @param left left operand
	 *  @param right right operand
	 *  @return value of the builtin.
	 */
	virtual bool
	executeBinOp(V left, V right) const {
		return left <= right;
	}
};

//! universal greater than operator
template <typename C, typename V>
class BoolGreater : public BoolBinOpSameType<C, V> {
public:
	//! c'tor
	/** @param symTab SymbolTable instance 
	 */
	BoolGreater(SymbolTable &symTab) : BoolBinOpSameType<C, V>(symTab) {}

private:
	/** execute the binary operator
	 *  @param left left operand
	 *  @param right right operand
	 *  @return value of the builtin.
	 */
	virtual bool
	executeBinOp(V left, V right) const {
		return left > right;
	}
};

//! universal greater or equal
template <typename C, typename V>
class BoolGreaterOrEqual : public BoolBinOpSameType<C, V> {
public:
	//! c'tor
	/** @param symTab SymbolTable instance 
	 */
	BoolGreaterOrEqual(
		SymbolTable &symTab
		) : 	BoolBinOpSameType<C, V>(symTab) {}

private:
	/** execute the binary operator
	 *  @param left left operand
	 *  @param right right operand
	 *  @return value of the builtin.
	 */
	virtual bool
	executeBinOp(V left, V right) const {
		return left >= right;
	}
};

//! AND operator (bit, boolean)
class EnumAND : public BuiltinBinOpSameType<ConstInteger, universal_integer> {
private:
	/** execute the binary operator
	 *  @param left left operand
	 *  @param right right operand
	 *  @return value of the builtin.
	 */
	virtual universal_integer
	executeBinOp(universal_integer left, universal_integer right) const {
		if ((left == VHDL_TRUE) && (right == VHDL_TRUE)) {
			return VHDL_TRUE;
		}

		return VHDL_FALSE;
	}
};

//! OR operator (bit, boolean)
class EnumOR : public BuiltinBinOpSameType<ConstInteger, universal_integer> {
private:
	/** execute the binary operator
	 *  @param left left operand
	 *  @param right right operand
	 *  @return value of the builtin.
	 */
	virtual universal_integer
	executeBinOp(universal_integer left, universal_integer right) const {
		if ((left == VHDL_TRUE) || (right == VHDL_TRUE)) {
			return VHDL_TRUE;
		}

		return VHDL_FALSE;
	}
};

//! NAND operator (bit, boolean)
class EnumNAND : public BuiltinBinOpSameType<ConstInteger, universal_integer> {
private:
	/** execute the binary operator
	 *  @param left left operand
	 *  @param right right operand
	 *  @return value of the builtin.
	 */
	virtual universal_integer
	executeBinOp(universal_integer left, universal_integer right) const {
		if ((left == VHDL_TRUE) && (right == VHDL_TRUE)) {
			return VHDL_FALSE;
		}

		return VHDL_TRUE;
	}
};

//! NOR operator (bit, boolean)
class EnumNOR : public BuiltinBinOpSameType<ConstInteger, universal_integer> {
private:
	/** execute the binary operator
	 *  @param left left operand
	 *  @param right right operand
	 *  @return value of the builtin.
	 */
	virtual universal_integer
	executeBinOp(universal_integer left, universal_integer right) const {
		if ((left == VHDL_TRUE) || (right == VHDL_TRUE)) {
			return VHDL_FALSE;
		}

		return VHDL_TRUE;
	}
};

//! XOR operator (bit, boolean)
class EnumXOR : public BuiltinBinOpSameType<ConstInteger, universal_integer> {
private:
	/** execute the binary operator
	 *  @param left left operand
	 *  @param right right operand
	 *  @return value of the builtin.
	 */
	virtual universal_integer
	executeBinOp(universal_integer left, universal_integer right) const {
		if (left != right) {
			return VHDL_TRUE;
		}

		return VHDL_FALSE;
	}
};

//! XNOR operator (bit, boolean)
class EnumXNOR : public BuiltinBinOpSameType<ConstInteger, universal_integer> {
private:
	/** execute the binary operator
	 *  @param left left operand
	 *  @param right right operand
	 *  @return value of the builtin.
	 */
	virtual universal_integer
	executeBinOp(universal_integer left, universal_integer right) const {
		if (left != right) {
			return VHDL_FALSE;
		}

		return VHDL_TRUE;
	}
};

//! unary NOT (bit, boolean)
class EnumNOT : public BuiltinUnop<ConstInteger, universal_integer> {
private:
	/** execute the builtin returning the result.
	 *  @return result of this builtin.
	 */
	virtual universal_integer 
	executeUnop(universal_integer operand) const {
		if (operand == VHDL_TRUE) {
			return VHDL_FALSE;
		}

		return VHDL_TRUE;
	}
};

//! return a predefined value, to be used by enumeration types.
class ReturnValue : public BuiltinFunction {
public:
	//! c'tor.
	/** @param value this value will be returned on execution. 
	 *  @param t type of the return value.
	 */
	ReturnValue(
		universal_integer value,
		const TypeDeclaration *t
		) : 	val(value),
			type(t) {}

	/** execute the builtin returning the result.
	 *  @param args arguments for the builtin function.
	 *  @return the predefined value. */
	virtual Expression* execute(std::list<Expression*> args) const {
		assert(args.empty());

		ConstInteger *ret =  new ConstInteger(this->val, 
					Location("Builtin result"));
		ret->type = new SubtypeIndication(this->type, 
						this->type->location);
		return ret;
	}

private:
	//! value to return on execution.
	universal_integer val;
	//! corresponding type declaration
	const TypeDeclaration *type;
};


};

#endif /* __BUILTIN_FUNCTION_HPP_INCLUDED */
