/**
 * Copyright (c) 2015, Lucee Assosication Switzerland. All rights reserved.
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either 
 * version 2.1 of the License, or (at your option) any later version.
 * 
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 * 
 * You should have received a copy of the GNU Lesser General Public 
 * License along with this library.  If not, see <http://www.gnu.org/licenses/>.
 * 
 */
package lucee.runtime.compiler;

import java.io.IOException;
import java.lang.reflect.Constructor;
import java.util.HashMap;
import java.util.Map;

import javax.servlet.jsp.tagext.BodyContent;

import lucee.commons.digest.HashUtil;
import lucee.commons.lang.ClassUtil;
import lucee.commons.lang.ExceptionUtil;
import lucee.commons.lang.MemoryClassLoader;
import lucee.loader.engine.CFMLEngine;
import lucee.runtime.Page;
import lucee.runtime.PageContext;
import lucee.runtime.PageSource;
import lucee.runtime.config.ConfigImpl;
import lucee.runtime.config.ConfigWeb;
import lucee.runtime.config.ConfigWebImpl;
import lucee.runtime.config.Constants;
import lucee.runtime.exp.PageException;
import lucee.runtime.op.Caster;
import lucee.transformer.util.SourceCode;

public class Renderer {
	
	private static final long MAX_SIZE = 1024*1024;
	private static MemoryClassLoader mcl;
	private static Map<String,Page> pages=new HashMap<String,Page>();
	
	private static Class<? extends Page> loadClass(ConfigWeb config, String className, String cfml, int dialect, boolean ignoreScopes) throws Exception{
		
		ConfigWebImpl cw = (ConfigWebImpl)config;
		CFMLCompilerImpl compiler = cw.getCompiler();
		// create className based o the content
		
		Class<? extends Page> clazz=null;
		if(mcl==null) {
			mcl=createMemoryClassLoader(cw);
		}
		else clazz=ClassUtil.loadClass(mcl, className, null);
		
		if(clazz!=null) return clazz;
		
		SourceCode sc=new SourceCode(cfml, false,dialect);
		
		// compile
		lucee.runtime.compiler.CFMLCompilerImpl.Result result = 
				compiler.compile(cw, sc, cw.getTLDs(dialect), cw.getFLDs(dialect), null, className,true,ignoreScopes);
		
		// before we add a new class, we make sure we are still in range
		if(mcl.getSize()+result.barr.length>MAX_SIZE) {
			mcl=createMemoryClassLoader(cw);
			pages.clear();
		}
				
				
		
		return (Class<? extends Page>) mcl.loadClass(className, result.barr);
	}
	
	private static MemoryClassLoader createMemoryClassLoader(ConfigWebImpl cw) throws IOException {
		return new MemoryClassLoader(cw, cw.getClass().getClassLoader());
	}

	private static Page loadPage(ConfigWeb cw,PageSource ps, String cfml, int dialect, boolean ignoreScopes) throws Exception{
		String className=HashUtil.create64BitHashAsString(cfml);
		
		// do we already have the page?
		Page p=pages.get(className);
		if(p!=null) return p;
		
		// load class
		Constructor<? extends Page> constr = loadClass(cw, className,cfml,dialect,ignoreScopes).getDeclaredConstructor(PageSource.class);
		p= constr.newInstance(ps);
		pages.put(className, p);
		return p;
	}
	
	public static Result script(PageContext pc, String cfml, int dialect, boolean catchOutput, boolean ignoreScopes) throws PageException {
		String prefix = ((ConfigImpl)pc.getConfig()).getCoreTagLib(dialect).getNameSpaceAndSeparator();
		String name= prefix+(dialect==CFMLEngine.DIALECT_CFML?Constants.CFML_SCRIPT_TAG_NAME:Constants.LUCEE_SCRIPT_TAG_NAME);
		return tag(pc, "<"+name+">"+cfml+"</"+name+">",dialect,catchOutput,ignoreScopes);
	}
	
	public static Result tag(PageContext pc, String cfml, int dialect, boolean catchOutput, boolean ignoreScopes) throws PageException {
		// execute
		Result res=new Result();
		BodyContent bc=null;
		//Variables before = pc.variablesScope();
		try{
			if(catchOutput)bc = pc.pushBody();
			//if(variables!=null)pc.setVariablesScope(variables);
			
			res.value=loadPage(pc.getConfig(), null, cfml,dialect,ignoreScopes).call(pc);
		}
		catch(Throwable t){
			ExceptionUtil.rethrowIfNecessary(t);
			throw Caster.toPageException(t);
		}
		finally{
			//if(variables!=null)pc.setVariablesScope(before);
			if(catchOutput) {
				if(bc!=null)res.output=bc.getString();
				pc.popBody();
			}
			//pc.flush();
		}
		return res;
	}
	
	public static class Result {

		private String output;
		private Object value;
		
		public String getOutput() {
			return output==null?"":output;
		}
		public Object getValue() {
			return value;
		}
		
		public String toString(){
			return "output:"+output+";value:"+value;
		}
	}
}
@@ -4001,149 +4001,48 @@
 ;%0A%09%09
-//Variables before = pc.variablesScope();%0A%09%09try%7B%0A%09%09%09if(catchOutput)bc = pc.pushBody();%0A%09%09%09//if(variables!=null)pc.setVariablesScope(variables
+try%7B%0A%09%09%09if(catchOutput)bc = pc.pushBody(
 );%0A%09
@@ -4246,62 +4246,8 @@
 ly%7B%0A
-%09%09%09//if(variables!=null)pc.setVariablesScope(before);%0A
 %09%09%09i
@@ -4333,25 +4333,8 @@
 %09%09%7D%0A
-%09%09%09//pc.flush();%0A
 %09%09%7D%0A

clean up