package org.exist.xquery.value;

import java.text.Collator;

import org.exist.xquery.Constants;
import org.exist.xquery.XPathException;

public abstract class NumericValue extends ComputableValue {

	public abstract String getStringValue() throws XPathException;

	public abstract AtomicValue convertTo(int requiredType) throws XPathException;
	
	public double getDouble() throws XPathException {
		return ((DoubleValue)convertTo(Type.DOUBLE)).getValue();
	}
	
	public long getLong() throws XPathException {
		return ((IntegerValue)convertTo(Type.INTEGER)).getValue();
	}
	
	public int getInt() throws XPathException {
		return (int)((IntegerValue)convertTo(Type.INTEGER)).getValue();
	}
	
	public boolean isNaN() {
		return false;
	}

	public boolean isInfinite() {
		return false;
	}
	
	/* (non-Javadoc)
	 * @see org.exist.xquery.value.AtomicValue#compareTo(int, org.exist.xquery.value.AtomicValue)
	 */
	public boolean compareTo(Collator collator, int operator, AtomicValue other)
		throws XPathException {
		if(Type.subTypeOf(other.getType(), Type.NUMBER)) {
			double otherVal = ((NumericValue)other).getDouble();
			double val = getDouble();
			switch(operator) {
				case Constants.EQ:
					return val == otherVal;
				case Constants.NEQ:
					return val != otherVal;
				case Constants.GT:
					return val > otherVal;
				case Constants.GTEQ:
					return val >= otherVal;
				case Constants.LT:
					return val < otherVal;
				case Constants.LTEQ:
					return val <= otherVal;
				default:
					throw new XPathException("Type error: cannot apply operator to numeric value");
			}
		}
		throw new XPathException("Type error: cannot compare operands: " + 
			Type.getTypeName(getType()) + " and " + 
			Type.getTypeName(other.getType()));
	}
	
	/* (non-Javadoc)
	 * @see org.exist.xquery.value.AtomicValue#compareTo(org.exist.xquery.value.AtomicValue)
	 */
	public int compareTo(Collator collator, AtomicValue other) throws XPathException {
		if(Type.subTypeOf(other.getType(), Type.NUMBER)) {
			double otherVal = ((NumericValue)other).getDouble();
			double val = getDouble();
			if(val == otherVal)
				return Constants.EQUAL;
			else if(val > otherVal)
				return Constants.SUPERIOR;
			else
				return Constants.INFERIOR;
		} else {
			throw new XPathException("cannot compare numeric value to non-numeric value");
		}
	}
	
	public abstract NumericValue negate() throws XPathException;
	public abstract NumericValue ceiling() throws XPathException;
	public abstract NumericValue floor() throws XPathException;
	public abstract NumericValue round() throws XPathException;
	public abstract NumericValue round(IntegerValue precision) throws XPathException;
	public abstract NumericValue mod(NumericValue other) throws XPathException;
	//TODO : implement here ?
	public abstract	IntegerValue idiv(NumericValue other) throws XPathException;
	public abstract NumericValue abs() throws XPathException;
	public abstract AtomicValue max(Collator collator, AtomicValue other) throws XPathException;
	public abstract AtomicValue min(Collator collator, AtomicValue other) throws XPathException;

	
}
