/*
 * @(#)BooleanOperation.java  2.5.0  24 July 2006
 *
 * Copyright 2006
 * College of Computer and Information Science
 * Northeastern University
 * Boston, MA  02115
 *
 * The Java Power Tools software may be used for educational
 * purposes as long as this copyright notice is retained intact
 * at the top of all source files.
 *
 * To discuss possible commercial use of this software, 
 * contact Richard Rasala at Northeastern University, 
 * College of Computer and Information Science,
 * 617-373-2462 or rasala@ccs.neu.edu.
 *
 * The Java Power Tools software has been designed and built
 * in collaboration with Viera Proulx and Jeff Raab.
 *
 * Should this software be modified, the words "Modified from 
 * Original" must be included as a comment below this notice.
 *
 * All publication rights are retained.  This software or its 
 * documentation may not be published in any media either
 * in whole or in part without explicit permission.
 *
 * This software was created with support from Northeastern 
 * University and from NSF grant DUE-9950829.
 */

package edu.neu.ccs.parser;

import java.text.ParseException;

import edu.neu.ccs.XBigInteger;
import edu.neu.ccs.XBoolean;
import edu.neu.ccs.XDouble;
import edu.neu.ccs.XNumber;

/**
 * <p>Class used to store a boolean operation.</p>
 *
 * <p>As of 2.5.0, this class was extracted from the class
 * <code>JPTParser</code> and made standalone.</p>
 *
 * <p>Unlike <code>Operation</code>, this class is concrete
 * in order to allow some methods that may not be needed to
 * use the default implementation that returns
 * <code>null</code>.</p>
 *
 * <p>Operations that will be used must be overridden.</p>
 * 
 * @author  Richard Rasala
 * @author  Jeff Raab
 */
public class BooleanOperation
    extends Operation
{
    
    /**
     * Whether or not the operation can accept
     * boolean or numeric arguments.
     * 
     * The default value is true.
     */
    protected boolean booleanOrNumeric = true;
    
    
    //////////////////
    // Constructors //
    //////////////////
    
    /**
     * Constructs an operation with the given symbol.
     *
     * @param s the symbol() for the operation
     */
    public BooleanOperation(String s) { super(s); }
    
    
    /**
     * Constructs an operation with the given symbol and settings
     * for unary and binary usage.
     *
     * @param s      the symbol() for the operation
     * @param unary  whether the operation may be unary
     * @param binary whether the operation may be binary
     */
    public BooleanOperation(String s, boolean unary, boolean binary) {
        super(s, unary, binary);
    }
    
    
    /**
     * Constructs an operation with the given symbol and settings
     * for unary and binary usage.
     *
     * @param s      the symbol() for the operation
     * @param unary  whether the operation may be unary
     * @param binary whether the operation may be binary
     * @param b_or_n whether the operation can accept
     *               boolean or numeric arguments
     */
    public BooleanOperation
        (String s, boolean unary, boolean binary, boolean b_or_n)
    {
        super(s, unary, binary);
        
        booleanOrNumeric = b_or_n;
    }
    
    
    /////////////
    // Methods //
    /////////////
    
    /**
     * <p>Performs the operation on the given values
     * and returns the result.</p>
     *
     * @param left  the left side operand for a binary operation
     *              or <code>null</code> for a unary operation
     * @param right the right side operand for a unary or binary
     *              operation
      */
    public final Object performOperation(Object left, Object right) 
        throws ParseException
    {
        XBoolean p;
        XBoolean q;
        
        XBigInteger a;
        XBigInteger b;
        
        XDouble x;
        XDouble y;
        
        if (left == null)
        // handle unary case
        {
            checkUnary();
            
            
            if (ParserUtilities.isXBoolean(right)) {
                p = (XBoolean)right;
                
                return unaryForXBoolean(p);
            }
            
            if (! booleanOrNumeric) {
                throw new ParseException(
                    "Boolean operation "
                    + symbol()
                    + " expects 1 boolean argument"
                    , 0);
            }
            
            if (ParserUtilities.isXIntegral(right)) {
                a = ParserUtilities.toXBigInteger((XNumber)right);
                
                return unaryForXIntegral(a);
            }
            
            if (ParserUtilities.isXNumber(right)) {
                x = ParserUtilities.toXDouble((XNumber)right);
                
                return unaryForXFloating(x);
            }
            
            throw new ParseException(
                "Boolean operation "
                + symbol()
                + " expects 1 boolean or 1 numeric argument"
                , 0);
        }
        else
        // handle binary case
        {
            checkBinary();
        
            if (ParserUtilities.isXBoolean(left) &&
                ParserUtilities.isXBoolean(right))
            {
                p = (XBoolean)left;
                q = (XBoolean)right;
                
                return binaryForXBoolean(p, q);
            }
            
            if (! booleanOrNumeric) {
                throw new ParseException(
                    "Boolean operation "
                    + symbol()
                    + " expects 2 boolean arguments"
                    , 0);
            }
            
            if (ParserUtilities.isXIntegral(left) &&
                ParserUtilities.isXIntegral(right))
            {
                a = ParserUtilities.toXBigInteger((XNumber)left);
                b = ParserUtilities.toXBigInteger((XNumber)right);
                
                return binaryForXIntegral(a, b);
            }
            
            if (ParserUtilities.isXNumber(left) &&
                ParserUtilities.isXNumber(right))
            {
                x = ParserUtilities.toXDouble((XNumber)left);
                y = ParserUtilities.toXDouble((XNumber)right);
                
                return binaryForXFloating(x, y);
            }
            
            throw new ParseException(
                "Boolean operation "
                + symbol()
                + " expects 2 boolean or 2 numeric arguments"
                , 0);
        }
    }
    
    /**
     * Returns whether or not the operation can accept
     * boolean or numeric arguments.
     */
    public final boolean handlesBooleanOrNumeric() {
        return booleanOrNumeric;
    }
    
    
    /**
     * Override this method to specify the result of this operation
     * acting as a unary operation on a boolean argument.
     *
     * @param p the argument
     */
    public Object unaryForXBoolean(XBoolean p) {
        return null;
    }
    
    
    /**
     * Override this method to specify the result of this operation
     * acting as a binary operation on boolean arguments.
     *
     * @param p argument 1
     * @param q argument 2
     */
    public Object binaryForXBoolean(XBoolean p, XBoolean q) {
        return null;
    }
    
    
    /**
     * Override this method to specify the result of this operation
     * acting as a unary operation on an integral argument.
     *
     * @param a the argument
     */
    public Object unaryForXIntegral(XBigInteger a) {
        return null;
    }
    
    
    /**
     * Override this method to specify the result of this operation
     * acting as a binary operation on integral arguments.
     *
     * @param a argument 1
     * @param b argument 2
     */
    public Object binaryForXIntegral(XBigInteger a, XBigInteger b) {
        return null;
    }
    
    
    /**
     * Override this method to specify the result of this operation
     * acting as a unary operation on a floating argument.
     *
     * @param x the argument
     */
    public Object unaryForXFloating(XDouble x) {
        return null;
    }
    
    
    /**
     * Override this method to specify the result of this operation
     * acting as a binary operation on floating arguments.
     *
     * @param x argument 1
     * @param y argument 2
     */
    public Object binaryForXFloating(XDouble x, XDouble y) {
        return null;
    }
    
}

