/*******************************************************************************
 * Copyright (c) 2003, 2016 IBM Corporation and others.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 *
 * Contributors:
 *     IBM Corporation - Initial API and implementation
 *     
 *     
 *******************************************************************************/
package snippet;

import java.beans.PropertyChangeListener;
import java.net.MalformedURLException;
import java.net.URL;

import org.eclipse.core.runtime.Adapters;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.jface.action.IAction;
import org.eclipse.jface.resource.ImageDescriptor;
import org.eclipse.osgi.util.NLS;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Display;
import org.eclipse.ui.IActionBars;
import org.eclipse.ui.IEditorDescriptor;
import org.eclipse.ui.IEditorInput;
import org.eclipse.ui.IEditorPart;
import org.eclipse.ui.IEditorReference;
import org.eclipse.ui.IEditorRegistry;
import org.eclipse.ui.IEditorSite;
import org.eclipse.ui.IPathEditorInput;
import org.eclipse.ui.IWorkbenchPage;
import org.eclipse.ui.IWorkbenchWindow;
import org.eclipse.ui.PartInitException;
import org.eclipse.ui.PlatformUI;
import org.eclipse.ui.browser.IWorkbenchBrowserSupport;
import org.eclipse.ui.internal.browser.BrowserViewer;
import org.eclipse.ui.internal.browser.IBrowserViewerContainer;
import org.eclipse.ui.internal.browser.ImageResource;
import org.eclipse.ui.internal.browser.TextAction;
import org.eclipse.ui.internal.browser.Trace;
import org.eclipse.ui.internal.browser.WebBrowserEditorInput;
import org.eclipse.ui.internal.browser.WebBrowserUIPlugin;
import org.eclipse.ui.part.EditorPart;

/**
 * An integrated JFX Web browser (webkit), defined as an editor to make better use of the desktop.
 * 
 * TODO the JFXBrowser has to implements lots of event trigger to be fully functionnal
 */
public class TXMJFXBrowserEditor extends EditorPart implements IBrowserViewerContainer {
	
	protected static final String PROPERTY_TITLE = "title"; //$NON-NLS-1$
	
	public static final String WEB_BROWSER_EDITOR_ID = "org.txm.rcp.editors.TXMJFXBrowserEditor"; //$NON-NLS-1$
	
	protected JFXBrowserViewer webBrowser;
	
	protected String initialURL;
	
	protected Image image;
	
	protected TextAction cutAction;
	
	protected TextAction copyAction;
	
	protected TextAction pasteAction;
	
	private boolean disposed;
	
	private boolean lockName;
	
	/**
	 * WebBrowserEditor constructor comment.
	 */
	public TXMJFXBrowserEditor() {
		super();
	}
	
	@Override
	public void createPartControl(Composite parent) {
		WebBrowserEditorInput input = getWebBrowserEditorInput();
		
		int style = 0;
		if (input == null || input.isLocationBarLocal()) {
			style += BrowserViewer.LOCATION_BAR;
		}
		if (input == null || input.isToolbarLocal()) {
			style += BrowserViewer.BUTTON_BAR;
		}
		webBrowser = new JFXBrowserViewer(parent, style);
		
		webBrowser.setURL(initialURL);
		webBrowser.setContainer(this);
		
		if (input == null || input.isLocationBarLocal()) {
			// cutAction = new TextAction(webBrowser, TextAction.CUT);
			// copyAction = new TextAction(webBrowser, TextAction.COPY);
			// pasteAction = new TextAction(webBrowser, TextAction.PASTE);
		}
		
		if (!lockName) {
			PropertyChangeListener propertyChangeListener = event -> {
				if (PROPERTY_TITLE.equals(event.getPropertyName())) {
					setPartName((String) event.getNewValue());
				}
			};
			webBrowser.addPropertyChangeListener(propertyChangeListener);
		}
	}
	
	@Override
	public void setPartName(String partName) {
		super.setPartName(partName);
	}
	
	@Override
	public void setTitleImage(Image titleImage) {
		super.setTitleImage(titleImage);
	}
	
	@Override
	public void dispose() {
		if (image != null && !image.isDisposed())
			image.dispose();
		image = null;
		
		super.dispose();
		// mark this instance as disposed to avoid stale references
		disposed = true;
	}
	
	public boolean isDisposed() {
		return disposed;
	}
	
	@Override
	public void doSave(IProgressMonitor monitor) {
		// do nothing
	}
	
	@Override
	public void doSaveAs() {
		// do nothing
	}
	
	/**
	 * Returns the copy action.
	 *
	 * @return org.eclipse.jface.action.IAction
	 */
	public IAction getCopyAction() {
		return copyAction;
	}
	
	/**
	 * Returns the cut action.
	 *
	 * @return org.eclipse.jface.action.IAction
	 */
	public IAction getCutAction() {
		return cutAction;
	}
	
	/**
	 * Returns the web editor input, if available. If the input was of
	 * another type, <code>null</code> is returned.
	 *
	 * @return org.eclipse.ui.internal.browser.IWebBrowserEditorInput
	 */
	protected WebBrowserEditorInput getWebBrowserEditorInput() {
		IEditorInput input = getEditorInput();
		if (input instanceof WebBrowserEditorInput)
			return (WebBrowserEditorInput) input;
		return null;
	}
	
	/**
	 * Returns the paste action.
	 *
	 * @return org.eclipse.jface.action.IAction
	 */
	public IAction getPasteAction() {
		return pasteAction;
	}
	
	@Override
	public void init(IEditorSite site, IEditorInput input) throws PartInitException {
		Trace.trace(Trace.FINEST, "Opening browser: " + input); //$NON-NLS-1$
		if (input instanceof IPathEditorInput) {
			IPathEditorInput pei = (IPathEditorInput) input;
			final IPath path = pei.getPath();
			URL url = null;
			try {
				if (path != null) {
					setPartName(path.lastSegment());
					url = path.toFile().toURI().toURL();
				}
				if (url != null)
					initialURL = url.toExternalForm();
			}
			catch (Exception e) {
				Trace.trace(Trace.SEVERE, "Error getting URL to file"); //$NON-NLS-1$
			}
			if (webBrowser != null) {
				if (initialURL != null)
					webBrowser.setURL(initialURL);
				site.getWorkbenchWindow().getActivePage().activate(this);
			}
			
			if (url != null)
				setTitleToolTip(url.getFile());
			
			Image oldImage = image;
			ImageDescriptor id = ImageResource.getImageDescriptor(ImageResource.IMG_INTERNAL_BROWSER);
			image = id.createImage();
			
			setTitleImage(image);
			if (oldImage != null && !oldImage.isDisposed())
				oldImage.dispose();
			// addResourceListener(file);
		}
		else if (input instanceof WebBrowserEditorInput) {
			WebBrowserEditorInput wbei = (WebBrowserEditorInput) input;
			initialURL = null;
			if (wbei.getURL() != null)
				initialURL = wbei.getURL().toExternalForm();
			if (webBrowser != null) {
				webBrowser.setURL(initialURL);
				site.getWorkbenchWindow().getActivePage().activate(this);
			}
			
			setPartName(wbei.getName());
			setTitleToolTip(wbei.getToolTipText());
			lockName = false;
			
			Image oldImage = image;
			ImageDescriptor id = wbei.getImageDescriptor();
			image = id.createImage();
			
			setTitleImage(image);
			if (oldImage != null && !oldImage.isDisposed())
				oldImage.dispose();
		}
		else {
			IPathEditorInput pinput = Adapters.adapt(input, IPathEditorInput.class);
			if (pinput != null) {
				init(site, pinput);
			}
			else {
				throw new PartInitException(NLS.bind(org.eclipse.ui.internal.browser.Messages.errorInvalidEditorInput, input.getName()));
			}
		}
		
		setSite(site);
		setInput(input);
	}
	
	@Override
	public boolean isDirty() {
		return false;
	}
	
	@Override
	public boolean isSaveAsAllowed() {
		return false;
	}
	
	/**
	 * Open the input in the internal Web browser.
	 */
	public static void open(WebBrowserEditorInput input) {
		IWorkbenchWindow workbenchWindow = WebBrowserUIPlugin.getInstance().getWorkbench().getActiveWorkbenchWindow();
		IWorkbenchPage page = workbenchWindow.getActivePage();
		
		try {
			IEditorReference[] editors = page.getEditorReferences();
			int size = editors.length;
			for (int i = 0; i < size; i++) {
				if (WEB_BROWSER_EDITOR_ID.equals(editors[i].getId())) {
					IEditorPart editor = editors[i].getEditor(true);
					if (editor != null && editor instanceof TXMJFXBrowserEditor) {
						TXMJFXBrowserEditor webEditor = (TXMJFXBrowserEditor) editor;
						WebBrowserEditorInput input2 = webEditor.getWebBrowserEditorInput();
						if (input2 == null || input.canReplaceInput(input2)) {
							editor.init(editor.getEditorSite(), input);
							return;
						}
					}
				}
			}
			
			page.openEditor(input, TXMJFXBrowserEditor.WEB_BROWSER_EDITOR_ID);
		}
		catch (Exception e) {
			Trace.trace(Trace.SEVERE, "Error opening Web browser", e); //$NON-NLS-1$
		}
	}
	
	/*
	 * Asks this part to take focus within the workbench.
	 */
	@Override
	public void setFocus() {
		if (webBrowser != null)
			webBrowser.setFocus();
	}
	
	/**
	 * Close the editor correctly.
	 */
	@Override
	public boolean close() {
		final boolean[] result = new boolean[1];
		Display.getDefault()
				.asyncExec(() -> result[0] = getEditorSite().getPage().closeEditor(TXMJFXBrowserEditor.this, false));
		return result[0];
	}
	
	@Override
	public IActionBars getActionBars() {
		return getEditorSite().getActionBars();
	}
	
	@Override
	public void openInExternalBrowser(String url) {
		final IEditorInput input = getEditorInput();
		final String id = getEditorSite().getId();
		Runnable runnable = () -> doOpenExternalEditor(id, input);
		Display display = getSite().getShell().getDisplay();
		close();
		display.asyncExec(runnable);
	}
	
	protected void doOpenExternalEditor(String id, IEditorInput input) {
		IEditorRegistry registry = PlatformUI.getWorkbench().getEditorRegistry();
		String name = input.getName();
		IEditorDescriptor[] editors = registry.getEditors(name);
		IWorkbenchPage page = PlatformUI.getWorkbench().getActiveWorkbenchWindow().getActivePage();
		
		String editorId = null;
		for (IEditorDescriptor editor : editors) {
			if (editor.getId().equals(id))
				continue;
			editorId = editor.getId();
			break;
		}
		
		IEditorDescriptor ddesc = registry.getDefaultEditor(name);
		if (ddesc != null && ddesc.getId().equals(id)) {
			int dot = name.lastIndexOf('.');
			String ext = name;
			if (dot != -1)
				ext = "*." + name.substring(dot + 1); //$NON-NLS-1$
			registry.setDefaultEditor(ext, null);
		}
		
		if (editorId == null) {
			// no editor
			// next check with the OS for an external editor
			if (registry.isSystemExternalEditorAvailable(name))
				editorId = IEditorRegistry.SYSTEM_EXTERNAL_EDITOR_ID;
		}
		
		if (editorId != null) {
			try {
				page.openEditor(input, editorId);
				return;
			}
			catch (PartInitException e) {
				// ignore
			}
		}
		
		// no registered editor - open using browser support
		try {
			URL theURL = new URL(webBrowser.getURL());
			IWorkbenchBrowserSupport support = PlatformUI.getWorkbench().getBrowserSupport();
			support.getExternalBrowser().openURL(theURL);
		}
		catch (MalformedURLException e) {
			// TODO handle this
		}
		catch (PartInitException e) {
			// TODO handle this
		}
	}
}
