package com.sitepen;

import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;

public class JsonRPC extends HttpServlet {
	static Map<String,Object> rpcTargets = new HashMap<String,Object>();
	
	@Override
	protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
		String content = streamToString(req.getInputStream());
		try {
			JSONObject rpcObject = new JSONObject(content);
			String methodName = rpcObject.getString("method");
			String id = rpcObject.getString("id");
			JSONArray paramsArray = rpcObject.getJSONArray("params");
			Object[] params = new Object[paramsArray.length()];
			for (int i = 0 ; i < paramsArray.length(); i++) {
				params[i] = paramsArray.get(i);
			}
			String targetPath = req.getPathInfo();
			Object target = rpcTargets.get(targetPath);
			JSONObject response = new JSONObject();
			response.put("id",id);
			try {				
				for (Method method : target.getClass().getMethods()) {
					if (method.getName().equals(methodName)) {
						Object result = method.invoke(target, params);
						response.put("result",result);
						response.put("error",JSONObject.NULL);
						resp.getOutputStream().print(response.toString());
						return;
					}
				}
				throw new RuntimeException("Could not find the method " + methodName + " on the object " + targetPath);
			} catch (Throwable e) {
				response.put("result",JSONObject.NULL);
				response.put("error",e.getMessage());
				resp.getOutputStream().print(response.toString());
			}
			
		}
		catch (JSONException jsonException) {
			throw new RuntimeException(jsonException);
		}
	}
	public static void registerRpcTarget(String path, Object target) {
		rpcTargets.put(path,target);
	}
	
	static String streamToString(InputStream in) throws IOException {
		StringBuffer out = new StringBuffer();
		byte[] b = new byte[4096];
		for (int n; (n = in.read(b)) != -1;) {
			out.append(new String(b, 0, n));
		}
		return out.toString();
	}
	static {
		registerRpcTarget("/echo",new Echo());
	}
}
