package ji9079541;

import javafx.application.Platform;
import javafx.event.EventHandler;
import javafx.scene.web.WebEngine;
import javafx.scene.web.WebErrorEvent;
import javafx.scene.web.WebView;
import netscape.javascript.JSObject;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NodeList;
import org.w3c.dom.events.Event;
import org.w3c.dom.events.EventListener;
import org.w3c.dom.events.EventTarget;

import javax.swing.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.IOException;
import java.time.Duration;
import java.time.Instant;
import java.util.concurrent.*;
import java.util.logging.Logger;

public class Demo3 {

    private static String HTML = """
            <!doctype html>
            <html>
            <head>
                <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
            </head>
            <body style="padding:10px;">
            <span style="font-weight:bold;">Die Meldung</span>
            <span style="font-weight:bold;color:Blue;">${umsEventText}</span>
            <br>
            <label>Ja<input type="checkbox" name="boxyes" id="boxyes" data-ums-is-defeasible="false"></label>
            <script>
                document.getElementById("boxyes").addEventListener("change", function () {
            
                    host.journalize("yep");
                });
            
            </script>
            </body>
            </html>
            """;

    private static final Logger log = Logger.getLogger(Demo3.class.getName());
    private static MyHost hardReferenceForHost;
    private static CompletableFuture<Void> loadSucceed;
    private static CompletableFuture<Void> loadSchedule;


    public static void main(String[] args) throws IOException {
        log.info("starting");
        Platform.startup(new Runnable() {
            @Override
            public void run() {
            }
        });
        final var timer = new Timer(1000, new ActionListener() {
            @Override
            public void actionPerformed(final ActionEvent e) {
                final var now = Instant.now();
                Platform.runLater(new Runnable() {
                    @Override
                    public void run() {
                        final var between = Duration.between(now, Instant.now());
                        log.info("between = " + between);
                        if (between.toSeconds() > 10) {
                            System.exit(1);
                        }
                    }
                });
            }
        });
        timer.setRepeats(true);
        timer.start();
        var executor = Executors.newSingleThreadScheduledExecutor();
        //we cant wait for the document to be loaded because thats broken as well, see the other bug report
        executor.scheduleWithFixedDelay(new Runnable() {
            @Override
            public void run() {
                loadSucceed = new CompletableFuture<>();
                loadSchedule = new CompletableFuture<>();
                load();
                try {
                    loadSchedule.get();
                    loadSucceed.get(1, TimeUnit.SECONDS);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                } catch (ExecutionException e) {
                    throw new RuntimeException(e);
                } catch (TimeoutException e) {
                    //we ran into the other bug, loads sometimes hang for no reason
                    log.warning("timed out waiting for load to succeed");
                }
            }
        }, 10, 10, TimeUnit.MILLISECONDS);

    }

    private static void load() {
        Platform.runLater(new Runnable() {
            @Override
            public void run() {
                aLoad();
            }
        });
    }

    static void addEventListener(final Document document, final EventListener listener) {
        if (document == null) {
            return;
        }
        final EventTarget eventTarget = (EventTarget) document;
        eventTarget.addEventListener("input", listener, true);
        eventTarget.addEventListener("change", listener, true);
        eventTarget.addEventListener("customevent", listener, true);
        eventTarget.addEventListener("click", listener, true);
    }

    public static void aLoad() {
        final var webView = new WebView();
        final WebEngine engine = webView.getEngine();
        webView.getEngine().getHistory().setMaxSize(0);
        webView.setContextMenuEnabled(false);
        webView.getEngine().documentProperty().addListener((obs, oldDoc, newDoc) -> {
            log.fine("load suceeded hurraaa = ");
            loadSucceed.complete(null);
            stuff(engine, webView);
        });
        engine.loadContent(HTML);
        log.fine(" expect load ");
        loadSchedule.complete(null);
    }

    public static class MyHost {
        public void processQ2() {
            log.fine("Callback through javascript");
        }

        public void journalize(String message) {
            log.fine("message = " + message);
        }
    }

    static void stuff(WebEngine engine, WebView webView) {
        final Document document = engine.getDocument();
        final JSObject jsObject = (JSObject) engine.executeScript("window");
        final NodeList bodyList = engine.getDocument().getElementsByTagName("body");
        final Element element = (Element) bodyList.item(0);
        hardReferenceForHost = new MyHost();
        //because setMember uses a weak reference
        jsObject.setMember("host", hardReferenceForHost);
        jsObject.setMember("justToConfuseYou", new Object() {
        });
        engine.executeScript("window.host.processQ2();");
        addEventListener(document, new EventListener() {

            @Override
            public void handleEvent(final Event evt) {
                Platform.runLater(() -> {
                    // System.out.println("evt = " + evt);
                });
            }
        });
        engine.setOnError(new EventHandler<WebErrorEvent>() {
            @Override
            public void handle(final WebErrorEvent webErrorEvent) {
                log.warning("Error: " + webErrorEvent.getMessage());
            }
        });
        log.fine(" expect callback through javascript ");
        webView.getEngine().executeScript("var evt = document.createEvent(\"MouseEvents\");\n" +
                " evt.initMouseEvent(\"click\", true, true, window, 1, 0, 0, 0, 0,\n" +
                " false, false, false, false, 0, null);\n" +
                "\n" +
                " var cb = document.getElementById(\"boxyes\");\n" +
                " cb.dispatchEvent(evt);");
    }
} 