content: package com.alecgorge.minecraft.jsonapi.packets.netty;
  
  import java.io.PrintWriter;
  import java.io.StringWriter;
  import java.nio.charset.Charset;
  import java.util.ArrayList;
  import java.util.List;
  import java.util.logging.Logger;
  
  import io.netty.buffer.ByteBuf;
  import io.netty.buffer.Unpooled;
  import io.netty.handler.codec.http.DefaultFullHttpResponse;
  import io.netty.handler.codec.http.FullHttpRequest;
  import io.netty.handler.codec.http.FullHttpResponse;
  import io.netty.handler.codec.http.HttpResponseStatus;
  import io.netty.handler.codec.http.HttpVersion;
  import io.netty.handler.codec.http.QueryStringDecoder;
  import io.netty.util.CharsetUtil;
  
  import org.json.simpleForBukkit.JSONArray;
  import org.json.simpleForBukkit.JSONObject;
  import org.json.simpleForBukkit.parser.JSONParser;
  import org.json.simpleForBukkit.parser.ParseException;
  
  import com.alecgorge.minecraft.jsonapi.JSONAPI;
  import com.alecgorge.minecraft.jsonapi.api.v2.JSONResponse;
  import com.alecgorge.minecraft.jsonapi.config.UsersConfig;
  import com.alecgorge.minecraft.jsonapi.dynamic.Caller;
  import com.alecgorge.minecraft.jsonapi.permissions.JSONAPIAuthResponse;
  import com.alecgorge.minecraft.jsonapi.permissions.JSONAPIUser;
  import com.alecgorge.minecraft.jsonapi.streams.StreamingResponse;
  
  public class APIv2Handler {
  	static Logger jsonapiLog = JSONAPI.instance.outLog;
  	static String CONTENT_TYPE_JSON = "application/json";
  	
  	FullHttpRequest request;
  	QueryStringDecoder uri;
  	
  	List<JSONResponse> requests = new ArrayList<JSONResponse>();
  	
  	JSONParser parser = new JSONParser();
  	
  	public APIv2Handler (FullHttpRequest req) {
  		request = req;
  		uri = new QueryStringDecoder(request.getUri());
  	}
  	
  	public static boolean canServe(QueryStringDecoder u) {
  		JSONAPI.dbug("can serve? " + u.path());
  		
  		return u.path().startsWith("/api/2/") && !u.path().equals("/api/2/websocket");
  	}
  	
  	public FullHttpResponse serve() {
  		try {
  			if(uri.path().equals("/api/2/call")) {
  				readPayload(false);
  				return call();
  			}
  			// just use websocket for subscriptions, ok?
  //			else if(uri.path().equals("/api/2/subscribe")) {
  //				readPayload(true);
  //				return subscribe();
  //			}
  			else if(uri.path().equals("/api/2/version")) {
  				return version();
  			}
  			else {
  				return resp(HttpResponseStatus.NOT_FOUND, "text/plain", "Not found.\n");
  			}
  		}
  		catch (ParseException e) {
  			StringWriter errors = new StringWriter();
  			e.printStackTrace(new PrintWriter(errors));
  			
  			return resp(HttpResponseStatus.BAD_REQUEST, CONTENT_TYPE_JSON, "["+JSONResponse.APIError(errors.toString(), 4, "JSON_PARSE_ERROR", "").toJSONString()+"]\n");
  		}
  	}
  	
  	public boolean isStream() {
  		return uri.path().equals("/api/2/subscribe");
  	}
  
  	public FullHttpResponse call() {
  		JSONArray a = new JSONArray();
  		
  		for(JSONResponse resp : requests) {
  			a.add(resp.getJSONObject());
  		}
  		
  		String json = a.toJSONString();
  		
  		JSONAPI.dbug("returning: " + json);
  		return resp(HttpResponseStatus.OK, CONTENT_TYPE_JSON, json + "\n");
  	}
  	
  	public FullHttpResponse version() {
  		JSONObject versionObj = new JSONObject();
  		versionObj.put("version", JSONAPI.instance.getDescription().getVersion());
  		versionObj.put("server_version", JSONAPI.instance.getServer().getVersion());
  		
  		return resp(HttpResponseStatus.OK, CONTENT_TYPE_JSON, versionObj.toJSONString());
  	}
  	
  	public FullHttpResponse resp(HttpResponseStatus resp, String type, String body) {
  		if(uri.parameters().containsKey("callback") && type.equals(CONTENT_TYPE_JSON)) {
  			body = uri.parameters().get("callback") + "(" + body + ");";
  		}
  		
  		ByteBuf buf = Unpooled.copiedBuffer(body, CharsetUtil.UTF_8);
  		FullHttpResponse r = new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, resp, buf);
  		r.headers().set("Access-Control-Allow-Origin", "*");
  		r.headers().set("Content-Length", buf.readableBytes());
  		r.headers().set("Content-Type", type);
  
  		return r;
  	}
  	
  	public StreamingResponse subscribe() {
  		try {
  			readPayload(true);
  		}
  		catch (ParseException e) {
  			e.printStackTrace();
  		}
  
  		List<String> 	sourceLists = new ArrayList<String>();
  		List<Boolean> 	showOlder 	= new ArrayList<Boolean>();
  		List<String> 	tag 		= new ArrayList<String>();
  		List<JSONObject>defaults	= new ArrayList<JSONObject>();
  		
  		
  		for(JSONResponse resp : requests) {
  			JSONAPIAuthResponse auth = resp.testLogin(true);
  			if(auth.isAllowed() && auth.isAuthenticated()) {
  				sourceLists.add(resp.getMethodName());
  				showOlder.add(resp.isShowOlder());
  				tag.add(resp.getTag());				
  			}
  			else {
  				defaults.add(resp.getJSONObject());
  			}
  		}
  		
  		StreamingResponse streams = new StreamingResponse(JSONAPI.instance, sourceLists, uri.parameters().containsKey("callback") ? uri.parameters().get("callback").get(0) : null, showOlder, tag, defaults);
  		
  		return streams;
  	}
  	
  	public void readPayload(boolean stream) throws ParseException {
          String json = null;
          if (uri.parameters().containsKey("json")) {
              json = uri.parameters().get("json").get(0);
          } else {
              ByteBuf byteBuf = request.content();
              if (byteBuf.isReadable()) {
                  json = byteBuf.toString(Charset.forName("UTF-8"));
              }
          }
  
          if (json != null) {
              Object o = parser.parse(json);
  			
  			JSONAPI.dbug("json obj: "+ o);
  			
  			if(o instanceof JSONObject) {
  				requests.add(new JSONResponse((JSONObject)o, stream));
  			}
  			else if(o instanceof JSONArray) {
  				for(Object obj : (JSONArray) o) {
  					if(obj instanceof JSONObject) {
  						requests.add(new JSONResponse((JSONObject)obj, stream));
  					}
  				}
  			}
  			
  			String username = requests.get(0).getUsername();
  			JSONAPIUser user = UsersConfig.config().getUser(username);
  			if(user == null || user.getLogging()) {
  				// logging
  				StringBuilder b = new StringBuilder("[JSONAPI] ");
  				b.append(stream ? "[Stream Request] " : "[API Request] ");
  				b.append(requests.get(0).getUsername()).append(" requested: ");
  				
  				for(JSONResponse r : requests) {
  					b.append(r.getMethodName()).append("(").append(r.getArguments() == null ? "" : r.getArguments()).append(")");
  					
  					JSONAPIAuthResponse a = r.testLogin(false);
  					b.append("{").append(a.isAuthenticated() ? "AUTHED" : "NOT AUTHED").append(", ");
  					b.append(a.isAllowed() ? "ALLOWED" : "NOT ALLOWED");				
  					Caller c = JSONAPI.instance.jsonServer.getCaller();
  					
  					if(!c.methodExists(r.getMethodName()) && !JSONAPI.instance.getStreamManager().streamExists(r.getMethodName())) {
  						b.append(", NO-EXIST");
  					}
  					
  					b.append("} ");
  				}
  				
  				jsonapiLog.info(b.toString());
  			}
  		}
  	}	
  
  }
  