package jscl.math.operator;

import jscl.math.*;
import jscl.math.function.Pow;
import jscl.mathml.MathML;
import jscl.text.ParserUtils;

import javax.annotation.Nonnull;

/**
 * User: serso
 * Date: 12/15/11
 * Time: 10:32 PM
 */
public class DoubleFactorial extends PostfixFunction {

    public static final String NAME = "!!";

    public DoubleFactorial(Generic expression) {
        super(NAME, new Generic[]{expression});
    }

    private DoubleFactorial(Generic[] parameter) {
        super(NAME, ParserUtils.copyOf(parameter, 1));
    }

    @Override
    public int getMinParameters() {
        return 1;
    }

    public Generic selfExpand() {
        return expressionValue();
    }

    @Override
    public Generic selfNumeric() {
        final Generic parameter = parameters[0];
        if (parameter.isInteger()) {
            int n = parameter.integerValue().intValue();
            if (n < 0) {
                //return expressionValue();
                throw new ArithmeticException("Cannot take factorial from negative integer!");
            }

            Generic result;
            if (n == 0) {
                result = JsclInteger.valueOf(1);
            } else {
                int i;
                if (n % 2 != 0) {
                    // odd
                    i = 1;
                } else {
                    // even
                    i = 2;
                }

                result = JsclInteger.valueOf(i);
                for (; i < n; i += 2) {
                    ParserUtils.checkInterruption();
                    result = result.multiply(JsclInteger.valueOf(i + 2));
                }
            }

            if (result instanceof JsclInteger) {
                return new NumericWrapper(((JsclInteger) result));
            } else {
                throw new NotIntegerException();
            }

        } else {
            throw new NotIntegerException();
        }
    }

    public void toMathML(MathML element, Object data) {
        int exponent = data instanceof Integer ? (Integer) data : 1;
        if (exponent == 1) bodyToMathML(element);
        else {
            MathML e1 = element.element("msup");
            bodyToMathML(e1);
            MathML e2 = element.element("mn");
            e2.appendChild(element.text(String.valueOf(exponent)));
            e1.appendChild(e2);
            element.appendChild(e1);
        }
    }

    @Nonnull
    @Override
    public Operator newInstance(@Nonnull Generic[] parameters) {
        return new DoubleFactorial(parameters);
    }

    void bodyToMathML(MathML element) {
        MathML e1 = element.element("mrow");
        try {
            JsclInteger en = parameters[0].integerValue();
            en.toMathML(e1, null);
        } catch (NotIntegerException e) {
            try {
                Variable v = parameters[0].variableValue();
                if (v instanceof Pow) {
                    GenericVariable.valueOf(parameters[0]).toMathML(e1, null);
                } else v.toMathML(e1, null);
            } catch (NotVariableException e2) {
                GenericVariable.valueOf(parameters[0]).toMathML(e1, null);
            }
        }
        MathML e2 = element.element("mo");
        e2.appendChild(element.text("!!"));
        e1.appendChild(e2);
        element.appendChild(e1);
    }

    @Nonnull
    public Variable newInstance() {
        return new DoubleFactorial((Generic) null);
    }
}
