package railo.runtime.sql;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

import railo.runtime.exp.DatabaseException;
import railo.runtime.sql.exp.Column;
import railo.runtime.sql.exp.Expression;
import railo.runtime.sql.exp.op.Operation;
import railo.runtime.sql.exp.value.ValueNumber;

public class Selects {

	private List<Column> orderbys=new ArrayList<Column>();
	private List<Select> selects=new ArrayList<Select>();
	


	public void addOrderByExpression(Column exp) {
		this.orderbys.add(exp);
	}

	/**
	 * @return the orderbys
	 */
	public Column[] getOrderbys() {
		if(orderbys==null) return new Column[0];
		return orderbys.toArray(new Column[orderbys.size()]);
	}

	public void addSelect(Select select) {
		selects.add(select);
	}
	
	public Select[] getSelects() {
		if(selects==null) return new Select[0];
		return selects.toArray(new Select[selects.size()]);
	}
	

	@Override

	public String toString() {
		return _toString(this);
	}
	public static String _toString(Selects __selects) {
		Select[] _selects = __selects.getSelects();
		Select s;
		StringBuffer sb=new StringBuffer();
		
		for(int y=0;y<_selects.length;y++) {
			s = _selects[y];
			
			if(y>0) {
				if(s.isUnionDistinct())sb.append("union distinct\n\n");
				else sb.append("union\n\n");
			}
			
			sb.append("select\n\t");

			if(s.isDistinct()) sb.append("distinct\n\t");
			ValueNumber top = s.getTop();
			if(top!=null) sb.append("top "+top.getString()+"\n\t");
			// select
			Expression[] sels = s.getSelects();
			Expression exp;
			boolean first=true;
			for(int i=0;i<sels.length;i++) {
				if(!first)sb.append("\t,");
				exp=sels[i];
				sb.append(exp.toString(false)+"\n");
				first=false;
			}
	
			// from
			sb.append("from\n\t");
			Column[] forms = s.getFroms();
			first=true;
			for(int i=0;i<forms.length;i++) {
				if(!first)sb.append("\t,");
				exp=forms[i];
				sb.append(exp.toString(false)+"\n");
				first=false;
			}
	
			// where
			if(s.getWhere()!=null){
				sb.append("where \n\t");
				sb.append(s.getWhere().toString(true));
				sb.append("\n");
			}
	
			// group by
			Column[] gbs = s.getGroupbys();
			if(gbs.length>0) {
				sb.append("group by\n\t");
				first=true;
				for(int i=0;i<gbs.length;i++) {
					if(!first)sb.append("\t,");
					exp=gbs[i];
					sb.append(exp.toString(false)+"\n");
					first=false;
				}
			}
	
			
			// having
			Operation having = s.getHaving();
			if(having!=null){
				sb.append("having \n\t");
				sb.append(having.toString(true));
				sb.append("\n");
			}
			
		}

		// order by
		if(__selects.orderbys!=null && __selects.orderbys.size()>0) {
			sb.append("order by\n\t");
			Iterator<Column> it = __selects.orderbys.iterator();
			Expression exp;
			boolean first = true;
			while(it.hasNext()) {
				if(!first)sb.append("\t,");
				exp=it.next();
				sb.append(exp.toString(false)+" "+(exp.isDirectionBackward()?"DESC":"ASC")+"\n");
				first=false;
			}
		}
		return sb.toString();
	}

	public Column[] getTables() {
		Iterator<Select> it = selects.iterator();
		Select s;
		ArrayList<Column> rtn=new ArrayList<Column>();
		Column[] froms;
		while(it.hasNext()) {
			s=it.next();
			froms = s.getFroms();
			for(int i=0;i<froms.length;i++) {
				rtn.add(froms[i]);
			}
		}
		return rtn.toArray(new Column[rtn.size()]);
	}

	public boolean isDistinct() {
		Select s;
		int len=selects.size();
		if(len==1) {
			s=selects.get(0);
			return s.isDistinct();
		}
		for(int i=1;i<len;i++) {
			s=selects.get(i);
			if(!s.isUnionDistinct()) return false;
		}
		return true;
	}
	public Column[] getDistincts() throws DatabaseException {
		List<Column> columns=new ArrayList<Column>();
		Select s;
		int len=selects.size();
		if(len==1) {
			s=selects.get(0);
			Expression[] _selects = s.getSelects();
			for(int i=0;i<_selects.length;i++){
				if(_selects[i] instanceof Column) {
					columns.add((Column) _selects[i]);
				}	
			}
			return columns.toArray(new Column[columns.size()]);
		}
		throw new DatabaseException("not supported for Union distinct",null,null,null);
	}
}
