package com.github.steveice10.bot.service.web;

import com.google.gson.JsonArray;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;

public class Code {
    public static String execute(Language language, String code) {
        String ret = null;
        String out = null;
        if(language == Language.BRAINF) {
            out = executeBrainf(code);
        } else if(language == Language.JAVA) {
            JsonObject idResp = Web.postJson(JsonObject.class, "http://www.javarepl.com/create");
            if(idResp != null) {
                String id = idResp.get("id").getAsString();
                JsonObject resp = Web.postJson(JsonObject.class, "http://www.javarepl.com/execute", "expression=" + Web.urlEncode(code), "id=" + Web.urlEncode(id));
                Web.post("http://www.javarepl.com/remove", "id=" + Web.urlEncode(id));
                if(resp != null) {
                    StringBuilder log = new StringBuilder();
                    for(JsonElement e : resp.getAsJsonArray("logs")) {
                        log.append(e.getAsJsonObject().get("message").getAsString()).append("\n");
                    }

                    out = log.toString().trim();
                }
            }
        } else if(language == Language.GROOVY) {
            JsonObject resp = Web.postJson(JsonObject.class, "https://groovyconsole.appspot.com/executor.groovy", "script=" + Web.urlEncode(code));
            if(resp != null) {
                ret = resp.get("executionResult").getAsString();
                out = resp.get("outputText").getAsString();
            }
        } else if(language == Language.JAVASCRIPT) {
            JsonElement resp = Web.postJson(JsonElement.class, "http://api.webshell.io/", "format=json", "key=9e58407624b572d8a842dff48e0fe795", "code=" + Web.urlEncode(code));
            if(resp != null) {
                if(resp.isJsonArray()) {
                    JsonArray array = resp.getAsJsonArray();
                    JsonElement eret = array.get(0);
                    if(!eret.isJsonNull()) {
                        ret = eret.getAsString();
                    }

                    out = array.get(1).getAsJsonObject().getAsJsonObject("_meta").get("view").getAsString();
                } else {
                    ret = resp.getAsString();
                }
            }
        } else if(language == Language.CLOJURE) {
            JsonObject resp = Web.getJson(JsonObject.class, "http://tryclj.com/eval.json", "expr=" + Web.urlEncode(code));
            if(resp != null) {
                if(resp.has("message")) {
                    out = resp.get("message").getAsString();
                } else {
                    ret = resp.get("result").getAsString();
                }
            }
        } else {
            String resp = Web.post("http://codepad.org/", "code=" + Web.urlEncode(code), "lang=" + language.getName(), "run=True");
            if(resp != null) {
                String outputStart = "<a name=\"output\"><span class=\"heading\">Output:</span></a>";
                String prefix = "<div class=\"highlight\">\n<pre>\n";
                int ind = resp.indexOf(prefix, resp.indexOf(outputStart) + outputStart.length()) + prefix.length();
                out = resp.substring(ind, resp.indexOf("</pre>", ind)).replaceAll("<a href=\"#line-(\\d+)\">Line (\\d+)</a>:", "Line $2:").trim();
            }
        }

        StringBuilder result = new StringBuilder();
        if(ret != null && !ret.isEmpty()) {
            result.append("ret -> ").append(ret).append("\n");
        }

        if(out != null && !out.isEmpty()) {
            result.append("out -> ").append(out).append("\n");
        }

        if(result.length() == 0) {
            result.append("-> ok").append("\n");
        }

        return result.toString().trim();
    }

    private static String executeBrainf(String s) {
        StringBuilder output = new StringBuilder();

        byte data[] = new byte[4096];
        int dataPointer = 0;
        for(int charPointer = 0; charPointer < s.length(); charPointer++) {
            switch(s.charAt(charPointer)) {
                case '>':
                    dataPointer++;
                    if(dataPointer >= data.length) {
                        dataPointer = data.length - 1;
                    }

                    break;
                case '<':
                    dataPointer--;
                    if(dataPointer < 0) {
                        dataPointer = 0;
                    }

                    break;
                case '+':
                    data[dataPointer]++;
                    break;
                case '-':
                    data[dataPointer]--;
                    break;
                case '.':
                    output.append((char) data[dataPointer]);
                    break;
                case ',':
                    data[dataPointer] = 0;
                    break;
                case '[':
                    if(data[dataPointer] == 0) {
                        int i = 1;
                        while(i > 0) {
                            char c = s.charAt(++charPointer);
                            if(c == '[') {
                                i++;
                            } else if(c == ']') {
                                i--;
                            }
                        }
                    }

                    break;
                case ']':
                    int i = 1;
                    while(i > 0) {
                        char c = s.charAt(--charPointer);
                        if(c == '[') {
                            i--;
                        } else if(c == ']') {
                            i++;
                        }
                    }

                    charPointer--;
                    break;
            }
        }

        return output.toString();
    }

    public static enum Language {
        BRAINF("Brainf***"),
        JAVA("Java"),
        GROOVY("Groovy"),
        JAVASCRIPT("Javascript"),
        CLOJURE("Clojure"),
        C("C"),
        CPP("C++"),
        D("D"),
        HASKELL("Haskell"),
        LUA("Lua"),
        OCAML("OCaml"),
        PHP("PHP"),
        PERL("Perl"),
        PYTHON("Python"),
        RUBY("Ruby"),
        SCHEME("Scheme"),
        TCL("Tcl");

        private String name;

        private Language(String name) {
            this.name = name;
        }

        public String getName() {
            return this.name;
        }

        public static Language getLanguage(String name) {
            for(Language language : values()) {
                if(language.getName().equalsIgnoreCase(name)) {
                    return language;
                }
            }

            return null;
        }
    }
}
