package railo.transformer.cfml.evaluator.impl;


import java.util.Iterator;
import java.util.List;
import java.util.Map;

import railo.commons.lang.StringUtil;
import railo.runtime.functions.system.CFFunction;
import railo.transformer.bytecode.Body;
import railo.transformer.bytecode.BytecodeException;
import railo.transformer.bytecode.Literal;
import railo.transformer.bytecode.Statement;
import railo.transformer.bytecode.cast.CastBoolean;
import railo.transformer.bytecode.cast.CastString;
import railo.transformer.bytecode.expression.ExprString;
import railo.transformer.bytecode.expression.Expression;
import railo.transformer.bytecode.literal.LitBoolean;
import railo.transformer.bytecode.literal.LitLong;
import railo.transformer.bytecode.literal.LitString;
import railo.transformer.bytecode.statement.PrintOut;
import railo.transformer.bytecode.statement.tag.Attribute;
import railo.transformer.bytecode.statement.tag.Tag;
import railo.transformer.bytecode.util.ASMUtil;
import railo.transformer.cfml.evaluator.EvaluatorException;
import railo.transformer.cfml.evaluator.EvaluatorSupport;
import railo.transformer.library.function.FunctionLib;
import railo.transformer.library.function.FunctionLibFunction;
import railo.transformer.library.tag.TagLibTag;

/**
 * Prft den Kontext des Tag function.
 * Das Attribute <code>argument</code> darf nur direkt innerhalb des Tag <code>function</code> liegen.
 * Dem Tag <code>argument</code> muss als erstes im tag function vorkommen
 */
public final class Function extends EvaluatorSupport {

	//
	/**
	 * @see railo.transformer.cfml.evaluator.EvaluatorSupport#evaluate(org.w3c.dom.Element, railo.transformer.library.tag.TagLibTag)
	 */
	public void evaluate(Tag tag, TagLibTag libTag, FunctionLib[] flibs) throws EvaluatorException {
		//Body p=(Body) tag.getParent();
		//Statement pp = p.getParent();
		
		boolean isCFC=true;
        try {
			isCFC = ASMUtil.getAncestorPage(tag).isComponent();
		} catch (BytecodeException e) {}

		Attribute attrName = tag.getAttribute("name");
		if(attrName!=null) {
			Expression expr = attrName.getValue();
			if(expr instanceof LitString && !isCFC){
				checkFunctionName(((LitString)expr).getString(),flibs);
			}
				
		}
		// attribute modifier
		Attribute attrModifier = tag.getAttribute("modifier");
		if(attrModifier!=null) {
			ExprString expr = CastString.toExprString(attrModifier.getValue());
			if(!(expr instanceof Literal))
				throw new EvaluatorException("Attribute modifier of the Tag Function, must be one of the following literal string values: [abstract] or [final]");
			String modifier=StringUtil.emptyIfNull(((Literal)expr).getString()).trim();
			if(!StringUtil.isEmpty(modifier) && !"abstract".equalsIgnoreCase(modifier) && !"final".equalsIgnoreCase(modifier))
				throw new EvaluatorException("Attribute modifier of the Tag Function, must be one of the following literal string values: [abstract] or [final]");
			
			
			boolean abstr = "abstract".equalsIgnoreCase(modifier);
			if(abstr)throwIfNotEmpty(tag);
		}
		
		// cachedWithin
		Attribute attrCachedWithin = tag.getAttribute("cachedwithin");
		if(attrCachedWithin!=null) {
			Expression val = attrCachedWithin.getValue();
			tag.addAttribute(new Attribute(attrCachedWithin.isDynamicType(), attrCachedWithin.getName(), LitLong.toExpr(ASMUtil.timeSpanToLong(val), null, null), "numeric"));
		}
		
		
		// Attribute Output
		// "output=true" wird in "railo.transformer.cfml.attributes.impl.Function" gehndelt
		Attribute attrOutput = tag.getAttribute("output");
		if(attrOutput!=null) {
			Expression expr = CastBoolean.toExprBoolean(attrOutput.getValue());
			if(!(expr instanceof LitBoolean))
				throw new EvaluatorException("Attribute output of the Tag Function, must be a literal boolean value (true or false, yes or no)");
			//boolean output = ((LitBoolean)expr).getBooleanValue();
			//if(!output) ASMUtil.removeLiterlChildren(tag, true);
		}
		
		
        //if(ASMUtil.isRoot(pp)) {
        	Map attrs = tag.getAttributes();
        	Iterator it = attrs.keySet().iterator();
        	Attribute attr;
        	while(it.hasNext()) {
        		attr=(Attribute) attrs.get(it.next());
        		checkAttributeValue(tag,attr);
        	}
        //}
        
	}


	public static void checkFunctionName(String name, FunctionLib[] flibs) throws EvaluatorException {
		FunctionLibFunction flf;
		for (int i = 0; i < flibs.length; i++) {
			flf = flibs[i].getFunction(name);
			if(flf!=null && flf.getCazz()!=CFFunction.class) {
				throw new EvaluatorException("The name ["+name+"] is already used by a built in Function");
			}
		}
	}


	public static void throwIfNotEmpty(Tag tag) throws EvaluatorException {
		Body body = tag.getBody();
		List statments = body.getStatements();
		Statement stat;
		Iterator it = statments.iterator();
		TagLibTag tlt;
		
		while(it.hasNext()) {
			stat=(Statement) it.next();
			if(stat instanceof PrintOut) {
				//body.remove(stat);
			}
			else if(stat instanceof Tag) {
				tlt = ((Tag)stat).getTagLibTag();
				if(!tlt.getTagClassName().equals("railo.runtime.tag.Argument"))
					throw new EvaluatorException("tag "+tlt.getFullName()+" is not allowed inside a function declaration");
			}
		}
	}

	private void checkAttributeValue(Tag tag, Attribute attr) throws EvaluatorException {
		if(!(attr.getValue() instanceof Literal))
			throw new EvaluatorException("Attribute ["+attr.getName()+"] of the Tag ["+tag.getFullname()+"] must be a literal/constant value");
        
    }
}
@@ -4714,16 +4714,27 @@
 ;%0A%09%09List
+%3CStatement%3E
  statmen
@@ -4784,24 +4784,35 @@
 ;%0A%09%09Iterator
+%3CStatement%3E
  it = statme
@@ -4883,20 +4883,8 @@
 tat=
-(Statement) 
 it.n
@@ -4916,77 +4916,8 @@
 eof 
-PrintOut) %7B%0A%09%09%09%09//body.remove(stat);%0A%09%09%09%7D%0A%09%09%09else if(stat instanceof 
 Tag)
@@ -5131,32 +5131,105 @@
 aration%22);%0A%09%09%09%7D%0A
+%09%09%09/*else if(stat instanceof PrintOut) %7B%0A%09%09%09%09//body.remove(stat);%0A%09%09%09%7D*/%0A
 %09%09%7D%0A%09%7D%0A%0A%09private

add generic types and remove unnecessary statement