package net.lax1dude.eaglercraft;

import java.io.PrintWriter;
import java.io.StringWriter;

import org.teavm.jso.JSBody;
import org.teavm.jso.browser.Window;
import org.teavm.jso.core.JSError;
import org.teavm.jso.dom.html.HTMLDocument;
import org.teavm.jso.dom.html.HTMLElement;

import net.lax1dude.eaglercraft.adapter.EaglerAdapterImpl2;
import net.minecraft.client.Minecraft;

public class Client {
	private static final String crashImage = "data:image/png;base64,[encoded data, 31296 characters]";

	private static class MinecraftImpl extends Minecraft {
		
		@Override
		public void displayCrashScreen(Throwable t) {
			StringWriter s = new StringWriter();
			t.printStackTrace(new PrintWriter(s));
			showCrashScreen(s.toString());
		}
		
	}
	
	public static class AbortedLaunchException extends RuntimeException {
		// yee
	}
	
	// avoid inlining of constant
	private static String crashImageWrapper() {
		return crashImage.substring(0);
	}
	
	public static HTMLElement rootElement = null;
	public static Minecraft instance = null;
    public static void main(String[] args) {
    	registerErrorHandler();
    	String[] e = getOpts();
    	try {
	    	try {
	    		EaglerAdapterImpl2.initializeContext(rootElement = Window.current().getDocument().getElementById(e[0]), e[1]);
	    	}catch(AbortedLaunchException ex) {
	    		return;
	    	}
    	}catch(Throwable ex2) {
    		StringWriter s = new StringWriter();
    		ex2.printStackTrace(new PrintWriter(s));
			showCrashScreen(s.toString());
    		return;
    	}
    	LocalStorageManager.loadStorage();
    	if(e.length > 2) {
    		EaglerAdapterImpl2.setServerToJoinOnLaunch(e[2]);
    	}
    	run0();
    }

    private static void run0() {
    	System.out.println(" -------- starting minecraft -------- ");
    	instance = new MinecraftImpl();
    	run1();
    }
    
    private static void run1() {
    	instance.run();
    }

	@JSBody(params = { }, script = "return window.minecraftOpts;")
	public static native String[] getOpts();

	@JSBody(params = { }, script = "window.minecraftError = null; window.onerror = function(message, file, line, column, errorObj) { if(errorObj) { window.minecraftError = errorObj; window.minecraftErrorL = \"\"+line+\":\"+column; javaMethods.get(\"net.lax1dude.eaglercraft.Client.handleNativeError()V\").invoke(); } else { alert(\"a native browser exception was thrown but your browser does not support fith argument in onerror\"); } };")
	public static native void registerErrorHandler();

	@JSBody(params = { }, script = "return window.minecraftError;")
	public static native JSError getWindowError();
	
	@JSBody(params = { }, script = "return window.minecraftErrorL;")
	public static native String getWindowErrorL();
	
	public static void handleNativeError() {
		JSError e = getWindowError();
		StringBuilder str = new StringBuilder();
		str.append("Native Browser Exception\n");
		str.append("----------------------------------\n");
		str.append("  Line: ").append(getWindowErrorL()).append('\n');
		str.append("  Type: ").append(e.getName()).append('\n');
		str.append("  Message: ").append(e.getMessage()).append('\n');
		str.append("----------------------------------\n\n");
		str.append(e.getStack()).append('\n');
		showCrashScreen(str.toString());
	}
	
	private static boolean isCrashed = false;
	
	private static void showCrashScreen(String t) {
		if(!isCrashed) {
			isCrashed = true;
			EaglerAdapterImpl2.removeEventHandlers();
			
			StringBuilder str = new StringBuilder();
			str.append("Game Crashed! I have fallen and I can't get up! If this has happened more than once then please copy the text on this screen and publish it in the issues feed of this fork's GitHub repository.\n\nThe URL to this fork's GitHub repository is: " + ConfigConstants.forkMe + "\n\n");
			str.append(t);
			str.append('\n').append('\n');
			str.append("eaglercraft.version = \"").append(ConfigConstants.version).append("\"\n");
			str.append("eaglercraft.minecraft = \"b1.3\"\n");
			str.append("eaglercraft.brand = \"eagtek\"\n");
			//str.append("eaglercraft.username = \"").append(EaglerProfile.username).append("\"\n");
			//str.append("eaglercraft.channel = \"").append(EaglerProfile.myChannel).append("\"\n");
			str.append('\n');
			shortenMinecraftOpts();
			addArray(str, "minecraftOpts");
			str.append('\n');
			addDebugNav(str, "userAgent");
			addDebugNav(str, "vendor");
			addDebugNav(str, "language");
			addDebugNav(str, "hardwareConcurrency");
			addDebugNav(str, "deviceMemory");
			addDebugNav(str, "platform");
			addDebugNav(str, "product");
			str.append('\n');
			str.append("rootElement.clientWidth = ").append(rootElement.getClientWidth()).append('\n');
			str.append("rootElement.clientHeight = ").append(rootElement.getClientHeight()).append('\n');
			addDebug(str, "innerWidth");
			addDebug(str, "innerHeight");
			addDebug(str, "outerWidth");
			addDebug(str, "outerHeight");
			addDebug(str, "devicePixelRatio");
			addDebugScreen(str, "availWidth");
			addDebugScreen(str, "availHeight");
			addDebugScreen(str, "colorDepth");
			addDebugScreen(str, "pixelDepth");
			str.append('\n');
			addDebug(str, "currentContext");
			str.append('\n');
			addDebugLocation(str, "href");
			str.append("\n----- Begin Minecraft Config -----\n");
			str.append(LocalStorageManager.dumpConfiguration());
			str.append("\n----- End Minecraft Config -----\n\n");
			addDebug(str, "minecraftServer");
			
			String s = rootElement.getAttribute("style");
			rootElement.setAttribute("style", (s == null ? "" : s) + "position:relative;");
			HTMLDocument doc = Window.current().getDocument();
			HTMLElement img = doc.createElement("img");
			HTMLElement div = doc.createElement("div");
			img.setAttribute("style", "z-index:100;position:absolute;top:10px;left:calc(50% - 151px);");
			img.setAttribute("src", crashImageWrapper());
			div.setAttribute("style", "z-index:100;position:absolute;top:135px;left:10%;right:10%;bottom:30px;background-color:white;border:1px solid #cccccc;overflow-x:hidden;overflow-y:scroll;overflow-wrap:break-word;white-space:pre-wrap;font: 14px monospace;padding:10px;");
			rootElement.appendChild(img);
			rootElement.appendChild(div);
			div.appendChild(doc.createTextNode(str.toString()));
			
		}
	}

	public static void showDatabaseLockedScreen(String msg) {
		String s = rootElement.getAttribute("style");
		rootElement.setAttribute("style", (s == null ? "" : s) + "position:relative;");
		HTMLDocument doc = Window.current().getDocument();
		HTMLElement img = doc.createElement("img");
		HTMLElement div = doc.createElement("div");
		img.setAttribute("style", "z-index:100;position:absolute;top:10px;left:calc(50% - 151px);");
		img.setAttribute("src", crashImageWrapper());
		div.setAttribute("style", "z-index:100;position:absolute;top:135px;left:10%;right:10%;bottom:30px;background-color:white;border:1px solid #cccccc;overflow-x:hidden;overflow-y:scroll;overflow-wrap:break-word;white-space:pre-wrap;font: 24px sans-serif;padding:10px;");
		rootElement.appendChild(img);
		rootElement.appendChild(div);
		div.appendChild(doc.createTextNode(msg));
	}
	
	@JSBody(params = { "v" }, script = "try { return \"\"+window[v]; } catch(e) { return \"<error>\"; }")
	private static native String getString(String var);
	
	@JSBody(params = { "v" }, script = "try { return \"\"+window.navigator[v]; } catch(e) { return \"<error>\"; }")
	private static native String getStringNav(String var);

	@JSBody(params = { "v" }, script = "try { return \"\"+window.screen[v]; } catch(e) { return \"<error>\"; }")
	private static native String getStringScreen(String var);

	@JSBody(params = { "v" }, script = "try { return \"\"+window.location[v]; } catch(e) { return \"<error>\"; }")
	private static native String getStringLocation(String var);
	
	@JSBody(params = { }, script = "for(var i = 0; i < window.minecraftOpts.length; ++i) { if(window.minecraftOpts[i].length > 2048) window.minecraftOpts[i] = \"[\" + Math.floor(window.minecraftOpts[i].length * 0.001) + \"k characters]\"; }")
	private static native void shortenMinecraftOpts();

	private static void addDebug(StringBuilder str, String var) {
		str.append("window.").append(var).append(" = ").append(getString(var)).append('\n');
	}
	
	private static void addDebugNav(StringBuilder str, String var) {
		str.append("window.navigator.").append(var).append(" = ").append(getStringNav(var)).append('\n');
	}
	
	private static void addDebugScreen(StringBuilder str, String var) {
		str.append("window.screen.").append(var).append(" = ").append(getStringScreen(var)).append('\n');
	}
	
	private static void addDebugLocation(StringBuilder str, String var) {
		str.append("window.location.").append(var).append(" = ").append(getStringLocation(var)).append('\n');
	}
	
	private static void addArray(StringBuilder str, String var) {
		str.append("window.").append(var).append(" = ").append(getArray(var)).append('\n');
	}
	
	@JSBody(params = { "v" }, script = "try { return (typeof window[v] !== \"undefined\") ? JSON.stringify(window[v]) : \"[\\\"<error>\\\"]\"; } catch(e) { return \"[\\\"<error>\\\"]\"; }")
	private static native String getArray(String var);
	
}
