package org.almibe.codeeditor;

import javafx.application.Platform;
import javafx.event.EventHandler;
import javafx.scene.Parent;
import javafx.scene.web.WebErrorEvent;
import javafx.scene.web.WebView;

import java.util.Arrays;
import java.util.Queue;
import java.util.concurrent.*;

public class CodeMirrorEditor implements CodeEditor {
    private final WebView webView;
    private boolean isEditorInitialized = false; //TODO make atomic var?
    private final Queue<Runnable> queue = new LinkedBlockingQueue<>();
    private ScheduledExecutorService executor;

    public CodeMirrorEditor() {
        webView = new WebView();
    }

    @Override
    public void init(Runnable... runAfterLoading) {
        queue.addAll(Arrays.asList(runAfterLoading));
        try {
            webView.getEngine().load(CodeMirrorEditor.class.getResource("codemirror/editor.html").toURI().toString()); //TODO maybe use .toURI.toString() instead of toExternalFOrm
        } catch (Exception ex) {
            throw new RuntimeException(ex);
        }
        webView.getEngine().setOnError(new EventHandler<WebErrorEvent>() {
            @Override
            public void handle(WebErrorEvent event) {
                throw new RuntimeException(event.getException());
            }
        });

        executor = Executors.newSingleThreadScheduledExecutor();
        ScheduledFuture future = executor.scheduleWithFixedDelay(new Init(), 0, 100, TimeUnit.MILLISECONDS);
    }

    public class Init implements Runnable {
        private final String command = "init();";

        @Override
        public void run() {
            Platform.runLater(() -> {
                try {
                    webView.getEngine().executeScript("CodeMirror;");
                    webView.getEngine().executeScript(this.command);
                    executor.shutdown();
                    executor = null;
                    while(!queue.isEmpty()) {
                        Runnable runnable = queue.remove();
                        Platform.runLater(runnable);
                    }
                    isEditorInitialized = true;
                } catch (Exception ex) {
                    ; //Do nothing
                }
            });
        }
    }

    @Override
    public String getContent() {
        return (String) webView.getEngine().executeScript("codeMirror.getValue();");
    }

    @Override
    public void setContent(String newContent, boolean markClean) {
        String escapedContent = JsString.quote(newContent);
        Platform.runLater(() -> {
            webView.getEngine().executeScript("codeMirror.setValue(" + escapedContent + ");");
            if (markClean) { this.markClean(); }
        });
    }

    @Override
    public boolean isClean() {
        return (boolean) webView.getEngine().executeScript("codeMirror.isClean();");
    }

    @Override
    public void markClean() {
        webView.getEngine().executeScript("codeMirror.markClean();");
    }

    @Override
    public Position getCursorPosition() {
        String position = (String) webView.getEngine().executeScript("");
        return null;
    }

    @Override
    public void setCursorPosition(Position position) {
        Platform.runLater(() -> {
            webView.getEngine().executeScript("");
        });
    }

    @Override
    public boolean isEditorInitialized() {
        return isEditorInitialized;
    }

    @Override
    public Parent getWidget() {
        return this.webView;
    }

    @Override
    public boolean isReadOnly() {
        return (Boolean) webView.getEngine().executeScript("codeMirror.getOption('readOnly');");
    }

    @Override
    public void setReadOnly(boolean readOnly) {
        Platform.runLater(() -> {
            webView.getEngine().executeScript("codeMirror.setOption('readOnly', " + readOnly + ");");
        });
    }

    @Override
    public String getMode() {
        return (String) webView.getEngine().executeScript("codeMirror.getOption('mode');");
    }

    @Override
    public void setMode(String mode) {
        Platform.runLater(() -> {
            webView.getEngine().executeScript("setMode(\"" + mode + "\");");
        });
    }

//    @Override
//    public void includeJSModules(String[] modules, Runnable runnable) {
//        //TODO test this
//        //fetchCodeEditorObject().call("importJSModules", modules, runnable);
//    }

    @Override
    public String getTheme() {
        return (String) webView.getEngine().executeScript("codeMirror.getOption('theme');");
    }

    @Override
    public void setTheme(String theme, String... cssFile) {
        String argument = "'" + theme + "'";
        if(cssFile != null && cssFile.length > 0) {
            String cssFileArgument = "";
            for(String file : cssFile) {
                cssFileArgument += ", '" + file + "'";
            }
            argument += cssFileArgument;
        }
        final String finalArg = argument;
        Platform.runLater(() -> {
            webView.getEngine().executeScript("setTheme(" + finalArg + ");");
        });
    }

    @Override
    public void runWhenReady(Runnable runnable) {
//        if(isEditorInitialized) {
//            runnable.run();
//        } else {
            queue.add(runnable);
            handleQueue();
//        }
    }

    private void handleQueue() {
        if(isEditorInitialized) {
            while(!queue.isEmpty()) {
                Runnable runnable = queue.remove();
                Platform.runLater(runnable);
            }
        }
    }
}

@@ -79,90 +79,17 @@
 afx.
-event.EventHandler;%0Aimport javafx.scene.Parent;%0Aimport javafx.scene.web.WebErrorEv
+scene.Par
 ent;
@@ -202,16 +202,66 @@
 rrent.*;
+%0Aimport java.util.concurrent.atomic.AtomicBoolean;
 %0A%0Apublic
@@ -355,17 +355,23 @@
 private 
-b
+AtomicB
 oolean i
@@ -395,38 +395,33 @@
 d = 
-false; //TODO make atomic var?
+new AtomicBoolean(false);
 %0A   
@@ -864,69 +864,8 @@
 ());
- //TODO maybe use .toURI.toString() instead of toExternalFOrm
 %0A   
@@ -991,135 +991,18 @@
 ror(
-new EventHandler%3CWebErrorEvent%3E() %7B%0A            @Override%0A            public void handle(WebErrorEvent event) %7B%0A               
+event -%3E %7B
  thr
@@ -1047,38 +1047,16 @@
 tion());
-%0A            %7D%0A       
  %7D);%0A%0A  
@@ -1876,15 +1876,18 @@
 ized
- = 
+.set(
 true
+)
 ;%0A  
@@ -1948,14 +1948,12 @@
     
- ;
  //
-D
+d
 o no
@@ -3166,16 +3166,22 @@
 tialized
+.get()
 ;%0A    %7D%0A
@@ -5142,24 +5142,30 @@
 rInitialized
+.get()
 ) %7B%0A        

clean up