package jp.kirikiri.tjs2;

import java.util.HashMap;

public class ScriptCache {
	private static final int SCRIPT_CACHE_MAX = 64;

	class ScriptCacheData {
		public String mScript;
		public boolean mExpressionMode;
		public boolean mMustReturnResult;

		public boolean equals( Object o ) {
			if( o instanceof ScriptCacheData && o != null ) {
				ScriptCacheData rhs = (ScriptCacheData)o;
				return( mScript.equals(rhs.mScript) && mExpressionMode == rhs.mExpressionMode &&
						mMustReturnResult == rhs.mMustReturnResult );
			} else {
				return false;
			}
		}
		public int hashCode() {
			int v = mScript.hashCode();
			v ^= mExpressionMode ? 1 : 0;
			v ^= mMustReturnResult ? 1 : 0;
			return v;
		}
	}

	private TJS mOwner;
	private HashMap<ScriptCacheData,ScriptBlock> mCache;

	public ScriptCache( TJS owner) {
		mOwner = owner;
		mCache = new HashMap<ScriptCacheData,ScriptBlock>(SCRIPT_CACHE_MAX);
	}

	public void execScript( final char[] script, Variant result, Dispatch2 context, final char[] name, int lineofs ) throws VariantException, TJSException, CompileException {
		String sname = null;
		if( name != null ) sname = new String(name);
		String sscript = new String(script);
		ScriptBlock blk = new ScriptBlock(mOwner);
		if( name != null ) blk.setName(sname, lineofs);
		blk.setText(result, sscript, context, false);
	}

	public void execScript( final String script, Variant result, Dispatch2 context, final String name, int lineofs ) throws VariantException, TJSException, CompileException {
		ScriptBlock blk = new ScriptBlock(mOwner);
		if( name != null ) blk.setName(name, lineofs);
		blk.setText(result, script, context, false);
	}

	public void evalExpression(char[] expression, Variant result, Dispatch2 context, char[] name, int lineofs) throws VariantException, TJSException, CompileException {
		String sname = null;
		if( name != null ) sname = new String(name);
		String sexpression = new String(expression);
		evalExpression(sexpression,result,context,sname,lineofs);
	}

	public void evalExpression(String expression, Variant result, Dispatch2 context, String name, int lineofs) throws VariantException, TJSException, CompileException {
		// currently this works only with anonymous script blocks.
		// note that this function is basically the same as function above.
		if( name != null && name.length() > 0 ) {
			ScriptBlock blk = new ScriptBlock(mOwner);
			blk.setName( name, lineofs);
			blk.setText( result, expression, context, true );
			blk = null;
			return;
		}

		// search through script block cache
		ScriptCacheData data = new ScriptCacheData();
		data.mScript = expression;
		data.mExpressionMode = true;
		data.mMustReturnResult = result != null;

		ScriptBlock block = mCache.get(data);
		if( block != null ) {
			// found in cache
			// execute script block in cache
			block.executeTopLevelScript(result, context);
			return;
		}

		// not found in cache
		ScriptBlock blk = new ScriptBlock(mOwner);

		blk.setText( result, expression, context, true);
		// add to cache
		if( blk.isReusable() ) {
			// currently only single-context script block is cached
			mCache.put(data, blk);
		}
		blk = null;
		return;
	}
}
