package com.hammurapi.extract;

import java.util.concurrent.TimeUnit;


public class Equal<T, V, C> extends ComparisonPredicate<T, V, C> {

	private boolean identity;

	public Equal(
			double initialCost, 
			TimeUnit costUnit,
			Extractor<T, V, C> leftExtractor,
			Extractor<T, V, C> rightExtractor, 
			boolean identity) {
		super(initialCost, costUnit, leftExtractor, rightExtractor);
		this.identity = identity;
	}

	@Override
	@SuppressWarnings("unchecked")
	public ComparisonResult compareTo(Extractor<T, Boolean, C> otherPredicate) {
		
		if (otherPredicate instanceof Equal) {
			if (equals(otherPredicate) || operandsAreReverseEqual((Equal) otherPredicate)) {
				return ComparisonResult.EQUAL_NM;
			}
			
			ComparisonResult cr = null;
			if (leftExtractor.equals(((ComparisonPredicate) otherPredicate).leftExtractor)) {
				cr = rightExtractor.compareTo(((ComparisonPredicate) otherPredicate).rightExtractor);
			} else if (leftExtractor.equals(((ComparisonPredicate) otherPredicate).rightExtractor)) {
				cr = rightExtractor.compareTo(((ComparisonPredicate) otherPredicate).leftExtractor);				
			} else if (rightExtractor.equals(((ComparisonPredicate) otherPredicate).leftExtractor)) {
				cr = leftExtractor.compareTo(((ComparisonPredicate) otherPredicate).rightExtractor);				
			} else if (rightExtractor.equals(((ComparisonPredicate) otherPredicate).rightExtractor)) {
				cr = leftExtractor.compareTo(((ComparisonPredicate) otherPredicate).leftExtractor);				
			}
			
			if (cr!=null && cr.isOneToOneMapping()) {
				switch (cr.getType()) {
				case UNEQUAL:
					return ComparisonResult.OPPOSITE_LESS_RESTRICTIVE_NM;
				case OPPOSITE:
					return ComparisonResult.OPPOSITE_NM;
				}
			}
		}		
		
		if (otherPredicate instanceof NotEqual) {
			NotEqual<T, V, C> op = (NotEqual<T, V, C>) otherPredicate;
			return operandsAreEqual(op) || operandsAreReverseEqual(op) ? ComparisonResult.OPPOSITE_NM : ComparisonResult.NOT_EQUAL_NM;
		} 
		
		if (otherPredicate instanceof Not) {
			Not<T, C> op = (Not<T, C>) otherPredicate;
			ComparisonResult cr = compareTo(op.getPredicate());
			if (cr.isOneToOneMapping()) {
				switch (cr.getType()) {
				case EQUAL:
					return ComparisonResult.OPPOSITE_NM;
				case OPPOSITE:
					return ComparisonResult.EQUAL_NM;
				default:
					return ComparisonResult.NOT_EQUAL_NM;
				}
			}
			/**
			 * TODO Implement comparisons.
			 * a * b ? c * d, * - can be <, <=, ==, !=, =>, >
			 * 36 permutations. Each permutation - compare a/b, c/d 
			 */
			
			// TODO Address mappings
		}
		return super.compareTo(otherPredicate);
	}

	@Override
	protected boolean compare(int n1, int n2) {
		return n1==n2;
	}
	
	@Override
	protected boolean compare(Object o1, Object o2) {
		if (identity) {
			return o1==o2;
		}
		
		if (o1==null) {
			return o2==null;
		}

		return o1.equals(o2);
	}

	@Override
	protected boolean compare(long n1, long n2) {
		return n1==n2;
	}

	@Override
	protected boolean compare(float n1, float n2) {
		return n1==n2;
	}

	@Override
	protected boolean compare(double n1, double n2) {
		return n1==n2;
	}

	@Override
	protected Extractor<T, Boolean, C> newInstance(Extractor<T, V, C> leftExtractor, Extractor<T, V, C> rightExtractor) {
		return new Equal<T,V,C>(initialCost, costUnit, leftExtractor, rightExtractor, identity);
	}
	
	public boolean isIdentity() {
		return identity;
	}
}
