package ec.gp.semantic;

import java.util.*;

/**
 * Base class for desired semantics with multiple permitted values for each fitness case. The values for each fitness
 * case are guaranteed to be sorted ascending.
 *
 * @param <SemType>
 * @author Tomasz Pawlak
 */
public class DesiredSemanticsBase<SemType> {
	private final static DesiredSemanticsCmp cmp = new DesiredSemanticsCmp();
	protected final NavigableSet<SemType>[] values;
	
	protected DesiredSemanticsBase(NavigableSet<SemType>[] values) {
		this.values = values;
	}
	
	public DesiredSemanticsBase(int length) {
		this.values = new TreeSet[length];
		for (int i = 0; i < length; ++i) {
			this.values[i] = new TreeSet<SemType>(cmp);
		}
	}
	
	public DesiredSemanticsBase(final ISemantics other) {
		this(other.size());
		
		int i = 0;
		for (final Object v : other) {
			this.addValuesFast(i++, (SemType) v);
		}
	}
	
	public DesiredSemanticsBase(final SemType... other) {
		this(other.length);
		
		int i = 0;
		for (final SemType v : other) {
			this.addValuesFast(i++, v);
		}
	}
	
	@Deprecated
	public DesiredSemanticsBase(final double... other) {
		this(other.length);
		
		int i = 0;
		for (final double v : other) {
			this.addValuesFast(i++, (SemType) (Double) v);
		}
	}

	/*private void prepareCollection(final int fitnessCase) {
		while (this.values.size() <= fitnessCase)
			this.values.add(new TreeSet<SemType>(this.cmp));
	}*/
	
	public void addValues(final int fitnessCase, final SemType... values) {
		assert fitnessCase >= 0 && fitnessCase < this.values.length;
		this.addValuesFast(fitnessCase, values);
	}
	
	private void addValuesFast(final int fitnessCase, final SemType... values) {
		assert values != null;
		
		final Set<SemType> set = this.values[fitnessCase];
		for (final SemType value : values) {
			set.add(value);
		}
	}
	
	public void markInconsistent(int fitnessCase) {
		assert fitnessCase >= 0 && fitnessCase < this.values.length;
		
		Set<SemType> set = this.values[fitnessCase];
		set.clear();
		set.add(null);
	}
	
	public void markDontCare(int fitnessCase) {
		assert fitnessCase >= 0 && fitnessCase < this.values.length;
		
		Set<SemType> set = this.values[fitnessCase];
		set.clear();
	}
	
	public NavigableSet<SemType>[] getValues() {
		return this.values;
	}
	
	/**
	 * If set is empty, then we `don't care' what value is at this position, if set contains only null, then the
	 * semantics is `inconsistent' at this position, otherwise set contains all possible values for this position.
	 *
	 * @param fitnessCase
	 * @return
	 */
	public NavigableSet<SemType> getValuesFor(int fitnessCase) {
		assert fitnessCase >= 0 && fitnessCase < this.values.length;
		
		NavigableSet<SemType> set = this.values[fitnessCase];
		
		assert set != null;
		return set;
	}
	
	public int size() {
		return this.values.length;
	}
	
	@Override
	public int hashCode() {
		int hash = 0;
		int i = 0;
		for (NavigableSet<SemType> value : values) {
			hash ^= value.hashCode() << i;
		}
		return hash;
	}
	
	@Override
	public boolean equals(Object obj) {
		if (!(obj instanceof DesiredSemanticsBase<?>))
			return false;
		
		DesiredSemanticsBase<SemType> other = (DesiredSemanticsBase<SemType>) obj;
		if (this.values.length != other.values.length)
			return false;
		
		for (int i = 0; i < this.values.length; ++i) {
			if (!this.values[i].equals(other.values[i])) {
				return false;
			}
		}
		
		assert this.hashCode() == obj.hashCode();
		return true;
	}
	
	/**
	 * Decreases memory usage of this desired semantics. This action is representation-specific and may make this
	 * object read-only.
	 */
	public void compact() {
		
	}
	
	/**
	 * Verifies whether the given semantics matches this desired semantics, i.e., any combination of desired semantics
	 * elements.
	 *
	 * @param semantics
	 * @return
	 */
	/*public boolean match(ISemantics semantics) {
		if (this.values.length != semantics.size())
			return false;

		int matches = 0;

		for (int i = 0; i < this.values.length; ++i) {
			for (SemType e : this.values[i]) {
				if (e.equals(semantics.getValue(i))) {
					++matches;
					break;
				}
			}

			if (matches < i)
				break;
		}

		assert matches <= this.values.length;

		return matches == this.values.length;
	}*/
}

final class DesiredSemanticsCmp implements Comparator {
	
	@Override
	public final int compare(final Object o1, final Object o2) {
		if (o1 == o2) //null == null, otherwise references are equal
			return 0;
		else if (o1 == null)
			return -1;
		else if (o2 == null)
			return 1;
		
		return ((Comparable) o1).compareTo(o2);
	}
	
}
