package org.bunkr.gui.components.tabs.markdown;

import javafx.concurrent.Worker;
import javafx.scene.web.WebView;
import org.bunkr.core.Resources;
import org.bunkr.core.inventory.InventoryPather;
import org.bunkr.gui.dialogs.QuickDialogs;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NodeList;
import org.w3c.dom.events.*;
import org.w3c.dom.events.Event;

import java.awt.*;
import java.io.IOException;
import java.net.*;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;
import java.util.function.Consumer;

/**
 * Creator: benmeier
 * Created At: 2015-12-31
 */
public class MarkdownWebViewAdapter
{
    private static final Set<String> ALLOWED_OUTBOUND_SCHEMA = new HashSet<>(Arrays.asList(
            "http", "https", "ftp", ""
    ));

    private static final String githubCSSPath = "/resources/css/github_css.css";

    private Consumer<String> onTryOpenFileItem;

    public void adapt(WebView subject) throws IOException
    {
        // load external style sheet into webview
        subject.getEngine().setUserStyleSheetLocation(Resources.getExternalPath(githubCSSPath));

        // disable existing context menu
        subject.setContextMenuEnabled(false);

        // disable javascript
        subject.getEngine().setJavaScriptEnabled(false);

        // need to bind the page load event in order to process link things
        subject.getEngine().getLoadWorker().stateProperty().addListener((observable, oldValue, newValue) -> {
            // do this when the page load has finished
            if (newValue == Worker.State.SUCCEEDED)
            {
                this.adaptLoadedPage(subject);
            }
        });

        subject.getEngine().setOnError(event -> QuickDialogs.exception(event.getException()));
    }

    private void adaptLoadedPage(WebView subject)
    {
        // get the document
        Document doc = subject.getEngine().getDocument();

        // loop through all link tags
        NodeList linkNodes = doc.getElementsByTagName("a");
        for (int i = 0; i < linkNodes.getLength(); i++)
        {
            Element element = (Element) linkNodes.item(i);
            // remove on click
            element.setAttribute("onclick", "");
            ((EventTarget) element).addEventListener("click", this::handleLinkOnClick, false);
        }

        // loop through all link tags
        NodeList imgNodes = doc.getElementsByTagName("img");
        for (int i = 0; i < imgNodes.getLength(); i++)
        {
            Element element = (Element) imgNodes.item(i);
            ((EventTarget) element).addEventListener("click", this::handleImgOnClick, false);
        }
    }

    private void handleImgOnClick(Event event)
    {
        // get the element that was clicked
        Element element = (Element) event.getTarget();

        // if the location target has an href
        if (element.hasAttribute("src"))
        {
            if(QuickDialogs.confirm("Outgoing image link to '%s'. Do you want to open it in a browser?", element.getAttribute("src")))
            {
                Desktop desktop = Desktop.isDesktopSupported() ? Desktop.getDesktop() : null;
                if (desktop != null && desktop.isSupported(Desktop.Action.BROWSE))
                {
                    try
                    {
                        desktop.browse(new URI(element.getAttribute("src")));
                    }
                    catch (Exception e)
                    {
                        QuickDialogs.error("Failed to open external browser: %s", e);
                    }
                }
            }
        }
    }

    private void handleLinkOnClick(Event ev)
    {
        // get the element that was clicked
        Element element = (Element) ev.getTarget();

        // if the location target has an href
        if (element.hasAttribute("href") && (! element.getAttribute("href").startsWith("#")))
        {
            ev.preventDefault();
            try
            {
                URI uri = new URI(element.getAttribute("href"));
                if (uri.getScheme() == null)
                {

                    String decodedPath = uri.getPath();
                    if (InventoryPather.isValidPath(decodedPath))
                    {
                        this.onTryOpenFileItem.accept(decodedPath);
                    }
                    else if (InventoryPather.isValidRelativePath(decodedPath))
                    {
                        this.onTryOpenFileItem.accept(decodedPath);
                    }
                }
                else if (ALLOWED_OUTBOUND_SCHEMA.contains(uri.getScheme().toLowerCase()))
                {
                    if(QuickDialogs.confirm("Outgoing link to '%s'. Do you want to open it in a browser?", uri))
                    {
                        Desktop desktop = Desktop.isDesktopSupported() ? Desktop.getDesktop() : null;
                        if (desktop != null && desktop.isSupported(Desktop.Action.BROWSE))
                        {
                            try
                            {
                                desktop.browse(uri);
                            }
                            catch (Exception e)
                            {
                                QuickDialogs.error("Failed to open external browser.");
                            }
                        }
                    }
                }
                else
                {
                    QuickDialogs.error("Error", null, "Outgoing link '%s' is using an unknown scheme %s", uri, uri.getScheme());
                }
            }
            catch (URISyntaxException e)
            {
                QuickDialogs.error("Error", null, "Outgoing link %s was not a valid uri", element.getAttribute("href"));
            }
        }
    }

    public void setOnTryOpenFileItem(Consumer<String> onTryOpenFileItem)
    {
        this.onTryOpenFileItem = onTryOpenFileItem;
    }
}

@@ -2304,32 +2304,65 @@
 ck%22,
+ evt -%3E
  this
-::
+.
 handle
-LinkOnClick
+ElementLinkAttributeClick(evt, %22href%22)
 , fa
@@ -2665,31 +2665,64 @@
 ck%22,
+ evt -%3E
  this
-::
+.
 handle
-ImgOnClick
+ElementLinkAttributeClick(evt, %22src%22)
 , fa
@@ -2771,987 +2771,63 @@
 ndle
-ImgOnClick(Event event)%0A    %7B%0A        // get the element that was clicked%0A        Element element = (Element) event.getTarget();%0A%0A        // if the location target has an href%0A        if (element.hasAttribute(%22src%22))%0A        %7B%0A            if(QuickDialogs.confirm(%22Outgoing image link to '%25s'. Do you want to open it in a browser?%22, element.getAttribute(%22src%22)))%0A            %7B%0A                Desktop desktop = Desktop.isDesktopSupported() ? Desktop.getDesktop() : null;%0A                if (desktop != null && desktop.isSupported(Desktop.Action.BROWSE))%0A                %7B%0A                    try%0A                    %7B%0A                        desktop.browse(new URI(element.getAttribute(%22src%22)));%0A                    %7D%0A                    catch (Exception e)%0A                    %7B%0A                        QuickDialogs.error(%22Failed to open external browser: %25s%22, e);%0A                    %7D%0A                %7D%0A            %7D%0A        %7D%0A    %7D%0A%0A    private void handleLinkOnClick(Event ev
+ElementLinkAttributeClick(Event event, String attribute
 )%0A  
@@ -2912,16 +2912,19 @@
 ment) ev
+ent
 .getTarg
@@ -3013,20 +3013,22 @@
 ute(
-%22href%22
+attribute
 ) && 
-(
 ! el
@@ -3046,22 +3046,25 @@
 tribute(
-%22href%22
+attribute
 ).starts
@@ -3073,17 +3073,16 @@
 th(%22#%22))
-)
 %0A       
@@ -3098,16 +3098,19 @@
       ev
+ent
 .prevent
@@ -3197,38 +3197,41 @@
 nt.getAttribute(
-%22href%22
+attribute
 ));%0A            
@@ -3281,17 +3281,16 @@
       %7B%0A
-%0A
         
@@ -4724,20 +4724,26 @@
 n un
-known scheme
+supported protocol
  %25s%22
@@ -4965,22 +4965,25 @@
 tribute(
-%22href%22
+attribute
 ));%0A    

allow in-archive image links