package org.reldb.rel.v0.types;

import org.reldb.rel.exceptions.*;
import org.reldb.rel.v0.generator.Generator;
import org.reldb.rel.v0.values.Value;
import org.reldb.rel.v0.values.ValueTuple;

/** Defines a join mapping between a two source TypeTupleS into a destination TypeTuple. */
public class JoinMap {
	private int degree;
	private int[] leftCommonToRight;
	private boolean[] rightExclude;
	private int commonDegree;
	
	/** Create a JoinMap to join tuples of the source types, which may contain common attributes,
	 * to the destination type. */
	public JoinMap(Heading destinationType, Heading leftType, Heading rightType) {
		degree = destinationType.getDegree();
		Heading intersection = leftType.intersect(rightType);
		if (intersection.getDegree() == 0)
			throw new ExceptionFatal("RS0382: " + leftType + " and " + rightType + " have no common attributes, and therefore need not be mapped.");
		leftCommonToRight = new int[leftType.getDegree()];
		rightExclude = new boolean[rightType.getDegree()];
		int leftAttributeIndex = 0;
		for (Attribute attribute: leftType.getAttributes()) {
			int rightAttributeIndex = rightType.getIndexOf(attribute.getName());
			leftCommonToRight[leftAttributeIndex++] = rightAttributeIndex;
			if (rightAttributeIndex >= 0)
				rightExclude[rightAttributeIndex] = true;
		}
		commonDegree = 0;
		for (int i=0; i<leftCommonToRight.length; i++)
			commonDegree += (leftCommonToRight[i] >= 0) ? 1 : 0; 
	}

	/** Return true if the left and right tuples have matching common attributes. */
	public boolean isJoinable(ValueTuple left, ValueTuple right) {
		Value[] leftValues = left.getValues();
		Value[] rightValues = right.getValues();
		for (int i=0; i<leftCommonToRight.length; i++)
			if (leftCommonToRight[i] >= 0 && !(leftValues[i].compareTo(rightValues[leftCommonToRight[i]])==0))
				return false;
		return true;
	}

	/** Return a new tuple consisting of a left tuple's common attributes. */
	public ValueTuple getLeftTupleCommon(Generator generator, ValueTuple left) {
		Value[] leftValues = left.getValues();
		Value[] leftCommonValues = new Value[commonDegree];
		int leftIndex = 0;
		for (int i=0; i<leftCommonToRight.length; i++)
			if (leftCommonToRight[i] >= 0)
				leftCommonValues[leftIndex++] = leftValues[i];
		return new ValueTuple(generator, leftCommonValues);
	}
	
	/** Return a new tuple consisting of a right tuple's common attributes. */
	public ValueTuple getRightTupleCommon(Generator generator, ValueTuple right) {
		Value[] rightValues = right.getValues();
		Value[] rightCommonValues = new Value[commonDegree];
		int rightIndex = 0;
		for (int i=0; i<leftCommonToRight.length; i++)
			if (leftCommonToRight[i] >= 0)
				rightCommonValues[rightIndex++] = rightValues[leftCommonToRight[i]];
		return new ValueTuple(generator, rightCommonValues);
	}
	
	/** Create a new tuple by a join of the left and right ValueTupleS.  
	 * Assume that common attributes have matching values, as (perhaps) 
	 * determined by isJoinable(). */
	public ValueTuple join(Generator generator, ValueTuple left, ValueTuple right) {
		Value[] leftValues = left.getValues();
		Value[] rightValues = right.getValues();
		Value[] target = new Value[degree];
		System.arraycopy(leftValues, 0, target, 0, leftValues.length);		
		int destinationIndex = leftValues.length;
		for (int i=0; i<rightValues.length; i++)
			if (!rightExclude[i])
				target[destinationIndex++] = rightValues[i];
		return new ValueTuple(generator, target);
	}

	/** Create a new tuple by a join of the left and right ValueTupleS.  
	 * Throw an exception if common attributes do not have matching values. */
	public ValueTuple joinChecked(Generator generator, ValueTuple left, ValueTuple right) {
		Value[] leftValues = left.getValues();
		Value[] rightValues = right.getValues();
		Value[] target = new Value[degree];
		int destinationIndex = 0;
		for (int i=0; i<leftCommonToRight.length; i++) {
			if (leftCommonToRight[i] >= 0 && !(leftValues[i].compareTo(rightValues[leftCommonToRight[i]])==0))
				throw new ExceptionSemantic("RS0254: Common attributes differ in value.");
			target[destinationIndex++] = leftValues[i];
		}
		for (int i=0; i<rightValues.length; i++)
			if (!rightExclude[i])
				target[destinationIndex++] = rightValues[i];
		return new ValueTuple(generator, target);
	}

	public String toString() {
		String outLR = null;
		for (int i=0; i<leftCommonToRight.length; i++)
			if (leftCommonToRight[i] >= 0)
				outLR = ((outLR==null) ? "" : outLR + ", ") + i + "=" + leftCommonToRight[i];
		String outR = "";
		for (int i=0; i<rightExclude.length; i++)
			outR += ((rightExclude[i]) ? "x" : "_");
		return "JoinMap commonLR=[" + ((outLR == null) ? "" : outLR) + "] excludeR=[" + outR + "]";
	}
}
