package relational;

import java.io.Serializable;
import java.util.Set;
import java.rmi.RemoteException;
import Tools.Text;

/**
 * A select statement.
 */
public class Select
	implements Serializable {

	// construct
	public Select(Class wholeTable, Where whereClause) {
		FromClause fc = FromClause.createAnonymous(wholeTable);
		init(fc.getNaturalColumnSpec(), fc, whereClause, false);
	}

	public Select(FieldDescriptor oneField, Where whereClause) {
		Class table = oneField.getDeclaringClass();
		FromClause fc = FromClause.createAnonymous(table);
		ColumnSpec cs = ColumnSpec.create(fc,
				new int[] { 0 },	// first (only) table in fromClause
				new FieldDescriptor[] { oneField });	// desired field
		init(cs, fc, whereClause, false);
	}

	public Select(ColumnSpec type, Where whereClause) {
		init(type, type.getFromClause(), whereClause, false);
	}

	public Select(ColumnSpec columnSpec, FromClause fromClause,
		Where whereClause) {
		init(columnSpec, fromClause, whereClause, false);
	}

	public Select(ColumnSpec columnSpec, FromClause fromClause,
		Where whereClause, boolean distinct) {
		init(columnSpec, fromClause, whereClause, distinct);
	}

	protected void init(ColumnSpec columnSpec, FromClause fromClause,
		Where whereClause, boolean distinct) {
		this.columnSpec = columnSpec;
		this.fromClause = fromClause;
		this.whereClause = whereClause;
		this.distinct = distinct;
	}

	public Select(Select s) {
		// used to get a fresh, unevaluated query.
		this.columnSpec = s.columnSpec;
		this.fromClause = s.fromClause;
		this.whereClause = s.whereClause;
	}

	// tweak
	public void setDistinct(boolean distinct) {
		this.distinct = distinct;
	}

	public void setOrderBy(OrderBy ob) {
		this.ob = ob;
	}

	// query
	public ColumnSpec getColumnSpec() { return columnSpec; }

	public Where getWhere() { return whereClause; }

	public FromClause getFromClause() { return fromClause; }

	// force evaluation now; cache results for future evaluate()s.
	public ResultSet evaluate(Database db) {
		if (rs == null) {
			try {
				rs = db.evaluateSelect(this);
			} catch (RemoteException rex) {
				rex.printStackTrace();
				System.err.println("Returning null");
			}
		}
		return rs;
	}

	// called by Database to sort ResultSet before returning it to caller.
	public ResultSet order(ResultSet rs) {
		if (ob != null) {
			rs = ob.order(rs);
		}
		return rs;
	}

	public String toString() {
		return "Select "
			+(distinct ? "DISTINCT " : "")
			+"\n  "+columnSpec
			+"\n  from "+fromClause
			+"\n  where\n"
			+Text.indent(4, whereClause.indentedString(2));
	}

	// member variables
	protected FromClause fromClause;	// shape of (virtually) joined table
	protected Where whereClause;		// conditions on joined table
	protected ColumnSpec columnSpec;	// final shape of output
	protected boolean distinct;			// resulting rows appear once apiece
	protected OrderBy ob;				// how you'd like the set sorted
	protected ResultSet rs;				// final output
}
