package perros;

import com.sun.net.httpserver.HttpExchange;
import com.sun.net.httpserver.HttpHandler;
import com.sun.net.httpserver.HttpServer;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.lang.reflect.Constructor;
import java.net.InetSocketAddress;
import java.util.HashMap;
import java.util.NoSuchElementException;
import java.util.Map;
import java.util.Scanner;
import javax.script.ScriptEngine;
import javax.script.ScriptEngineManager;
import javax.script.ScriptException;

interface ApiHandler {
    void handle(Request request) throws Exception;
}

class Request {
    private HttpExchange exchange;
    private Map<String, String> queryParams;
    private Map bodyJson;
    private static ScriptEngine engine;

    public Request(HttpExchange exch) {
        exchange = exch;
        String query = exch.getRequestURI().getQuery();
        queryParams = parseQuery(query);
        bodyJson = parseBody(exch.getRequestBody());
    }

    public InputStream requestBody() {
        return exchange.getRequestBody();
    }

    public OutputStream responseBody() {
        return exchange.getResponseBody();
    }

    public Map<String, String> queryParams() {
        return queryParams;
    }

    public Map bodyJson() {
        return bodyJson;
    }

    public String method() {
        return exchange.getRequestMethod();
    }

    public void badRequest() throws IOException {
        finish(400);
    }

    public void ok() throws IOException {
        finish(200);
    }

    public void unauthorized() throws IOException {
        finish(403);
    }

    public void internalServerError(String error) throws IOException {
        finish(500, "Internal Server Error: " + error);
    }

    public void finish(int status) throws IOException {
        exchange.sendResponseHeaders(status, 0);
        exchange.getResponseBody().close();
    }

    public void finish(int status, String body) throws IOException {
        byte[] bytes = body.getBytes();
        exchange.sendResponseHeaders(status, bytes.length);
        try (OutputStream out = exchange.getResponseBody()) {
            out.write(bytes);
        }
    }

    private Map parseBody(InputStream stream) {
        String bodyString = "";
        try {
            try (Scanner scanner = new Scanner(stream)) {
                bodyString = scanner.useDelimiter("\\A").next();
            }
        } catch (NoSuchElementException e) { // Empty body is OK
        }
        try {
            String script = "Java.asJSONCompatible(" + bodyString + ")";
            return (Map)engine.eval(script);
        } catch (ScriptException se) {
            se.printStackTrace();
            return new HashMap<String, String>();
        }
    }

    private Map<String, String> parseQuery(String query) {
        Map<String, String> result = new HashMap<String, String>();
        if (query == null) {
            return result;
        }
        String[] parts = query.split("&");
        for (String part : parts) {
            String[] values = part.split("=");
            result.put(values[0], values[1]);
        }
        return result;
    }

    static {
        ScriptEngineManager sem = new ScriptEngineManager();
        engine = sem.getEngineByName("javascript");
    }
}

class Router implements HttpHandler {
    private Map<String, ApiHandler> routes = new HashMap<>();
    private Map<String, Class<? extends ApiHandler>> routeClasses = new HashMap<>();

    public void handle(HttpExchange exchange) throws IOException {
        String path = exchange.getRequestURI().getPath();
        ApiHandler handler = resolve(path);
        if (handler == null) {
            exchange.sendResponseHeaders(400, 0);
            exchange.getResponseBody().close();
        } else {
            Request request = new Request(exchange);
            try {
                handler.handle(request);
            } catch (Exception e) {
                request.internalServerError(e.getClass().toString());
            }
        }
    }

    public void addRoute(String route, Class<? extends ApiHandler> clazz) {
        routeClasses.put(route, clazz);
    }

    private ApiHandler resolve(String route) {
        ApiHandler result = routes.get(route);
        if (result != null) {
            return result;
        }
        Class<? extends ApiHandler> clazz = routeClasses.get(route);
        if (clazz != null) {
            try {
                Constructor<? extends ApiHandler> ctor = clazz.getConstructor();
                result = ctor.newInstance(new Object[] {});
            } catch (Exception e) {
                result = BadRoute.getInstance();
            }
            routes.put(route, result);
        }
        return result;
    }
}

class App {
    public static void main(String[] args) throws Exception {
        if (args.length < 1) {
            System.err.println("USAGE: <port>");
            System.exit(1);
        }

        Router router = new Router();
        router.addRoute("/auth", AuthHandler.class);
        router.addRoute("/register", RegisterHandler.class);
        router.addRoute("/admin", AdminHandler.class);
        router.addRoute("/users", UsersHandler.class);
        router.addRoute("/stats", StatsHandler.class);

        int port = Integer.parseInt(args[0]);
        HttpServer server = HttpServer.create(new InetSocketAddress(port), 0);
        server.createContext("/", router);
        server.setExecutor(null);
        System.out.println("Starting server...");
        server.start();
    }
}

@@ -4509,16 +4509,37 @@
  ctor = 
+%0A                    
 clazz.ge
@@ -4539,16 +4539,24 @@
 lazz.get
+Declared
 Construc
@@ -4558,24 +4558,66 @@
 structor();%0A
+                ctor.setAccessible(true);%0A
             
@@ -4650,23 +4650,8 @@
 nce(
-new Object%5B%5D %7B%7D
 );%0A 
@@ -4677,32 +4677,69 @@
 (Exception e) %7B%0A
+                e.printStackTrace();%0A
                 

Fix getConstructor call for lazy route loading