package relational;

import Tools.Assert;
import java.lang.reflect.*;
import java.util.Arrays;

/**
 * A simple (inefficient for network transfer) implementation of Row --
 * it stores every object that has a field referenced by the
 * columnspec.<p>
 * 
 * (A more efficiently serializable implementation would store only the
 * necessary fields / primary keys.)
 */
public class BasicRow
	implements Row {

	FromClause fromClause;
	Relational[] theTables;
		// these should mirror 1-1 the tables in the ColumnSpec.fromClause.

	public BasicRow(FromClause fromClause, Row row,
		int index, Relational oneMore) {
		// this constructor is handy when you have a BasicRow, and you
		// want to tack on just one more item.
		// index should be the table[] index where oneMore goes,
		// with respect to row.getFromClause(). We ask for this so you
		// can compute it once, say, with
		//			fromClause.getIndex(oneMore.getClass())
		// and then we don't have to figure it out each time here.
		this.fromClause = fromClause;
		if (row instanceof BasicRow) {
			// insert oneMore into row at index: copy row array
			// to our new array in two pieces, then stuff oneMore in
			// the middle.
			Relational[] source = ((BasicRow) row).theTables;
			this.theTables = new Relational[source.length+1];
			if (index>0) {
				System.arraycopy(source, 0, theTables, 0, index);
			}
			if (index<source.length) {
				System.arraycopy(source, index, theTables, index+1,
					source.length-index);
			}
			this.theTables[index] = oneMore;
		} else {
			// source row is a singleton object
			if (index==0) {
				theTables = new Relational[] { oneMore, (Relational) row };
			} else {
				theTables = new Relational[] { (Relational) row, oneMore };
			}
		}
	}

	public BasicRow(FromClause fromClause, Relational[] data) {
		this.fromClause = fromClause;
		this.theTables = data;
	}

	public Relational getTable(FromClause fc, int table) {
		Assert.assert(fromClause == fc);
		return theTables[table];
	}

	public Object getField(int tableIndex, FieldDescriptor fd) {
		return fd.get(theTables[tableIndex]);
	}

	public Object getField(FieldDescriptor fd) {
		// TODO abysmally slow
		int i = fromClause.getIndex(fd.getDeclaringClass());
		return fd.get(theTables[i]);
	}

	public ColumnSpec getColumnSpec() {
		return fromClause.getNaturalColumnSpec();
	}

	public boolean supports(ColumnSpec cs) {
		return (cs.getFromClause().subsetOf(fromClause));
	}

	public boolean equals(Object o) {
		// not sure if it's a good idea to claim I'm equal to a regular
		// object when I'm a singleton -- in fact, I know it's a bad
		// idea, since he wouldn't claim the same about me. Harrumph.
		if (o instanceof BasicRow) {
			BasicRow br = (BasicRow) o;
			if (!(br.fromClause.equals(this.fromClause))) {
				return false;
			}
			return Arrays.equals(br.theTables, theTables);
		}
		return false;
	}

	public int hashCode() {
		// need a.hashCode()==b.hashCode() when a.equals(b) for the
		// 'distinct' select code to work using a hashtable.
		int hc = 0;
		for (int i=0; i<theTables.length; i++) {
			hc = hc ^ theTables[i].hashCode();
		}
		return hc;
	}

	public FromClause getFromClause() {
		return fromClause;
	}

	public Relational getTable(Class c) {
		return getTable(fromClause, fromClause.getIndex(c));
	}

	public Relational getTable(String tableName) {
		return getTable(fromClause, fromClause.getIndex(tableName));
	}
}
