/**
 * 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.jsr223;

import java.io.File;
import java.io.IOException;
import java.io.Reader;

import javax.script.Bindings;
import javax.script.ScriptContext;
import javax.script.ScriptEngine;
import javax.script.ScriptEngineFactory;
import javax.script.ScriptException;
import javax.script.SimpleScriptContext;

import lucee.commons.io.IOUtil;
import lucee.runtime.PageContext;
import lucee.runtime.compiler.Renderer;
import lucee.runtime.compiler.Renderer.Result;
import lucee.runtime.engine.ThreadLocalPageContext;
import lucee.runtime.exp.PageException;
import lucee.runtime.type.KeyImpl;
import lucee.runtime.type.scope.Variables;
import lucee.runtime.util.PageContextUtil;


public class ScriptEngineImpl implements ScriptEngine {

	private ScriptEngineFactoryImpl factory;
	private ScriptContext context;
	private PageContext pc;
	
	public ScriptEngineImpl(ScriptEngineFactoryImpl factory) {
		this.factory=factory;
		pc = createPageContext();
		

	}

	@Override
	public Object eval(String script, ScriptContext context) throws ScriptException {
		if(context==null)context=getContext();
		
		PageContext oldPC = ThreadLocalPageContext.get();
		PageContext pc=getPageContext(context);
		try{
			Result res = factory.tag?Renderer.tag(pc, script,factory.dialect,false,true):Renderer.script(pc, script,factory.dialect,false,true);
			return res.getValue();
		}
		catch (PageException pe) {
			throw toScriptException(pe);
		}
		finally{
			releasePageContext(pc,oldPC);
		}
	}

	@Override
	public void put(String key, Object value) {
		PageContext oldPC = ThreadLocalPageContext.get();
		PageContext pc=getPageContext(getContext());
		try{
			pc.undefinedScope().set(KeyImpl.init(key), value);
		} 
		catch (PageException e) {
			// ignored
		}
		finally{
			releasePageContext(pc,oldPC);
		}
		
	}

	@Override
	public Object get(String key) {
		PageContext oldPC = ThreadLocalPageContext.get();
		PageContext pc=getPageContext(getContext());
		try{
			return pc.undefinedScope().get(KeyImpl.init(key),null);
		}
		finally{
			releasePageContext(pc,oldPC);
		}
	}

	@Override
	public Bindings getBindings(int scope) {
		return getContext().getBindings(scope);
	}

	@Override
	public void setBindings(Bindings bindings, int scope) {
		getContext().setBindings(bindings, scope);
	}
	

	
	private ScriptException toScriptException(Exception e) {
		ScriptException se = new ScriptException(e);
		se.setStackTrace(e.getStackTrace());
		return se;
	}
	

	@Override
	public ScriptContext getContext() {
		if(context==null) {
			context = new SimpleScriptContext();
			context.setBindings(new VariablesBinding(), ScriptContext.ENGINE_SCOPE); // we do our own 
		}
		return context;
	}
	
	private ScriptContext getContext(Bindings b) {
		ScriptContext def = getContext();
		SimpleScriptContext custom = new SimpleScriptContext();
		Bindings gs = getBindings(ScriptContext.GLOBAL_SCOPE);
		if (gs != null) custom.setBindings(gs, ScriptContext.GLOBAL_SCOPE);
		
		custom.setBindings(b,ScriptContext.ENGINE_SCOPE);
		custom.setReader(def.getReader());
		custom.setWriter(def.getWriter());
		custom.setErrorWriter(def.getErrorWriter());
		return custom;
	}
	
	@Override
	public void setContext(ScriptContext context) {
		this.context= context;
	}
	
	@Override
	public Bindings createBindings() {
		return new VariablesBinding();
	}

	private PageContext getPageContext(ScriptContext context) {
		pc.setVariablesScope(toVariables(context.getBindings(ScriptContext.ENGINE_SCOPE)));
		ThreadLocalPageContext.register(pc);
		return pc;
	}
	

	private void releasePageContext(PageContext pc, PageContext oldPC) {
		pc.flush();
		ThreadLocalPageContext.release();
		if(oldPC!=null)ThreadLocalPageContext.register(oldPC);
	}

	private Variables toVariables(Bindings bindings) {
		if(bindings instanceof VariablesBinding) return ((VariablesBinding) bindings).getVaraibles();
		RuntimeException t = new RuntimeException("not supported! "+bindings.getClass().getName());
		t.printStackTrace();
		throw t;
		//return new BindingsAsVariables(bindings);
	}
	
	private PageContext createPageContext() {
		try {
			File root = new File(factory.engine.getCFMLEngineFactory().getResourceRoot(),"jsr223-webroot");
			return PageContextUtil.getPageContext(root,"localhost", "/index.cfm", "", null, null, null, null, System.out, false,Long.MAX_VALUE,true);
		}
		catch (Exception e) {
			throw new RuntimeException(e);
		}
	}
	

///////////// calling other methods of the same class /////////////////
	
	@Override
	public Object eval(Reader reader, ScriptContext context) throws ScriptException {
		try {
			return eval(IOUtil.toString(reader),context);
		} catch (IOException ioe) {
			throw toScriptException(ioe);
		}
	}

	@Override
	public Object eval(String script) throws ScriptException {
		return eval(script,getContext());
	}

	@Override
	public Object eval(Reader reader) throws ScriptException {
		return eval(reader,getContext());
	}

	@Override
	public Object eval(String script, Bindings b) throws ScriptException { // TODO
		return eval(script,getContext(b));
	}

	@Override
	public Object eval(Reader reader, Bindings b) throws ScriptException {// TODO
		return eval(reader,getContext(b));
	}

	@Override
	public ScriptEngineFactory getFactory() {
		return factory;
	}
}
@@ -1112,16 +1112,52 @@
 IOUtil;%0A
+import lucee.commons.io.SystemUtil;%0A
 import l
@@ -1357,24 +1357,56 @@
 eException;%0A
+import lucee.runtime.op.Caster;%0A
 import lucee
@@ -5155,20 +5155,112 @@
 X_VALUE,
-true
+%0A%09%09%09%09%09Caster.toBooleanValue(SystemUtil.getSystemPropOrEnvVar(%22lucee.ignore.scopes%22, null),false)
 );%0A%09%09%7D%0A%09

add support for enviroment variable for ignoring scopes