package me.exz.omniocular.handler;

import me.exz.omniocular.util.NBTHelper;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.item.Item;
import net.minecraft.item.ItemStack;
import net.minecraft.nbt.NBTTagCompound;
import net.minecraft.util.StatCollector;
import net.minecraftforge.fluids.Fluid;
import net.minecraftforge.fluids.FluidRegistry;
import net.minecraftforge.fluids.FluidStack;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;

import javax.script.Invocable;
import javax.script.ScriptEngine;
import javax.script.ScriptEngineManager;
import javax.script.ScriptException;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import static me.exz.omniocular.util.NBTHelper.NBTCache;

@SuppressWarnings({"CanBeFinal", "UnusedDeclaration"})
public class JSHandler {
    static ScriptEngine engine;
    static HashSet<String> scriptSet = new HashSet<>();
    private static List<String> lastTips = new ArrayList<>();
    private static int lastHash;
    private static Map<String, String> fluidList = new HashMap<>();
    private static Map<String, String> displayNameList = new HashMap<>();
    private static EntityPlayer entityPlayer;

    static List<String> getBody(Map<Pattern, Node> patternMap, NBTTagCompound n, String id, EntityPlayer player) {
        entityPlayer = player;
        if (n.hashCode() != lastHash || player.worldObj.getTotalWorldTime() % 10 == 0) {
            lastHash = n.hashCode();
            lastTips.clear();
            //LogHelper.info(NBTHelper.NBT2json(n));
            try {
                String bareJson = NBTHelper.NBT2json(n);
                String json = "var nbt=" + bareJson + ";";
                JSHandler.engine.eval(json);
                WebSocketHandler.broadcast(bareJson);
            } catch (ScriptException e) {
                e.printStackTrace();
            }
            for (Map.Entry<Pattern, Node> entry : patternMap.entrySet()) {
                Matcher matcher = entry.getKey().matcher(id);
                if (matcher.matches()) {
                    Element item = (Element) entry.getValue();
//                    if (item.getElementsByTagName("head").getLength() > 0) {
//                        Node head = item.getElementsByTagName("head").item(0);
//                    }
                    if (item.getElementsByTagName("line").getLength() > 0) {
                        String tip;
                        NodeList lines = item.getElementsByTagName("line");
                        for (int i = 0; i < lines.getLength(); i++) {
                            Node line = lines.item(i);
                            String displayname = "";
                            if (line.getAttributes().getNamedItem("displayname") != null && !line.getAttributes().getNamedItem("displayname").getTextContent().trim().isEmpty()) {
                                displayname = StatCollector.translateToLocal(line.getAttributes().getNamedItem("displayname").getTextContent());
                            }
                            String functionContent = line.getTextContent();
                            String hash = "S" + NBTHelper.MD5(functionContent);
                            if (!JSHandler.scriptSet.contains(hash)) {
                                JSHandler.scriptSet.add(hash);
                                if (!functionContent.contains("return")) {
                                    functionContent = "return " + functionContent.trim();
                                }
                                String script = "function " + hash + "()" + "{" + functionContent + "}";
                                try {
                                    JSHandler.engine.eval(script);
                                } catch (Exception e) {
                                    e.printStackTrace();
                                }
                            }
                            Invocable invoke = (Invocable) JSHandler.engine;
                            try {
                                String result = String.valueOf(invoke.invokeFunction(hash, ""));
                                if (result.equals("__ERROR__") || result.equals("null") || result.equals("undefined")
                                        || result.equals("NaN")) {
                                    continue;
                                }
                                if (patternMap == ConfigHandler.tooltipPattern) {
                                    tip = "\u00A77" + displayname + ": \u00A7f";
                                } else {
                                    tip = ConfigHandler.settingList.get("displaynameTileentity")
                                            .replace("DISPLAYNAME", displayname)
                                            .replace("RETURN", result);
                                }
                            } catch (Exception e) {
                                continue;
                                //e.printStackTrace();
                            }
                            if (tip.equals("__ERROR__")) {
                                continue;
                            }
                            lastTips.addAll(Arrays.asList(tip.split("\n")));
                        }
                    }
                }
            }
        }
        return lastTips;
    }

    //todo provide an function to detect player keyboard action. (hold shift, etc.)
    static void initEngine() {
        ScriptEngineManager manager = new ScriptEngineManager(null);
        engine = manager.getEngineByName("javascript");
        setSpecialChar();
        /* java 8 work around */
        try {
            engine.eval("load(\"nashorn:mozilla_compat.js\");");
        } catch (ScriptException e) {
            //e.printStackTrace();
        }
        try {
            engine.eval("importClass(Packages.me.exz.omniocular.handler.JSHandler);");
            engine.eval("function translate(t){return Packages.me.exz.omniocular.handler.JSHandler.translate(t)}");
            engine.eval("function translateFormatted(t,obj){return Packages.me.exz.omniocular.handler.JSHanlder.translateFormatted(t,obj)}");
            engine.eval("function name(n){return Packages.me.exz.omniocular.handler.JSHandler.getDisplayName(n.hashCode)}");
            engine.eval("function fluidName(n){return Packages.me.exz.omniocular.handler.JSHandler.getFluidName(n)}");
            engine.eval("function holding(){return Packages.me.exz.omniocular.handler.JSHandler.playerHolding()}");
            engine.eval("function armor(i){return Packages.me.exz.omniocular.handler.JSHandler.playerArmor(i)}");
            engine.eval("function isInHotbar(n){return Packages.me.exz.omniocular.handler.JSHandler.haveItemInHotbar(n)}");
            engine.eval("function isInInv(n){return Packages.me.exz.omniocular.handler.JSHandler.haveItemInInventory(n)}");
        } catch (ScriptException e) {
            e.printStackTrace();
        }
    }

    private static void setSpecialChar() {
        String MCStyle = "\u00A7";
        engine.put("BLACK", MCStyle + "0");
        engine.put("DBLUE", MCStyle + "1");
        engine.put("DGREEN", MCStyle + "2");
        engine.put("DAQUA", MCStyle + "3");
        engine.put("DRED", MCStyle + "4");
        engine.put("DPURPLE", MCStyle + "5");
        engine.put("GOLD", MCStyle + "6");
        engine.put("GRAY", MCStyle + "7");
        engine.put("DGRAY", MCStyle + "8");
        engine.put("BLUE", MCStyle + "9");
        engine.put("GREEN", MCStyle + "a");
        engine.put("AQUA", MCStyle + "b");
        engine.put("RED", MCStyle + "c");
        engine.put("LPURPLE", MCStyle + "d");
        engine.put("YELLOW", MCStyle + "e");
        engine.put("WHITE", MCStyle + "f");

        engine.put("OBF", MCStyle + "k");
        engine.put("BOLD", MCStyle + "l");
        engine.put("STRIKE", MCStyle + "m");
        engine.put("UNDER", MCStyle + "n");
        engine.put("ITALIC", MCStyle + "o");
        engine.put("RESET", MCStyle + "r");
        String WailaStyle = "\u00A4";
        String WailaIcon = "\u00A5";
        engine.put("TAB", WailaStyle + WailaStyle + "a");
        engine.put("ALIGNRIGHT", WailaStyle + WailaStyle + "b");
        engine.put("ALIGNCENTER", WailaStyle + WailaStyle + "c");
        engine.put("HEART", WailaStyle + WailaIcon + "a");
        engine.put("HHEART", WailaStyle + WailaIcon + "b");
        engine.put("EHEART", WailaStyle + WailaIcon + "c");
//        LogHelper.info("Special Char loaded");
    }

    public static String translate(String t) {
        return StatCollector.translateToLocal(t);
    }

    public static String translateFormatted(String t, Object[] format) {
        return StatCollector.translateToLocalFormatted(t, format);
    }

    public static String playerHolding() {
        ItemStack is = entityPlayer.getHeldItem();
        if (is == null) {
            return "";
        }
        return Item.itemRegistry.getNameForObject(is.getItem());
    }

    public static String playerArmor(int i) {
        if (i < 0 || i > 3) {
            return null;
        }
        ItemStack is = entityPlayer.inventory.armorInventory[i];
        if (is == null) {
            return "";
        }
        return Item.itemRegistry.getNameForObject(is.getItem());
    }

    public static Boolean haveItemInHotbar(String n) {
        int s = entityPlayer.inventory.func_146029_c((Item) Item.itemRegistry.getObject(n));
        return s > -1 && s < 9;
    }

    public static Boolean haveItemInInventory(String n) {
        return entityPlayer.inventory.func_146029_c((Item) Item.itemRegistry.getObject(n)) != -1;
    }

    public static String getDisplayName(String hashCode) {
        try {
            NBTTagCompound nc = NBTCache.get(Integer.valueOf(hashCode));
            ItemStack is = ItemStack.loadItemStackFromNBT(nc);
            return is.getDisplayName();
        } catch (Exception e) {
            return "__ERROR__";
        }
    }

    public static String getFluidName(String uName) {
        if (fluidList.containsKey(uName)) {
            return fluidList.get(uName);
        } else {
            try {
                Fluid f = FluidRegistry.getFluid(uName.toLowerCase());
                FluidStack fs = new FluidStack(f, 1);
                String lName = fs.getLocalizedName();
                fluidList.put(uName, lName);
                return lName;
            } catch (Exception e) {
                return "__ERROR__";
            }
        }
    }
}