package prefuse.data.expression;

import java.util.Comparator;

import prefuse.data.Schema;
import prefuse.data.Tuple;
import prefuse.util.TypeLib;
import prefuse.util.collections.DefaultLiteralComparator;
import prefuse.util.collections.LiteralComparator;

/**
 * Predicate implementation that computes a comparison operation. Supported
 * operations are equals, not equals, less than, greater than, less than or
 * equal to, and greater than or equal to.
 * 
 * @author <a href="http://jheer.org">jeffrey heer</a>
 */
public class ComparisonPredicate extends BinaryExpression implements Predicate {

    /** Indicates a less-than comparison. */
    public static final int LT   = 0;
    /** Indicates a greater-than comparison. */
    public static final int GT   = 1;
    /** Indicates a equals comparison. */
    public static final int EQ   = 2;
    /** Indicates a not-equals comparison. */
    public static final int NEQ  = 3;
    /** Indicates a less-than-or-equals comparison. */
    public static final int LTEQ = 4;
    /** Indicates a greater-than-or-equals comparison. */
    public static final int GTEQ = 5;
    
    private Comparator m_cmp;
    
    /**
     * Create a new ComparisonPredicate. Uses a default comparator instance.
     * @param operation the comparison operation to compute
     * @param left the left sub-expression
     * @param right the right sub-expression
     */
    public ComparisonPredicate(int operation, 
            Expression left, Expression right)
    {
        this(operation, left, right, DefaultLiteralComparator.getInstance());
    }

    /**
     * Create a new ComparisonPredicate.
     * @param operation the comparison operation to compute
     * @param left the left sub-expression
     * @param right the right sub-expression
     * @param cmp the comparator to use to compare values
     */
    public ComparisonPredicate(int operation, 
            Expression left, Expression right, Comparator cmp)
    {
        super(operation, LT, GTEQ, left, right);
        this.m_cmp = cmp;
    }
    
    
    /**
     * Get the comparator used to compare instances.
     * @return the comparator instance
     */
    public Comparator getComparator() {
        return m_cmp;
    }
    
    // ------------------------------------------------------------------------
    
    /**
     * @see prefuse.data.expression.Expression#getType(prefuse.data.Schema)
     */
    public Class getType(Schema s) {
        return boolean.class;
    }
    
    /**
     * @see prefuse.data.expression.Expression#getBoolean(prefuse.data.Tuple)
     */
    public boolean getBoolean(Tuple t) {
        Class lType = m_left.getType(t.getSchema());
        Class rType = m_right.getType(t.getSchema());
        
        int c = 0;
        if ( TypeLib.isNumericType(lType) && TypeLib.isNumericType(rType) ) {
            Class type = TypeLib.getNumericType(lType, rType);
            if ( type == int.class || type == byte.class ) {
                int x = m_left.getInt(t);
                int y = m_right.getInt(t);
                c = ((LiteralComparator)m_cmp).compare(x,y);
            } else if ( type == long.class ) {
                long x = m_left.getLong(t);
                long y = m_right.getLong(t);
                c = ((LiteralComparator)m_cmp).compare(x,y);
            } else if ( type == float.class ) {
                float x = m_left.getFloat(t);
                float y = m_right.getFloat(t);
                c = ((LiteralComparator)m_cmp).compare(x,y);
            } else if ( type == double.class ) {
                double x = m_left.getDouble(t);
                double y = m_right.getDouble(t);
                c = ((LiteralComparator)m_cmp).compare(x,y);
            } else {
                throw new IllegalStateException();
            }
        } else {
            c = m_cmp.compare(m_left.get(t), m_right.get(t));
        }
       
        switch ( m_op ) {
        case LT:
            return ( c == -1 );
        case GT:
            return ( c == 1 );
        case EQ:
            return ( c == 0 );
        case NEQ:
            return ( c != 0 );
        case LTEQ:
            return ( c <= 0 );
        case GTEQ:
            return ( c >= 0 );
        default:
            throw new IllegalStateException("Unknown operation.");
        }
    }

    /**
     * @see prefuse.data.expression.Expression#get(prefuse.data.Tuple)
     */
    public Object get(Tuple t) {
        return ( getBoolean(t) ? Boolean.TRUE : Boolean.FALSE );
    }
    
    /**
     * @see java.lang.Object#toString()
     */
    public String toString() {
        String op = "?";
        switch ( m_op ) {
        case LT:
            op = "<";
            break;
        case GT:
            op = ">";
            break;
        case EQ:
            op = "=";
            break;
        case NEQ:
            op = "!=";
            break;
        case LTEQ:
            op = "<=";
            break;
        case GTEQ:
            op = ">=";
            break;
        }
        return m_left.toString()+' '+op+' '+m_right.toString();
    }
    
} // end of class BinaryPredicate
