package org.teiid.query.sql.lang;

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

import net.sf.saxon.sxpath.XPathExpression;

import org.teiid.core.TeiidProcessingException;
import org.teiid.core.types.DataTypeManager;
import org.teiid.core.util.EquivalenceUtil;
import org.teiid.query.sql.LanguageVisitor;
import org.teiid.query.sql.symbol.DerivedColumn;
import org.teiid.query.sql.symbol.Expression;
import org.teiid.query.sql.symbol.XMLNamespaces;
import org.teiid.query.xquery.saxon.SaxonXQueryExpression;

public class XMLTable extends TableFunctionReference {
	
	public static class XMLColumn extends ProjectedColumn {
		private boolean ordinal;
		private String path;
		private Expression defaultExpression;
		
		private XPathExpression pathExpression;
		
		public XMLColumn(String name) {
			super(name, DataTypeManager.DefaultDataTypes.INTEGER);
			this.ordinal = true;
		}
		
		public XMLColumn(String name, String type, String path, Expression defaultExpression) {
			super(name, type);
			this.path = path;
			this.defaultExpression = defaultExpression;
		}
		
		protected XMLColumn() {
			
		}
		
		public Expression getDefaultExpression() {
			return defaultExpression;
		}
		
		public void setDefaultExpression(Expression defaultExpression) {
			this.defaultExpression = defaultExpression;
		}
		
		public String getPath() {
			return path;
		}
		
		public void setPath(String path) {
			this.path = path;
		}
		
		public boolean isOrdinal() {
			return ordinal;
		}
		
		public void setOrdinal(boolean ordinal) {
			this.ordinal = ordinal;
		}
		
		public void setPathExpression(XPathExpression pathExpression) {
			this.pathExpression = pathExpression;
		}
		
		public XPathExpression getPathExpression() {
			return pathExpression;
		}
		
		@Override
		public boolean equals(Object obj) {
			if (obj == this) {
				return true;
			}
			if (!super.equals(obj) || !(obj instanceof XMLColumn)) {
				return false;
			}
			XMLColumn other = (XMLColumn)obj;
			return this.ordinal == other.ordinal 
				&& EquivalenceUtil.areEqual(this.path, other.path)
				&& EquivalenceUtil.areEqual(this.defaultExpression, other.defaultExpression);
		}
		
		@Override
		public XMLColumn clone() {
			XMLColumn clone = new XMLColumn();
			super.copyTo(clone);
			clone.ordinal = this.ordinal;
			clone.path = this.path;
			if (this.defaultExpression != null) {
				clone.defaultExpression = (Expression)this.defaultExpression.clone();
			}
			clone.pathExpression = this.pathExpression;
			return clone;
		}
	}
	
    private List<XMLColumn> columns = new ArrayList<XMLColumn>();
    private XMLNamespaces namespaces;
    private String xquery;
    private List<DerivedColumn> passing = new ArrayList<DerivedColumn>();
    private boolean usingDefaultColumn;
    
    private SaxonXQueryExpression xqueryExpression;
    
    public List<DerivedColumn> getPassing() {
		return passing;
	}
    
    public void compileXqueryExpression() throws TeiidProcessingException {
    	this.xqueryExpression = new SaxonXQueryExpression(xquery, namespaces, passing, this.columns);
    }
    
    public SaxonXQueryExpression getXQueryExpression() {
		return xqueryExpression;
	}
    
    public void setPassing(List<DerivedColumn> passing) {
		this.passing = passing;
	}
    
    public String getXquery() {
		return xquery;
	}
    
    public void setXquery(String xquery) {
		this.xquery = xquery;
	}
    
    public List<XMLColumn> getColumns() {
		return columns;
	}
    
    public void setColumns(List<XMLColumn> columns) {
    	if (columns.isEmpty()) {
    		usingDefaultColumn = true;
        	columns.add(new XMLColumn("OBJECT_VALUE", DataTypeManager.DefaultDataTypes.XML, ".", null)); //$NON-NLS-1$ //$NON-NLS-2$
    	}
		this.columns = columns;
	}
    
    public boolean isUsingDefaultColumn() {
		return usingDefaultColumn;
	}
    
    public XMLNamespaces getNamespaces() {
		return namespaces;
	}
    
    public void setNamespaces(XMLNamespaces namespaces) {
		this.namespaces = namespaces;
	}
    
	@Override
	public void acceptVisitor(LanguageVisitor visitor) {
		visitor.visit(this);
	}

	@Override
	protected XMLTable cloneDirect() {
		XMLTable clone = new XMLTable();
		this.copy(clone);
		for (XMLColumn column : columns) {
			clone.getColumns().add(column.clone());
		}
		if (this.namespaces != null) {
			clone.namespaces = this.namespaces.clone();
		}
		if (this.passing != null) {
			for (DerivedColumn col : this.passing) {
				clone.passing.add(col.clone());
			}
		}
		clone.xquery = this.xquery;
		if (this.xqueryExpression != null) {
			clone.xqueryExpression = this.xqueryExpression.clone();
		}
		clone.usingDefaultColumn = usingDefaultColumn;
		return clone;
	}

	@Override
	public boolean equals(Object obj) {
		if (obj == this) {
			return true;
		}
		if (!super.equals(obj) || !(obj instanceof XMLTable)) {
			return false;
		}
		XMLTable other = (XMLTable)obj;
		return this.columns.equals(other.columns) 
			&& EquivalenceUtil.areEqual(this.namespaces, other.namespaces)
			&& this.xquery.equals(other.xquery)
			&& this.passing.equals(other.passing);
	}

}
