package de.hsadmin.jscli;

import java.io.Reader;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

import javax.script.ScriptEngine;
import javax.script.ScriptEngineManager;
import javax.script.ScriptException;

public class ScriptClient {

	final private ScriptEngine engine;
	final private Set<String> completionStrings;

	public ScriptClient(ConsoleWrapper console, String user, String runAs) throws JSCliException {
		completionStrings = new HashSet<String>();
		CASTicket grantingTicket = new CASTicket(console, user, runAs);
		RpcClient rpcClient = new RpcClient(grantingTicket);
		ScriptEngineManager engineManager = new ScriptEngineManager();
		engine = engineManager.getEngineByName("js");
		try {
			engine.put("casgrantingticket", grantingTicket);
			engine.put("xmlrpcclient", rpcClient);
			engine.put("xmlrpcLastResult", null);
			engine.eval("importClass(java.util.ArrayList);");
			engine.eval("importClass(java.util.HashMap);");
			engine.eval("function hsaParseParam(val) { " +
					"if (val instanceof java.util.List) return val;" + 
					"if (val instanceof java.util.Map) return val;" + 
					"if (typeof val === 'object' && val.constructor === Array) { res = hsaParseParamArray(val); } " +
					"else if (typeof val === 'object') { res = hsaParseParamObject(val); }; " +
					"return res; " +
					"}");
			engine.eval("function hsaParseParamArray(o) { " +
					"var lst = new ArrayList(); " +
					"var val = ''; " +
					"for (var idx=0; idx < o.length; idx++) { " +
					"  val = o[idx]; " +
					"  if (typeof val === 'object' && val.constructor === Array) { val = hsaParseParamArray(val); } " +
					"  else if (typeof val === 'object') { val = hsaParseParamObject(val); }; " +
					"  lst.add(val); " +
					"}; " +
					"return lst; " +
					"}");
			engine.eval("function hsaParseParamObject(o) { " +
					"var hsh = new HashMap(); " +
					"for (var key in o) { " +
					"  var val = o[key]; " +
					"  if (typeof val === 'object' && val.constructor === Array) { val = hsaParseParamArray(val); } " +
					"  else if (typeof val === 'object') { val = hsaParseParamObject(val); }; " +
					"  hsh.put(key, val); " +
					"}; " +
					"return hsh; " +
					"}");
			engine.eval("function hsaToNativeJSObject(val) { " +
					"if (val instanceof java.util.List) {" +
				    "    var res = [];" +
				    "    for (i = 0; i < val.size(); i++) {" +
				    "        res[i] = hsaToNativeJSObject(val.get(i));" +
				    "    }" +
				    "    return res;" +
				    "}" +
				    "if (val instanceof java.util.Map) {" +
				    "    var res = {};" +
				    "    var iter = val.keySet().iterator();" +
				    "    while (iter.hasNext()) {" +
				    "        var key = iter.next();" +
				    "        res[key] = hsaToNativeJSObject(val.get(key));" +
				    "    }" +
				    "    return res;" +
				    "}" +
				    "return val;" +
					"};");
		} catch (ScriptException e) {
			throw new JSCliException(e);
		}
		List<String> methods = rpcClient.listMethods();
		for (String method : methods) {
			String[] parts = method.split("\\.");
			if (parts.length == 2) {
				String module = parts[0];
				String function = parts[1];
				if ("delete".equals(function)) {
					function = "remove";
				}
				completionStrings.add(module);
				completionStrings.add(module + "." + function);
				try {
					engine.eval("if (typeof " + module + " === 'undefined') " +
								"{ var " + module + " = { }; };\n" +
								module + "['" + function + "'] = function(json) { " +
								"var mod = '" + module + "'; " +
								("remove".equals(function) ? "var fct = 'delete'; " : "var fct = '" + function + "'; ") +
								"var params = new ArrayList(); " +
								"params.add(casgrantingticket.getRunAs()); " + 
								"params.add(casgrantingticket.getTicket()); " + 
								"if (typeof json === 'undefined') { json = { where:{}, set:{} } };" +
								"if (fct == 'update' || fct == 'add') { params.add(hsaParseParamObject(json['set'])); }; " + 
								"if (fct == 'update' || fct == 'delete' || fct == 'search') { params.add(hsaParseParamObject(json['where'])); }; " +
								"xmlrpcLastResult = xmlrpcclient.execute(mod + '.' + fct, params);" +
								"return hsaToNativeJSObject(xmlrpcLastResult); " + 
								"};");
				} catch (ScriptException e) {
					e.printStackTrace();
				}
			}
		}
		console.codeCompletion(getCodeCompletionStrings());
	}
	
	public String[] getCodeCompletionStrings() {
		String[] codeCompletionStrings = new String[completionStrings.size()];
		int idx = 0;
		for (String s : completionStrings) {
			codeCompletionStrings[idx] = s;
			idx++;
		}
		return codeCompletionStrings;
	}

	public Object execute(String snippet) throws JSCliException {
		try {
			engine.put("xmlrpcLastResult", null);
			return engine.eval(snippet);
		} catch (ScriptException e) {
			throw new JSCliException(e);
		}
	}
	
	public Object execute(Reader rd) throws JSCliException {
		try {
			engine.put("xmlrpcLastResult", null);
			return engine.eval(rd);
		} catch (ScriptException e) {
			throw new JSCliException(e);
		}
	}
	
	public Object getLastRpcResult() {
		return engine.get("xmlrpcLastResult");
	}
	
}
