/*****************************************************************************

 JEP 2.4.1, Extensions 1.1.1
      April 30 2007
      (c) Copyright 2007, Nathan Funk and Richard Morris
      See LICENSE-*.txt for license information.

 *****************************************************************************/

package weasel.jep.function;


import java.util.Stack;

import weasel.Calc;
import weasel.jep.ParseException;
import weasel.jep.type.Complex;


/**
 * Addition function. Supports any number of parameters although typically only
 * 2 parameters are used.
 * 
 * @author nathan
 */
public class Add extends PostfixMathCommand {

	public Add() {
		numberOfParameters = -1;
	}

	/**
	 * Calculates the result of applying the "+" operator to the arguments from
	 * the stack and pushes it back on the stack.
	 */
	@Override
	public void run(Stack stack) throws ParseException {
		checkStack(stack);// check the stack

		Object sum = stack.pop();
		Object param;
		int i = 1;

		// repeat summation for each one of the current parameters
		while (i < curNumberOfParameters) {
			// get the parameter from the stack
			param = stack.pop();

			// add it to the sum (order is important for String arguments)
			sum = add(param, sum);

			i++;
		}

		stack.push(sum);

		return;
	}

	/**
	 * Adds two numbers together. The parameters can be of type Number, Complex,
	 * or String. If a certain combination of types is not supported, a
	 * ParseException is thrown.
	 * 
	 * @param param1 The first parameter to be added.
	 * @param param2 The second parameter to be added.
	 * @return The sum of param1 and param2, or concatenation of the two if they
	 *         are Strings.
	 * @throws ParseException
	 */
	public Object add(Object param1, Object param2) throws ParseException {

		if (param1 instanceof String || param2 instanceof String) {
			StringBuilder sb = new StringBuilder();
			sb.append(Calc.toString(param1));
			sb.append(Calc.toString(param2));
			return sb.toString();
		}


		if (param1 instanceof Complex) {
			if (param2 instanceof Complex)
				return add((Complex) param1, (Complex) param2);
			else if (param2 instanceof Number) return add((Complex) param1, (Number) param2);
		} else if (param1 instanceof Number) {

			if (param2 instanceof Complex)
				return add((Complex) param2, (Number) param1);
			else if (param2 instanceof Number)
				return add((Number) param1, (Number) param2);
			else if (param2 instanceof Boolean) return add((Number) param1, ((Boolean) param2) ? 1 : 0);

		} else if ((param1 instanceof String) && (param2 instanceof String)) {
			return (String) param1 + (String) param2;
		} else if ((param1 instanceof Boolean) && (param2 instanceof Boolean)) {
			return add(((Boolean) param1) ? 1 : 0, ((Boolean) param2) ? 1 : 0);
		} else if ((param1 instanceof Boolean) && (param2 instanceof Number)) {
			return add(((Boolean) param1) ? 1 : 0, (Number) param2);
		}


		throw new ParseException("Can't add " + param2.getClass().getSimpleName() + " to " + param1.getClass().getSimpleName());
	}

	public Double add(Number d1, Number d2) {
		return new Double(d1.doubleValue() + d2.doubleValue());
	}

	public Complex add(Complex c1, Complex c2) {
		return new Complex(c1.re() + c2.re(), c1.im() + c2.im());
	}

	public Complex add(Complex c, Number d) {
		return new Complex(c.re() + d.doubleValue(), c.im());
	}
}
