/* JRubyInterpreter.java

	Purpose:
		
	Description:
		
	History:
		Sat Feb 10 19:56:11     2007, Created by tomyeh

Copyright (C) 2007 Potix Corporation. All Rights Reserved.

{{IS_RIGHT
	This program is distributed under LGPL Version 2.1 in the hope that
	it will be useful, but WITHOUT ANY WARRANTY.
}}IS_RIGHT
*/
package org.zkoss.zk.scripting.jruby;

import java.util.Iterator;

import org.jruby.Ruby;
import org.jruby.internal.runtime.GlobalVariables;
import org.jruby.javasupport.JavaEmbedUtils;
import org.jruby.javasupport.JavaObject;
import org.jruby.javasupport.JavaUtil;
import org.jruby.runtime.GlobalVariable;
import org.jruby.runtime.IAccessor;
import org.jruby.runtime.builtin.IRubyObject;

import org.zkoss.zk.scripting.util.GenericInterpreter;
import org.zkoss.zk.ui.Page;

/**
 * JRuby interpreter.
 *
 * @author tomyeh
 */
public class JRubyInterpreter extends GenericInterpreter {
	private Ruby _runtime;

	/** Returns the native interpreter, or null if it is not initialized
	 * or destroyed.
	 * From application's standpoint, it never returns null, and the returned
	 * object must be an instance of {@link org.jruby.Ruby}
	 * @since 3.0.2
	 */
	public Object getNativeInterpreter() {
		return _runtime;
	}

	//GenericInterpreter//
	protected void exec(String script) {
		_runtime.evalScriptlet(script);
	}

	protected boolean contains(String name) {
		return _runtime.getGlobalVariables().isDefined(GlobalVariable.variableName(name));
	}

	protected Object get(String name) {
		IRubyObject ro = _runtime.getGlobalVariables().get(GlobalVariable.variableName(name));
		return rubyToJava(ro);
	}

	protected void set(String name, Object value) {
		_runtime.getGlobalVariables().define(GlobalVariable.variableName(name), new Variable(value), org.jruby.internal.runtime.GlobalVariable.Scope.GLOBAL);
	}

	protected void unset(String name) {
		_runtime.getGlobalVariables().set(GlobalVariable.variableName(name), _runtime.getNil());
	}

	//Interpreter//
	public void init(Page owner, String zslang) {
		super.init(owner, zslang);

		_runtime = Ruby.newInstance();
		_runtime.setGlobalVariables(new Variables(_runtime));
	}

	public void destroy() {
		JavaEmbedUtils.terminate(_runtime);
		_runtime = null;

		super.destroy();
	}

	/**TODO: need to dig out a solution from jruby's manual
	public Class getClass(String clsnm) {
	}
	public Function getFunction(String name, Class[] argTypes) {
	}
	*/

	//utilities//
	private IRubyObject javaToRuby(Object value) {
		IRubyObject ro = JavaUtil.convertJavaToRuby(_runtime, value);
		if (ro instanceof JavaObject)
			return _runtime.getModule("JavaUtilities").callMethod(_runtime.getCurrentContext(), "wrap", ro);
		return ro;
	}

	private Object rubyToJava(IRubyObject value) {
		return JavaUtil.convertRubyToJava(value, Object.class);
	}

	/** The global scope. */
	private class Variables extends GlobalVariables {
		private Variables(Ruby runtime) {
			super(runtime);

			//we have to copy variables from the origin one to this
			GlobalVariables vars = runtime.getGlobalVariables();
			for (Iterator it = vars.getNames().iterator(); it.hasNext();) {
				final String nm = (String) it.next();
				set(nm, vars.get(nm));
			}
		}

		public IRubyObject get(String name) {
			IRubyObject ro = super.get(name);
			if (ro == _runtime.getNil()) {
				if (name.length() > 1 && name.charAt(0) == '$') //just in case
					name = name.substring(1);
				Object val = getFromNamespace(name);
				if (val != UNDEFINED)
					return javaToRuby(val);
			}
			return ro;
		}
	}

	/*ruby variable*/
	private class Variable implements IAccessor {
		private Object _value;

		public Variable(Object value) {
			_value = value;
		}

		public IRubyObject getValue() {
			return javaToRuby(_value);
		}

		public IRubyObject setValue(IRubyObject value) {
			_value = rubyToJava(value);
			return value;
		}
	}
}
