package eu.dnetlib.scripting;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.InputStream;
import java.io.InputStreamReader;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;

import clojure.lang.Namespace;
import clojure.lang.RT;
import clojure.lang.Symbol;
import clojure.lang.Var;

/**
 * Simple clojure scripting engine.
 * 
 * <p>It (re)loads a library.clj from the classpath, and a /var/lib/dnet/scripts/site.clj from the filesystem, every time
 * a statement is executed</p>
 * 
 * @author marko
 *
 */
public class ClojureEngine implements ScriptingLanguage, ApplicationContextAware {
	private static final Log log = LogFactory.getLog(ClojureEngine.class); // NOPMD by marko on 11/24/08 5:02 PM

	private ApplicationContext applicationContext;

	public String execute(final String code) {
		log.debug("executing clojure: " + code);

		final Var eval = RT.var("clojure.core", "eval");

		Var.intern(Namespace.findOrCreate(Symbol.create("clojure.core")), Symbol.create("*spring-context*"), applicationContext);

		try {
			loadScript(getClass().getResourceAsStream("library.clj"));
			loadScript(new File("/var/lib/dnet/scripts/site.clj"));

			final Object parsed = RT.readString(code);

			final Object res = eval.invoke(parsed);
			if (res == null)
				return "nil";
			return res.toString();
		} catch (final Exception e) {
			log.warn("got exception while executing user script", e);
			return e.getMessage();
		}
	}

	public void reload() {
		execute("1");
	}
	
	private void loadScript(final File file) throws FileNotFoundException, Exception {
		if (file.exists())
			loadScript(new FileInputStream(file));
	}

	private void loadScript(final InputStream stream) throws Exception {
		if (stream != null)
			clojure.lang.Compiler.load(new InputStreamReader(stream));
	}

	public ApplicationContext getApplicationContext() {
		return applicationContext;
	}

	public void setApplicationContext(final ApplicationContext applicationContext) {
		this.applicationContext = applicationContext;
	}

}
