/*
 * Copyright 2013-2014 Yuichiro Moriguchi
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package net.morilib.nina.interpret;

import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PrintStream;
import java.io.PrintWriter;
import java.io.Reader;
import java.io.StringReader;
import java.io.StringWriter;
import java.util.Collections;
import java.util.HashMap;

import javax.script.Bindings;
import javax.script.Compilable;
import javax.script.CompiledScript;
import javax.script.ScriptEngine;
import javax.script.ScriptEngineManager;
import javax.script.ScriptException;
import javax.script.SimpleBindings;

import net.morilib.nina.NinaConsole;
import net.morilib.nina.cmd.NinaInfo;
import net.morilib.nina.cmd.NinatMessages;
import net.morilib.nina.translate.NinaTranslator;
import net.morilib.nina.translate.NinaTranslatorJavaScript;
import net.morilib.nina.translate.sh.ShNinaRealFile;
import net.morilib.sh.ShFile;
import net.morilib.sh.ShFileSystem;

public class NinaScript {

	private final NinaInfo info = new NinaInfo() {

		public void print(String p, Object... a) {
			out.println(NinatMessages.getMessage(p, a));
		}

		public void perror(String p, Object... a) {
			out.print(NinatMessages.getMessage("errorheader"));
			out.println(NinatMessages.getMessage(p, a));
		}

		public void pwarn(String p, Object... a) {
			out.print(NinatMessages.getMessage("warnheader"));
			out.println(NinatMessages.getMessage(p, a));
		}

		public void pmessage(String p) {
			out.print(NinatMessages.getMessage("errorheader"));
			out.println(p);
		}

		@Override
		public String getOutputFilename() {
			return "script";
		}

		@Override
		public String getFilename() {
			return "script";
		}

		@Override
		public String getClassName() {
			return "script";
		}

		@Override
		public String getBufSize() {
			return "1024";
		}

		@Override
		public String getTitleFile(String s) {
			return null;
		}

		@Override
		public Object getOutputDir() {
			return new File(".");
		}

		@Override
		public NinaConsole getConsole() {
			return this;
		}

		@Override
		public OutputStream getOutputStream(Object path,
				String file) throws IOException {
			return new FileOutputStream(new File((File)path, file));
		}

		@Override
		public OutputStream getOutputStream(
				Object file) throws IOException {
			return new FileOutputStream((File)file);
		}

		@Override
		public void postprocess() {
		}

		@Override
		public ShFile getShOutputDir(ShFileSystem fs) {
			return new ShNinaRealFile(fs, "/output",
					(File)getOutputDir());
		}

		@Override
		public ShFile getShOutputFile(ShFileSystem fs, String p,
				String f) {
			return new ShNinaRealFile(fs, p,
					new File((File)getOutputDir(), f));
		}

		@Override
		public Object getInjectFile() {
			return null;
		}

		@Override
		public InputStream getInputStream(Object path,
				String file) throws IOException {
			return new FileInputStream(new File((File)path, file));
		}

		@Override
		public InputStream getInputStream(
				Object file) throws IOException {
			return new FileInputStream((File)file);
		}

	};

	//
	private PrintWriter out = new PrintWriter(new StringWriter());
	private CompiledScript js;
	private Bindings bind = new SimpleBindings();

	private NinaScript() {}

	/**
	 * 
	 * @param rd
	 * @return
	 * @throws IOException
	 * @throws ScriptException
	 */
	public static NinaScript newInstance(
			Reader rd) throws IOException, ScriptException {
		NinaScript sc = new NinaScript();
		ByteArrayOutputStream ot;
		ScriptEngineManager mn;
		ScriptEngine en;
		NinaTranslator nt;
		PrintStream pt;

		nt = new NinaTranslatorJavaScript();
		ot = new ByteArrayOutputStream();
		pt = new PrintStream(ot, true);
		nt.translate("script", rd, sc.info,
				Collections.<String>emptyList(),
				new HashMap<String, String>(), pt);
		mn = new ScriptEngineManager();
		en = mn.getEngineByName("javascript");
		sc.js = ((Compilable)en).compile(ot.toString());
		return sc;
	}

	/**
	 * 
	 * @param s
	 * @return
	 * @throws ScriptException
	 */
	public static NinaScript newInstance(
			String s) throws ScriptException {
		try {
			return newInstance(new StringReader(s));
		} catch(IOException e) {
			throw new RuntimeException(e);
		}
	}

	/**
	 * 
	 * @param rd
	 * @return
	 * @throws ScriptException
	 * @throws IOException
	 */
	public static Object eval(
			Reader rd) throws ScriptException, IOException {
		return newInstance(rd).eval();
	}

	/**
	 * 
	 * @param s
	 * @return
	 * @throws ScriptException
	 */
	public static Object eval(String s) throws ScriptException {
		return newInstance(s).eval();
	}

	/**
	 * 
	 * @param name
	 * @param o
	 */
	public void bind(String name, Object o) {
		bind.put(name, o);
	}

	/**
	 * 
	 * @return
	 * @throws ScriptException
	 */
	public Object eval() throws ScriptException {
		return js.eval(bind);
	}

}
