/*
 * WANDORA
 * Knowledge Extraction, Management, and Publishing Application
 * http://wandora.org
 *
 * Copyright (C) 2004-2023 Wandora Team
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 *
 *
 */


package org.wandora.application.gui.topicpanels.webview;

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Component;
import java.awt.Desktop;
import java.awt.Dimension;
import java.awt.Image;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.ComponentEvent;
import java.awt.event.ComponentListener;
import java.awt.event.MouseWheelEvent;
import java.awt.image.BufferedImage;
import java.io.StringWriter;
import java.net.MalformedURLException;
import java.net.URI;
import java.net.URL;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.Hashtable;
import java.util.List;
import java.util.Map;

import javax.swing.JPopupMenu;
import javax.swing.SwingUtilities;
import javax.xml.transform.OutputKeys;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;

import org.apache.commons.io.IOUtils;
import org.w3c.dom.Document;
import org.wandora.application.CancelledException;
import org.wandora.application.RefreshListener;
import org.wandora.application.Wandora;
import org.wandora.application.gui.UIBox;
import org.wandora.application.gui.WandoraOptionPane;
import org.wandora.application.gui.simple.SimpleButton;
import org.wandora.application.gui.simple.SimpleField;
import org.wandora.application.gui.topicstringify.TopicToString;
import org.wandora.application.modulesserver.ModulesWebApp;
import org.wandora.application.modulesserver.WandoraModulesServer;
import org.wandora.application.tools.browserextractors.BrowserExtractRequest;
import org.wandora.application.tools.browserextractors.BrowserExtractorManager;
import org.wandora.application.tools.server.HTTPServerTool;
import org.wandora.application.tools.webview.AddWebLocationAsOccurrence;
import org.wandora.application.tools.webview.AddWebLocationAsSubjectIdentifier;
import org.wandora.application.tools.webview.AddWebLocationAsSubjectLocator;
import org.wandora.application.tools.webview.AddWebSelectionAsBasename;
import org.wandora.application.tools.webview.AddWebSelectionAsOccurrence;
import org.wandora.application.tools.webview.AddWebSourceAsOccurrence;
import org.wandora.application.tools.webview.CreateWebLocationTopic;
import org.wandora.application.tools.webview.OpenFirebugInWebView;
import org.wandora.application.tools.webview.OpenOccurrenceInWebView;
import org.wandora.application.tools.webview.OpenWebLocationInExternalBrowser;
import org.wandora.topicmap.Association;
import org.wandora.topicmap.Locator;
import org.wandora.topicmap.Topic;
import org.wandora.topicmap.TopicMap;
import org.wandora.topicmap.TopicMapException;
import org.wandora.topicmap.TopicMapListener;
import org.wandora.utils.Options;
import org.wandora.utils.Tuples.T3;

import javafx.application.Platform;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.concurrent.Worker;
import javafx.concurrent.Worker.State;
import javafx.embed.swing.JFXPanel;
import javafx.embed.swing.SwingFXUtils;
import javafx.event.EventHandler;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.scene.image.WritableImage;
import javafx.scene.web.PopupFeatures;
import javafx.scene.web.PromptData;
import javafx.scene.web.WebEngine;
import javafx.scene.web.WebEvent;
import javafx.scene.web.WebView;
import javafx.util.Callback;
import netscape.javascript.JSObject;

/**
 *
 * @author akivela
 */


public class WebViewPanel extends javax.swing.JPanel implements TopicMapListener, RefreshListener, ActionListener, ComponentListener {


	private static final long serialVersionUID = 1L;
	
	private static final String JAVASCRIPT_RESOURCE_GET_SELECTED_SOURCE = "js/GetSelectionHTML.js";
    private static final String JAVASCRIPT_RESOURCE_GET_SOURCE_WITH_SELECTION_INDEXES = "js/GetSourceWithSelectionIndexes.js";
    
    public static String javaFXVersion = "";
    public static int javaFXVersionInt = 0;
    
    public boolean USE_LOCAL_OPTIONS = true;
    
    private String title = null;
    private Topic rootTopic = null;
    private TopicMap tm = null;
    private boolean isUIInitialized = false;
    private Options options = null;

    private Component fxPanelHandle = null;
    private WebView webView = null;
    private WebEngine webEngine = null;
    private String webSource = null;
    
    private boolean informPopupBlocking = true;
    private boolean informVisibilityChanges = true;
    
    private BrowserExtractorManager browserExtractorManager = null;
    
    private boolean viewBrowser = true;
    
    private static final String failedToOpenMessage = "<h1>Failed to open URL</h1>";
    
    private ModulesWebApp selectedWebApp=null;
    private static final Color WEBAPP_ACTIVE_COLOR = new Color(243,243,243);
    private static final Color WEBAPP_PASSIVE_COLOR = Color.WHITE;
    
    
    
    
    public class WandoraJFXPanel extends JFXPanel {


		private static final long serialVersionUID = 1L;

		// EmbeddedScene.mouseEvent calls it's listeners with 40x wheel rotation 
        // multiplier -> mouseDelta in JS is 4800 instead of 120. This screws up
        // the zoom behavior in D3 powered visualizations.
        //
        // The Bug is in D3! Wandora uses a slightly modified version of D3 Javascript
        // library with fixed wheel multiplier.
        @Override
        protected void processMouseWheelEvent(MouseWheelEvent e) {
            MouseWheelEvent ee = new MouseWheelEvent(
                    (Component) e.getSource(), e.getID(), e.getWhen(),
                    e.getModifiers(), e.getX(), e.getY(), e.getXOnScreen(),
                    e.getYOnScreen(), e.getClickCount(),
                    e.isPopupTrigger(), e.getScrollType(), e.getScrollAmount(),
                    e.getWheelRotation(), e.getPreciseWheelRotation());
            super.processMouseWheelEvent(ee);
        }
    }
    
    
    /**
     * Creates new form WebViewPanel
     */
    public WebViewPanel() {
        try {
            browserExtractorManager = new BrowserExtractorManager(Wandora.getWandora());
        }
        catch(Exception e) {
        	e.printStackTrace();
        }
    }
    
    

    /**
     * This method is called from within the constructor to initialize the form.
     * WARNING: Do NOT modify this code. The content of this method is always
     * regenerated by the Form Editor.
     */
    @SuppressWarnings("unchecked")
    // <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
    private void initComponents() {
        java.awt.GridBagConstraints gridBagConstraints;

        buttonPanel = new javax.swing.JPanel();
        backButton = new SimpleButton();
        forwardButton = new SimpleButton();
        reloadButton = new SimpleButton();
        stopButton = new SimpleButton();
        urlTextField = new SimpleField();
        menuButton = new SimpleButton();

        setMinimumSize(new java.awt.Dimension(20, 20));
        setPreferredSize(new java.awt.Dimension(20, 20));
        setLayout(new java.awt.BorderLayout());

        buttonPanel.setBackground(new java.awt.Color(238, 238, 238));
        buttonPanel.setBorder(javax.swing.BorderFactory.createLineBorder(new java.awt.Color(204, 204, 204)));
        buttonPanel.setLayout(new java.awt.GridBagLayout());

        backButton.setText("<");
        backButton.setToolTipText("Go back one page");
        backButton.setBorder(null);
        backButton.setBorderPainted(false);
        backButton.setContentAreaFilled(false);
        backButton.setMargin(new java.awt.Insets(0, 0, 0, 0));
        backButton.setPreferredSize(new java.awt.Dimension(24, 24));
        backButton.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                backButtonActionPerformed(evt);
            }
        });
        gridBagConstraints = new java.awt.GridBagConstraints();
        gridBagConstraints.insets = new java.awt.Insets(4, 4, 4, 4);
        buttonPanel.add(backButton, gridBagConstraints);

        forwardButton.setText(">");
        forwardButton.setToolTipText("Go forward one page");
        forwardButton.setBorder(null);
        forwardButton.setBorderPainted(false);
        forwardButton.setContentAreaFilled(false);
        forwardButton.setMargin(new java.awt.Insets(0, 0, 0, 0));
        forwardButton.setPreferredSize(new java.awt.Dimension(24, 24));
        forwardButton.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                forwardButtonActionPerformed(evt);
            }
        });
        gridBagConstraints = new java.awt.GridBagConstraints();
        gridBagConstraints.insets = new java.awt.Insets(4, 0, 4, 0);
        buttonPanel.add(forwardButton, gridBagConstraints);

        reloadButton.setText("R");
        reloadButton.setToolTipText("Reload current page");
        reloadButton.setBorderPainted(false);
        reloadButton.setContentAreaFilled(false);
        reloadButton.setMargin(new java.awt.Insets(0, 0, 0, 0));
        reloadButton.setPreferredSize(new java.awt.Dimension(24, 24));
        reloadButton.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                reloadButtonActionPerformed(evt);
            }
        });
        gridBagConstraints = new java.awt.GridBagConstraints();
        gridBagConstraints.insets = new java.awt.Insets(4, 4, 4, 4);
        buttonPanel.add(reloadButton, gridBagConstraints);

        stopButton.setText("S");
        stopButton.setToolTipText("Stop and close current page");
        stopButton.setBorderPainted(false);
        stopButton.setContentAreaFilled(false);
        stopButton.setMargin(new java.awt.Insets(0, 0, 0, 0));
        stopButton.setPreferredSize(new java.awt.Dimension(24, 24));
        stopButton.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                stopButtonActionPerformed(evt);
            }
        });
        buttonPanel.add(stopButton, new java.awt.GridBagConstraints());

        urlTextField.setMargin(new java.awt.Insets(2, 4, 2, 4));
        urlTextField.setPreferredSize(new java.awt.Dimension(6, 24));
        urlTextField.addKeyListener(new java.awt.event.KeyAdapter() {
            public void keyPressed(java.awt.event.KeyEvent evt) {
                urlTextFieldKeyPressed(evt);
            }
        });
        gridBagConstraints = new java.awt.GridBagConstraints();
        gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL;
        gridBagConstraints.weightx = 1.0;
        gridBagConstraints.insets = new java.awt.Insets(4, 4, 4, 4);
        buttonPanel.add(urlTextField, gridBagConstraints);

        menuButton.setText("=");
        menuButton.setToolTipText("More Webview options and tools");
        menuButton.setBorderPainted(false);
        menuButton.setContentAreaFilled(false);
        menuButton.setMargin(new java.awt.Insets(0, 0, 0, 0));
        menuButton.setPreferredSize(new java.awt.Dimension(25, 25));
        menuButton.addMouseListener(new java.awt.event.MouseAdapter() {
            public void mousePressed(java.awt.event.MouseEvent evt) {
                menuButtonMousePressed(evt);
            }
        });
        gridBagConstraints = new java.awt.GridBagConstraints();
        gridBagConstraints.insets = new java.awt.Insets(4, 0, 4, 4);
        buttonPanel.add(menuButton, gridBagConstraints);

        add(buttonPanel, java.awt.BorderLayout.NORTH);
    }// </editor-fold>//GEN-END:initComponents

    private void urlTextFieldKeyPressed(java.awt.event.KeyEvent evt) {//GEN-FIRST:event_urlTextFieldKeyPressed
        //System.out.println("evt.getKeyCode() == "+evt.getKeyCode());
        if(webEngine != null && evt.getKeyCode() == 10) {
            final String u = toURL(urlTextField.getText());
            Platform.runLater(new Runnable() {
                @Override
                public void run() {
                    urlTextField.setBackground(Color.WHITE);
                    webEngine.load(u);
                }
            });
        }
    }//GEN-LAST:event_urlTextFieldKeyPressed

    private void backButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_backButtonActionPerformed
        if(webEngine != null) {
            Platform.runLater(new Runnable() {
                @Override
                public void run() {
                    webEngine.executeScript("history.back()");
                }
            });
        }
    }//GEN-LAST:event_backButtonActionPerformed

    private void forwardButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_forwardButtonActionPerformed
        if(webEngine != null) {
            Platform.runLater(new Runnable() {
                @Override
                public void run() {
                    webEngine.executeScript("history.forward()");
                }
            }); 
        }
    }//GEN-LAST:event_forwardButtonActionPerformed

    
    
    private void menuButtonMousePressed(java.awt.event.MouseEvent evt) {//GEN-FIRST:event_menuButtonMousePressed
        Object[] menuItems = getBrowserMenuStruct();
        if(menuItems != null && menuItems.length > 0) {
            JPopupMenu popupMenu = UIBox.makePopupMenu(menuItems, this);
            popupMenu.show(this, menuButton.getX()+evt.getX(), menuButton.getY()+evt.getY());
        }
    }//GEN-LAST:event_menuButtonMousePressed

    
    
    private void reloadButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_reloadButtonActionPerformed
        if(webEngine != null) {
            Platform.runLater(new Runnable() {
                @Override
                public void run() {
                    webEngine.reload();
                }
            }); 
        }
    }//GEN-LAST:event_reloadButtonActionPerformed

    private void stopButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_stopButtonActionPerformed
        selectedWebApp = null;
        browse((String) null);
    }//GEN-LAST:event_stopButtonActionPerformed

    
    
    
    // Variables declaration - do not modify//GEN-BEGIN:variables
    private javax.swing.JButton backButton;
    private javax.swing.JPanel buttonPanel;
    private javax.swing.JButton forwardButton;
    private javax.swing.JButton menuButton;
    private javax.swing.JButton reloadButton;
    private javax.swing.JButton stopButton;
    private javax.swing.JTextField urlTextField;
    // End of variables declaration//GEN-END:variables

    
    
    // -------------------------------------------------------------------------
    
    
    
    public WebEngine getWebEngine() {
        return this.webEngine;
    }
    
    
    
    
    public String getWebLocation() {
        return urlTextField.getText();
        //return webEngine.getLocation();
    }
    
    
    
    private Object[] getBrowserMenuStruct() {
        List<Object> topicMenuItems = new ArrayList<>();
        if(rootTopic != null) {
            try {
                if(rootTopic.getSubjectLocator() != null) {
                    topicMenuItems.add(rootTopic.getSubjectLocator().toExternalForm());
                    topicMenuItems.add("---");
                }
                for(Locator l : rootTopic.getSubjectIdentifiers()) {
                    topicMenuItems.add(l.toExternalForm());
                }
                boolean firstOccurrence = true;
                for(Topic occurrenceType : rootTopic.getDataTypes()) {
                    Hashtable<Topic,String> scopedOccurrences = rootTopic.getData(occurrenceType);
                    for(Topic occurrenceScope : scopedOccurrences.keySet()) {
                        if(firstOccurrence) {
                            topicMenuItems.add("---");
                            firstOccurrence = false;
                        }
                        topicMenuItems.add("Open occurrence "+TopicToString.toString(occurrenceType)+" - "+TopicToString.toString(occurrenceScope));
                        topicMenuItems.add(new OpenOccurrenceInWebView(rootTopic, occurrenceType, occurrenceScope));
                    }
                }
            }
            catch(Exception e) {
                e.printStackTrace();
            }
        }
        
        
        List<Object> browseServices = new ArrayList<>();
        Wandora wandora = Wandora.getWandora();
        if(wandora != null) {
            WandoraModulesServer httpServer = wandora.httpServer;
            
            List<ModulesWebApp> webApps=httpServer.getWebApps();
            Map<String,ModulesWebApp> webAppsMap=new HashMap<>();
            for(ModulesWebApp wa : webApps) webAppsMap.put(wa.getAppName(), wa);
            List<String> sorted = new ArrayList<>(webAppsMap.keySet());
            Collections.sort(sorted);

            for(String appName : sorted) {
                ModulesWebApp wa=webAppsMap.get(appName);
                
                if(wa.isRunning()) {
                     String url=wa.getAppStartPage();
                     if(url==null) continue;

                     browseServices.add(appName);
                     browseServices.add(UIBox.getIcon("gui/icons/open_browser.png"));
                     browseServices.add(new HTTPServerTool(HTTPServerTool.OPEN_PAGE_IN_BROWSER_TOPIC_PANEL, wa, this));
                }
                else {
                     browseServices.add(appName);
                     browseServices.add(UIBox.getIcon("gui/icons/open_browser.png"));
                     browseServices.add(new HTTPServerTool(HTTPServerTool.OPEN_PAGE_IN_BROWSER_TOPIC_PANEL, wa, this));
                }
            }
        }
        
        List<Object> extractors = new ArrayList<>();
        if(browserExtractorManager != null) {
            T3<String, Integer, Integer> contentWithSelectionIndexes = getSourceWithSelectionIndexes();
            String content = contentWithSelectionIndexes.e1;
            int start = contentWithSelectionIndexes.e2.intValue();
            int end = contentWithSelectionIndexes.e3.intValue();
            String selection = null;
            try {
                if(start != -1 && end != -1) {
                    selection = content.substring(start,end);
                }
            }
            catch(Exception e) {}
            BrowserExtractRequest extractRequest=new BrowserExtractRequest(getWebLocation(), content, null, "WebView", start, end, selection);
            String[] browserExtractors = browserExtractorManager.getExtractionMethods(extractRequest);

            for(String browserExtractorName : browserExtractors) {
                extractors.add(browserExtractorName);
                extractors.add((ActionListener) this);
            }
        }
        
        Object[] menuStruct = new Object[] {
            "Open current topic",
                topicMenuItems.toArray(),
            "Open Wandora's services",
                browseServices.toArray(),
            "---",
            "Open Firebug", new OpenFirebugInWebView(),
            "Open in external browser", new OpenWebLocationInExternalBrowser(),
            "---",
            "Add to current topic",
            new Object[] {
                "Add selection as an occurrence...", new AddWebSelectionAsOccurrence(),
                "Add selection source as an occurrence...", new AddWebSourceAsOccurrence(true),
                "Add source as an occurrence...", new AddWebSourceAsOccurrence(),
                "Add location as an occurrence...", new AddWebLocationAsOccurrence(),
                "---",
                "Add location as a subject locator", new AddWebLocationAsSubjectLocator(),
                "Add location as a subject identifier", new AddWebLocationAsSubjectIdentifier(),
                "---",
                "Add selection as a basename", new AddWebSelectionAsBasename(),
                // "---",
                // "Add links as associations",
                // "Add image locations as associations",
            },
            "Create a topic",
            new Object[] {
                "Create topic from location", new CreateWebLocationTopic(),
                "---",
                "Create topic from location and make instance", new CreateWebLocationTopic(true,false,true,false),
                "Create topic from location and make subclass", new CreateWebLocationTopic(false,true,true,false),
                "Create topic from location and associate", new CreateWebLocationTopic(false,false,true,true),
            },
            "---",
            "Extract",
            extractors.toArray()
        };
        

        return menuStruct;
    }
    
    
    
    
    
    // -------------------------------------------------------------------------
    
    
    private static String toURL(String str) {
        try {
            return new URL(str).toExternalForm();
        } 
        catch (MalformedURLException exception) {
            if(!str.startsWith("http://")) {
                return "http://"+str;
            }
            else {
                return null;
            }
        }
    }
    
    
    
    
    // ------------------------------------------------------ topic listener ---
    
    
    @Override
    public void topicSubjectIdentifierChanged(Topic t, Locator added, Locator removed) throws TopicMapException {
        doRefresh();
    }

    @Override
    public void topicBaseNameChanged(Topic t, String newName, String oldName) throws TopicMapException {
        doRefresh();
    }

    @Override
    public void topicTypeChanged(Topic t, Topic added, Topic removed) throws TopicMapException {
        doRefresh();
    }

    @Override
    public void topicVariantChanged(Topic t, Collection<Topic> scope, String newName, String oldName) throws TopicMapException {
        doRefresh();
    }

    @Override
    public void topicDataChanged(Topic t, Topic type, Topic version, String newValue, String oldValue) throws TopicMapException {
        doRefresh();
    }

    @Override
    public void topicSubjectLocatorChanged(Topic t, Locator newLocator, Locator oldLocator) throws TopicMapException {
        doRefresh();
    }

    @Override
    public void topicRemoved(Topic t) throws TopicMapException {
        doRefresh();
    }

    @Override
    public void topicChanged(Topic t) throws TopicMapException {
        doRefresh();
    }

    @Override
    public void associationTypeChanged(Association a, Topic newType, Topic oldType) throws TopicMapException {
        doRefresh();
    }

    @Override
    public void associationPlayerChanged(Association a, Topic role, Topic newPlayer, Topic oldPlayer) throws TopicMapException {
        doRefresh();
    }

    @Override
    public void associationRemoved(Association a) throws TopicMapException {
        doRefresh();
    }

    @Override
    public void associationChanged(Association a) throws TopicMapException {
        doRefresh();
    }

    
    // -------------------------------------------------------------------------
    
    
    @Override
    public void doRefresh() throws TopicMapException {
        
    }

    
    public void open(Topic topic) throws TopicMapException {
        try {
            rootTopic = topic;
            if(!isUIInitialized) {
                isUIInitialized = true;
                initializeUI();
            }
            else {
                browse(rootTopic);
            }
        }
        catch(Exception e) {
            e.printStackTrace();
        }
    }

    
    
    public void openContent(final String str) {
        try {
            if(str != null) {
                if(!isUIInitialized) {
                    isUIInitialized = true;
                    initializeUI();
                }
                Platform.runLater(new Runnable() {
                    @Override
                    public void run() {
                        webEngine.loadContent(str);
                    }
                });
            }
        }
        catch(Exception e) {
            e.printStackTrace();
        }
    }
    
    
    
    
    private void initializeUI() {
        Wandora wandora = Wandora.getWandora();
        if(options == null) {
            if(USE_LOCAL_OPTIONS) {
                options = new Options(wandora.getOptions());
            }
            else {
                options = wandora.getOptions();
            }
        }
        tm = wandora.getTopicMap();
        initComponents();
        Platform.setImplicitExit(false);
        final WandoraJFXPanel fxPanel = new WandoraJFXPanel();
        fxPanelHandle = fxPanel;
        this.add(fxPanel, BorderLayout.CENTER);
        Platform.runLater(new Runnable() {
            @Override
            public void run() {
                initFX(fxPanel);
                browse(rootTopic);
            }
        });
        this.addComponentListener(this);

        backButton.setText("");
        backButton.setIcon(UIBox.getIcon("gui/icons/webview/backward.png"));
        forwardButton.setText("");
        forwardButton.setIcon(UIBox.getIcon("gui/icons/webview/forward.png"));
        stopButton.setText("");
        stopButton.setIcon(UIBox.getIcon("gui/icons/webview/stop.png"));
        reloadButton.setText("");
        reloadButton.setIcon(UIBox.getIcon("gui/icons/webview/reload.png"));
        menuButton.setText("");
        menuButton.setIcon(UIBox.getIcon("gui/icons/webview/menu.png"));
    }
    
    
    
    
    
    public void browse(Topic topic) {
        try {
            String u = null;
            if(topic != null) {
                u = topic.getFirstSubjectIdentifier().toExternalForm();
                if(selectedWebApp != null) {
                    String url=selectedWebApp.getAppTopicPage(u);
                    if(url!=null) {
                        urlTextField.setBackground(WEBAPP_ACTIVE_COLOR);
                        browse(url, false);
                        return;
                    }
                }
            }
            browse(u);
        }
        catch(Exception e) {
            e.printStackTrace();
        }
    }
    
    
    
    public void browse(final String url) {
        browse(url, true);
    }
    
    
    
    public void browse(final String url, boolean resetWebApp) {
        if(resetWebApp && url != null) {
            selectedWebApp = null;
            WandoraModulesServer s=Wandora.getWandora().getHTTPServer();
            for(ModulesWebApp webApp : s.getWebApps()){
                if(url.equals(webApp.getAppStartPage())) {
                    urlTextField.setBackground(WEBAPP_ACTIVE_COLOR);
                    selectedWebApp = webApp;
                    break;
                }
            }
        }
        if(selectedWebApp == null) {
            urlTextField.setBackground(WEBAPP_PASSIVE_COLOR);
        }
        
        Platform.runLater(new Runnable() {
            @Override
            public void run() {
                webEngine.load(url);
            }
        });
    }
    
    public void executeScript(final String script) {
        Platform.runLater(new Runnable() {
            @Override
            public void run() {
                webEngine.executeScript(script);
            }
        });
    }
    
    
    private Object scriptReturn = null;
    public Object executeSynchronizedScript(final String script) {
        scriptReturn = null;
        if(script != null && script.length()>0) {
            Thread runner = new Thread() {
                @Override
                public void run() {
                    scriptReturn = webEngine.executeScript(script);
                }
            };
            Platform.runLater(runner);

            do {
                try {
                    Thread.sleep(100);
                }
                catch(Exception ex) {

                }
            }
            while(runner.isAlive());
        }
        return scriptReturn;
    }
    

    public Object executeSynchronizedScriptResource(String scriptResource) {
        try {
            String script = IOUtils.toString(this.getClass().getResourceAsStream(scriptResource),"UTF-8");
            return executeSynchronizedScript(script);
        }
        catch(Exception e) {
            e.printStackTrace();
        }
        return null;
    }
    
    
    
    private void startLoadingAnimation() {
        Wandora wandora = Wandora.getWandora();
        if(wandora != null) wandora.setAnimated(true, this);
    }
    private void stopLoadingAnimation() {
        Wandora wandora = Wandora.getWandora();
        if(wandora != null) wandora.setAnimated(false, this);
    }
    
    
    
    private void initFX(final JFXPanel fxPanel) {
        Group group = new Group();
        Scene scene = new Scene(group);
        fxPanel.setScene(scene);

        webView = new WebView();

        if(javaFXVersionInt >= 8) {
            webView.setScaleX(1.0);
            webView.setScaleY(1.0);
            //webView.setFitToHeight(false);
            //webView.setFitToWidth(false);
            //webView.setZoom(javafx.stage.Screen.getPrimary().getDpi() / 96);
        }

        group.getChildren().add(webView);
        
        int w = this.getWidth();
        int h = this.getHeight()-34;
        
        webView.setMinSize(w, h);
        webView.setMaxSize(w, h);
        webView.setPrefSize(w, h);

        // Obtain the webEngine to navigate
        webEngine = webView.getEngine();
        
        webEngine.locationProperty().addListener(new ChangeListener<String>() {
                @Override
                public void changed(ObservableValue<? extends String> observable, String oldValue, final String newValue) {
                    if (newValue.endsWith(".pdf")) {
                        try {
                            int a = WandoraOptionPane.showConfirmDialog(Wandora.getWandora(), "Open PDF document in external application?", "Open PDF document in external application?", WandoraOptionPane.YES_NO_OPTION);
                            if(a == WandoraOptionPane.YES_OPTION) {
                                Desktop dt = Desktop.getDesktop();
                                dt.browse(new URI(newValue));
                            }
                        }
                        catch(Exception e) {}
                    }
                    else {
                        SwingUtilities.invokeLater(new Runnable() {
                            @Override 
                            public void run() {
                                urlTextField.setText(newValue);
                            }
                        });
                    }
                }
        });
        webEngine.titleProperty().addListener(new ChangeListener<String>() {
                @Override
                public void changed(ObservableValue<? extends String> observable, String oldValue, final String newValue) {
                    SwingUtilities.invokeLater(new Runnable() {
                        @Override 
                        public void run() {
                            title = newValue;
                        }
                    });
                }
        });
        webEngine.setOnAlert(new EventHandler<WebEvent<java.lang.String>>() {
            @Override
            public void handle(WebEvent<String> t) {
                if(t != null) {
                    String str = t.getData();
                    if(str != null && str.length() > 0) {
                        WandoraOptionPane.showMessageDialog(Wandora.getWandora(), str, "Javascript Alert", WandoraOptionPane.PLAIN_MESSAGE);
                    }
                }
            }
        });
        webEngine.setConfirmHandler(new Callback<String, Boolean>() {
            @Override 
            public Boolean call(String msg) {
                int a = WandoraOptionPane.showConfirmDialog(Wandora.getWandora(), msg, "Javascript Alert", WandoraOptionPane.YES_NO_OPTION);
                return (a == WandoraOptionPane.YES_OPTION);
            }
        });
        webEngine.setPromptHandler(new Callback<PromptData, String>() {
            @Override 
            public String call(PromptData data) {
                String a = WandoraOptionPane.showInputDialog(Wandora.getWandora(), data.getMessage(), data.getDefaultValue(), "Javascript Alert", WandoraOptionPane.QUESTION_MESSAGE);
                return a;
            }
        });
        
        webEngine.setCreatePopupHandler(new Callback<PopupFeatures,WebEngine>() {
            @Override 
            public WebEngine call(PopupFeatures features) {
                if(informPopupBlocking) {
                    WandoraOptionPane.showMessageDialog(Wandora.getWandora(), 
                            "A javascript popup has been blocked. Wandora doesn't allow javascript popups in Webview topic panel.", 
                            "Javascript popup blocked", 
                            WandoraOptionPane.PLAIN_MESSAGE);
                }
                informPopupBlocking = false;
                return null;
            }
        });
        webEngine.setOnVisibilityChanged(new EventHandler<WebEvent<Boolean>>() {
            @Override
            public void handle(WebEvent<Boolean> t) {
                if(t != null) {
                    Boolean b = t.getData();
                    if(informVisibilityChanges) {
                        WandoraOptionPane.showMessageDialog(Wandora.getWandora(), 
                                "A browser window visibility change has been blocked. Wandora doesn't allow visibility changes of windows in Webview topic panel.", 
                                "Javascript visibility chnage blocked", 
                                WandoraOptionPane.PLAIN_MESSAGE);
                        informVisibilityChanges = false;
                    }
                }
            }
        });
        webEngine.getLoadWorker().stateProperty().addListener(
            new ChangeListener<State>() {
                @Override
                public void changed(ObservableValue ov, State oldState, State newState) {
                    if(newState == Worker.State.SCHEDULED) {
                        //System.out.println("Scheduled!");
                        startLoadingAnimation();
                    }
                    if(newState == Worker.State.SUCCEEDED) {
                        Document doc = webEngine.getDocument();
                        try {
                            Transformer transformer = TransformerFactory.newInstance().newTransformer();
                            //transformer.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "no");
                            transformer.setOutputProperty(OutputKeys.METHOD, "xml");
                            transformer.setOutputProperty(OutputKeys.INDENT, "yes");
                            transformer.setOutputProperty(OutputKeys.ENCODING, "UTF-8");
                            transformer.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", "4");

                            // transformer.transform(new DOMSource(doc), new StreamResult(new OutputStreamWriter(System.out, "UTF-8")));
                            
                            StringWriter stringWriter = new StringWriter();
                            transformer.transform(new DOMSource(doc), new StreamResult(stringWriter));
                            webSource = stringWriter.toString();
                        }
                        catch (Exception ex) {
                            ex.printStackTrace();
                        }
                        stopLoadingAnimation();
                    }
                    else if(newState == Worker.State.CANCELLED) {
                        //System.out.println("Cancelled!");
                        stopLoadingAnimation();
                    }
                    else if(newState == Worker.State.FAILED) {
                        webEngine.loadContent(failedToOpenMessage);
                        stopLoadingAnimation();
                    }
                }
            });
        
    }
    
    
    

    public void stop() {
        //System.out.println("---- Stopping Webview topic panel!");
        browse((String) null);
    }
    
    


    public void refresh() throws TopicMapException {
        
    }


    public boolean applyChanges() throws CancelledException, TopicMapException {
        return true;
    }

    

    public Topic getTopic() throws TopicMapException {
        return rootTopic;
    }

    

    public String getTitle() {
        if(rootTopic == null) {
            return "Webview";
        }
        else {
            return TopicToString.toString(rootTopic);
        }
    }






    @Override
    public void actionPerformed(ActionEvent e) {
        String cmd = e.getActionCommand();
        if(cmd != null) {
            if(cmd.startsWith("http://")) {
                browse(cmd);
            }
            else {
                Wandora w = Wandora.getWandora();
                if(w != null) w.setAnimated(true, this);
                T3<String, Integer, Integer> contentWithSelectionIndexes = getSourceWithSelectionIndexes();
                String content = contentWithSelectionIndexes.e1;
                int start = contentWithSelectionIndexes.e2.intValue();
                int end = contentWithSelectionIndexes.e3.intValue();
                String selection = null;
                try {
                    if(start != -1 && end != -1) {
                        selection = content.substring(start,end);
                    }
                }
                catch(Exception ex) {}
                BrowserExtractRequest extractRequest=new BrowserExtractRequest(getWebLocation(), content, cmd, "WebView", start, end, selection);
                String message=browserExtractorManager.doPluginExtract(extractRequest);
                if(message!=null) {
                    WandoraOptionPane.showMessageDialog(Wandora.getWandora(), message, cmd);
                }
                if(w != null) w.setAnimated(false, this);
            }
        }
    }
    
    
    
    public Image getSnapshot() {
        WritableImage image = webView.snapshot(null, null);
        BufferedImage bufferedImage = SwingFXUtils.fromFXImage(image, null);
        return bufferedImage;
    }
    
    
    
    public String getSelectedText() {
        //System.out.println("get selected text");
        String selection = (String) executeSynchronizedScript("window.getSelection().toString()");
        //System.out.println("  and the selection is "+selection);
        return selection;
    }
    
    
    public String getSelectedSource() {
        String selection = (String) executeSynchronizedScriptResource(JAVASCRIPT_RESOURCE_GET_SELECTED_SOURCE);
        //System.out.println("--------");
        //System.out.println(selection);
        //System.out.println("--------");
        return selection;
    }
    
    public T3<String,Integer,Integer> getSourceWithSelectionIndexes() {
        String content = null;
        int start = -1;
        int end = -1;
        try {
            JSObject d = (JSObject) executeSynchronizedScriptResource(JAVASCRIPT_RESOURCE_GET_SOURCE_WITH_SELECTION_INDEXES);

            if(d != null) {
                //System.out.println("========");
                //System.out.println(d.toString());
                //System.out.println("========");
                try {
                    content = (String) d.getMember("content");
                    start = (Integer) d.getMember("selectionStart");
                    end = (Integer) d.getMember("selectionEnd");
                } catch(Exception e) {}
            }
            //System.out.println("--------");
            //System.out.println(content);
            //if(start != -1 && end != -1) System.out.println(content.substring(start, end));
            //else System.out.println("ALL");
            //System.out.println("--------");
            //System.out.println("start: "+start);
            //System.out.println("end: "+end);
            //System.out.println("--------");
        }
        catch(Exception e) {
            e.printStackTrace();
        }
        return new T3<>(content, Integer.valueOf(start), Integer.valueOf(end));
    }
    
    
    public String getSource() {
        return webSource;
    }
    
    
    
    public String getWebTitle() {
        if(webEngine != null) { 
            return webEngine.getTitle();
        }
        else return null;
    }
    

    
    // -------------------------------------------------------------------------
    // --------------------------------------------------- ComponentListener ---
    // -------------------------------------------------------------------------
    
    
    @Override
    public void componentShown(ComponentEvent e) {
        handleComponentEvent(e);
    }

    @Override
    public void componentResized(ComponentEvent e) {
        handleComponentEvent(e);
    }

    @Override
    public void componentHidden(ComponentEvent e) {
        handleComponentEvent(e);
    }

    @Override
    public void componentMoved(ComponentEvent e) {
        handleComponentEvent(e);
    }
   
    
    private void handleComponentEvent(ComponentEvent e) {
        try {
            Component c = this;
            if(this.getParent() != null) c = this.getParent();
            
            final int w = c.getWidth();
            final int h = c.getHeight()-35;
            Dimension d = new Dimension(w, h);
            
            if(this.getParent() != null) {
                this.setPreferredSize(d);
                this.setMinimumSize(d);
                this.setMaximumSize(d);
            }
            

            if(webView != null && w > 1 && h > 1) {
                Platform.runLater(new Runnable() {
                    @Override
                    public void run() {
                        webView.setMinSize(w, h);
                        webView.setMaxSize(w, h);
                        webView.setPrefSize(w, h);
                    }
                });
            }
            revalidate();
            repaint();
        }
        catch(Exception ex) {}
    }

    // -------------------------------------------------------------------------
    


}
