// Copyright (c) 1996-2002 Brian D. Carlstrom

package bdc.scheme.procedure;

import bdc.scheme.Scheme;
import bdc.scheme.SchemeException;
import bdc.scheme.Stack;
import bdc.scheme.exception.ArgumentCountException;
import bdc.scheme.expression.ProcedureN;
import bdc.util.Constants;

/**
    (- x &hellip;)
*/
public class Minus extends ProcedureN
{
    public Object apply0 (Stack stack) throws SchemeException
    {
        throw new ArgumentCountException(this, 0, 0);
    }

    public Object apply1 (Stack stack) throws SchemeException
    {
        Object o1 = stack.array[stack.inUse-1];
        Number n = Scheme.number(o1, this);

        if (n instanceof Integer) {
            return Constants.getInteger(-(n.intValue()));
        }
        return new Double(-(n.doubleValue()));
    }

    public Object apply2 (Stack stack) throws SchemeException
    {
        Object o1 = stack.array[stack.inUse-1];
        Object o2 = stack.array[stack.inUse-2];
        Number n1  = Scheme.number(o1, this);
        Number n2  = Scheme.number(o2, this);

        if ((n1 instanceof Double) ||
            (n2 instanceof Double))
        {
            return new Double(n1.doubleValue() - n2.doubleValue());
        }
        return Constants.getInteger(n1.intValue() - n2.intValue());
    }

    public Object apply3 (Stack stack) throws SchemeException
    {
        Object o1 = stack.array[stack.inUse-1];
        Object o2 = stack.array[stack.inUse-2];
        Object o3 = stack.array[stack.inUse-3];
        Number n1  = Scheme.number(o1, this);
        Number n2  = Scheme.number(o2, this);
        Number n3  = Scheme.number(o3, this);

        if ((n1 instanceof Double) ||
            (n2 instanceof Double) ||
            (n3 instanceof Double))
        {
            return new Double(n1.doubleValue() -
                              n2.doubleValue() -
                              n3.doubleValue());
        }
        return Constants.getInteger(n1.intValue() -
                            n2.intValue() -
                            n3.intValue());
    }

    public Object apply4 (Stack stack) throws SchemeException
    {
        Object o1 = stack.array[stack.inUse-1];
        Object o2 = stack.array[stack.inUse-2];
        Object o3 = stack.array[stack.inUse-3];
        Object o4 = stack.array[stack.inUse-4];
        Number n1  = Scheme.number(o1, this);
        Number n2  = Scheme.number(o2, this);
        Number n3  = Scheme.number(o3, this);
        Number n4  = Scheme.number(o4, this);

        if ((n1 instanceof Double) ||
            (n2 instanceof Double) ||
            (n3 instanceof Double) ||
            (n4 instanceof Double))
        {
            return new Double(n1.doubleValue() -
                              n2.doubleValue() -
                              n3.doubleValue() -
                              n4.doubleValue());
        }
        return Constants.getInteger(n1.intValue() -
                            n2.intValue() -
                            n3.intValue() -
                            n4.intValue());
    }

    public Object applyN (int n, Stack stack) throws SchemeException
    {
        boolean integerResult;
        int     intResult    = 0;
        double  doubleResult = 0;

        Number number = Scheme.number(stack.array[stack.inUse-1],
                                      this);
        if (number instanceof Integer) {
            intResult     = number.intValue();
            integerResult = true;
        }
        else {
            doubleResult  = number.doubleValue();
            integerResult = false;
        }

        for (int i = 2; i <= n; i++) {
            number = Scheme.number(stack.array[stack.inUse-i], this);
            if (!integerResult) {
                doubleResult -= number.doubleValue();
            }
            else if (number instanceof Integer) {
                intResult    -= number.intValue();
            }
            else {
                integerResult = false;
                doubleResult  = (double)intResult;
                doubleResult -= number.doubleValue();
            }
        }

        if (integerResult) {
            return Constants.getInteger(intResult);
        }
        return new Double(doubleResult);
    }
}
