/**
 * Copyright © 2012-2024 by Data Virtuality GmbH
 */
package org.teiid.query.sql.function;

import org.teiid.metadata.ExecuteAs;
import org.teiid.query.sql.LanguageVisitor;
import org.teiid.query.sql.StreamPositionAware;
import org.teiid.query.sql.lang.Command;
import org.teiid.query.sql.lang.Query;
import org.teiid.query.sql.symbol.ElementSymbol;
import org.teiid.query.sql.symbol.Expression;
import org.teiid.query.sql.symbol.GroupSymbol;
import org.teiid.query.sql.visitor.SQLStringVisitor;

import java.util.*;


/**
 * @author Mate Simovic
 * @date 2024.06.07
 */
public class CreateFunctionCommand extends Command {
	
    private List projectedSymbols;
    private List<? extends Expression> resultSetColumns;

    private GroupSymbol virtualGroup;
    
    private int updateType = Command.TYPE_UNKNOWN;

	private ElementSymbol returnVariable;

	private String owner;
	private StreamPositionAware ownerPos;

	private ExecuteAs executeAs = null;
	private StreamPositionAware executeAsPos;

	/**
	 * Constructor for CreateUpdateFunctionCommand.
	 */
	public CreateFunctionCommand() {
		super();
	}

	/**
	 * Return type of command to make it easier to build switch statements by command type.
	 * @return The type of this command
	 */
	public int getType() {
		return Command.
				TYPE_UPDATE_FUNCTION;	
	}

    // =========================================================================
    //                  P R O C E S S I N G     M E T H O D S
    // =========================================================================

    public void acceptVisitor(LanguageVisitor visitor) {
        visitor.visit(this);
    }

	/**
	 * Deep clone statement to produce a new identical statement.
	 * @return Deep clone 
	 */
	public Object clone() {		
		CreateFunctionCommand copy = new CreateFunctionCommand();

        //Clone this class state
        if (this.projectedSymbols != null) {
            copy.projectedSymbols = Util.deepClone(this.projectedSymbols, Expression.class);
        }
        if (this.resultSetColumns != null) {
            copy.resultSetColumns = Util.deepClone(this.resultSetColumns, Expression.class);
        }
        if (this.selIntoStatements != null) {
        	copy.selIntoStatements = this.selIntoStatements;
        }
        if (this.virtualGroup != null) {
        	copy.virtualGroup = this.virtualGroup.clone();
        }
		if (this.returnVariable != null) {
			copy.returnVariable = this.returnVariable;
		}
        copy.updateType = this.updateType;
        copy.anonymous = this.anonymous;
        this.copyMetadataState(copy);
		return copy;
	}

    /**
     * Compare two CreateUpdateFunctionCommand for equality.  They will only evaluate to equal if
     * they are IDENTICAL: the commandTypes are same.
     * @param obj Other object
     * @return True if equal
     */
    public boolean equals(Object obj) {
    	// Quick same object test
    	if(this == obj) {
    		return true;
		}

		// Quick fail tests		
    	if(! (obj instanceof CreateFunctionCommand)) {
    		return false;
		}
    	
    	CreateFunctionCommand other = (CreateFunctionCommand)obj;
        
        return sameOptionAndHint(other);
    }

	@Override
	public int hashCode() {
		return hashOptionAndHint();
	}

    /**
     * Returns a string representation of an instance of this class.
     * @return String representation of object
     */
    public String toString() {
    	return SQLStringVisitor.getSQLString(this);
    }

    // ========================================
    // 			Methods inherited from Command
    // ========================================

	/**
	 * Get the ordered list of all elements returned by this query.  These elements
	 * may be ElementSymbols or ExpressionSymbols but in all cases each represents a 
	 * single column.
	 * @return Ordered list of SingleElementSymbol
	 */
	public List getProjectedSymbols(){
        if(this.projectedSymbols != null){
            return this.projectedSymbols;
        }
        //user may have not entered any query yet
        return Collections.EMPTY_LIST;
	}  

	public List<? extends Expression> getResultSetColumns() {
 		return resultSetColumns;
 	}
	
	public void setResultSetColumns(List<? extends Expression> resultSetColumns) {
		this.resultSetColumns = resultSetColumns;
	}
	
	/**
     * @param projSymbols
     */
    public void setProjectedSymbols(List projSymbols) {
        projectedSymbols = projSymbols;
    }

	/**
	 * @see Command#areResultsCachable()
	 */
	public boolean areResultsCachable() {
		return Query.areColumnsCachable(getProjectedSymbols());
	}
    
    public GroupSymbol getVirtualGroup() {
        return this.virtualGroup;
    }

    public void setVirtualGroup(GroupSymbol virtualGroup) {
        this.virtualGroup = virtualGroup;
    }

	public void setUpdateType(int type) {
		//we select the count as the last operation
	 	this.resultSetColumns = Command.getUpdateCommandSymbol();
		this.updateType = type;
 	}

	public int getUpdateType() {
		return updateType;
	}

	public void setReturnVariable(ElementSymbol symbol) {
		this.returnVariable = symbol;
	}

	public ElementSymbol getReturnVariable() {
		return returnVariable;
	}
    
	@Override
	public boolean returnsResultSet() {
		return this.resultSetColumns != null && !this.resultSetColumns.isEmpty();
	}	
	
	private boolean anonymous = true;
	
	public boolean isAnonymous() {
		return anonymous;
	}
	
	public void setAnonymous(boolean anonymous) {
		this.anonymous = anonymous;
	}
	
	private Set<Object> selIntoStatements = new HashSet<Object>();
	
	public void addSelIntoStatement(Object selIntoStatement) {
		selIntoStatements.add(selIntoStatement);
	}
	
	public Set<Object> getSelIntoStatements() {
		return selIntoStatements;
	}

	public String getOwner() {
		return owner;
	}

	public void setOwner(String owner) {
		this.owner = owner;
	}

	public StreamPositionAware getOwnerPos() {
		return ownerPos;
	}

	public void setOwnerPos(StreamPositionAware ownerPos) {
		this.ownerPos = ownerPos;
	}

	public ExecuteAs getExecuteAs() {
		return executeAs;
	}

	public void setExecuteAs(ExecuteAs executeAs) {
		this.executeAs = executeAs;
	}

	public StreamPositionAware getExecuteAsPos() {
		return executeAsPos;
	}

	public void setExecuteAsPos(StreamPositionAware executeAsPos) {
		this.executeAsPos = executeAsPos;
	}
} // END CLASS
