/* 
-*- coding: latin-1 -*-

This file is part of RefactorErl.

RefactorErl is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published
by the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

RefactorErl 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 Lesser General Public License for more details.

You should have received a copy of the GNU Lesser General Public License
along with RefactorErl.  If not, see <http://plc.inf.elte.hu/erlang/>.

The Original Code is RefactorErl.

The Initial Developer of the Original Code is Eötvös Loránd University.
Portions created  by Eötvös Loránd University and ELTE-Soft Ltd.
are Copyright 2007-2025 Eötvös Loránd University, ELTE-Soft Ltd.
and Ericsson Hungary. All Rights Reserved.
*/

package com.refactorerl.ui.presentation.dependency;

import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Status;
import org.eclipse.core.runtime.jobs.Job;
import org.eclipse.jface.dialogs.MessageDialog;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.events.SelectionListener;
import org.eclipse.swt.events.TraverseEvent;
import org.eclipse.swt.events.TraverseListener;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Text;
import org.eclipse.ui.IStorageEditorInput;
import org.eclipse.ui.PartInitException;
import org.eclipse.ui.PlatformUI;
import org.eclipse.ui.forms.widgets.FormToolkit;

import com.refactorerl.ui.common.Environment;
import com.refactorerl.ui.communication.exceptions.CommunicationException;
import com.refactorerl.ui.communication.exceptions.ConnectionException;
import com.refactorerl.ui.communication.exceptions.RequestException;
import com.refactorerl.ui.logic.dependency.DependencyRequestFactory;
import com.refactorerl.ui.presentation.Activator;
import com.refactorerl.ui.presentation.GeneralContentProposalAdapter;
import com.refactorerl.ui.presentation.codebrowser.StringContentStorage;
import com.refactorerl.ui.presentation.codebrowser.StringInput;
import com.refactorerl.ui.presentation.dependency.DrawDepView.OutputFormat;

/**
 * Abstract superclass which defines the common widgets present in all of the
 * module, function and module block dependency panels, and also opens a view
 * with the result of the executed dependency request. Extending classes will
 * need to implement the placement of special widgets and the execution of the
 * proper dependency request based on the data parsed from the common widgets.
 * 
 * 
 * @author Daniel Lukacs, 2014 ELTE IK
 *
 */
abstract class ControlPanel {

	Button localCopyButton = null;
	Text storePathText = null;
	Text fileNameText = null;
	Button svgButton = null;
	Button zestButton = null;
	Button smartGraphButton = null;
	Button erlangButton = null;
	Button allButton = null;
	Button cyclesButton = null;

	Composite parent;
	FormToolkit toolkit;
	List<Control> hideWhenErlSelected;
	List<Control> hideWhenHTMLSelected;
	List<Control> hideWhenModuleBlockSelected;

	public ControlPanel(Composite parent, FormToolkit toolkit, List<Control> hideWhenErlSelected,
			List<Control> hideWhenHTMLSelected, List<Control> hideWhenModuleBlockSelected) {
		this.parent = parent;
		this.toolkit = toolkit;
		this.hideWhenErlSelected = hideWhenErlSelected;
		this.hideWhenHTMLSelected = hideWhenHTMLSelected;
		this.hideWhenModuleBlockSelected = hideWhenModuleBlockSelected;

	}

	/**
	 * This method defines the special widgets of the inheriting class.
	 * 
	 * @return
	 */
	abstract public Composite getComposite();

	// TODO String is not safe, bytes would be better here
	/**
	 * This method gathers is responsible to gather data from special widgets in
	 * the inheriting class and execute the proper dependency request based on
	 * the parameters and the gathered data.
	 * 
	 * @param outputFormat
	 * @param type
	 * @param fileName
	 *            The name of the output file to be created.
	 * @return The string contents of the svg, dot, html etc... file created by
	 *         RefactorErl in response to the dependency request
	 * @throws RequestException
	 * @throws ConnectionException
	 * @throws CommunicationException
	 */
	abstract protected String makeDependencyRequest(DependencyRequestFactory.DepOutputFormat outputFormat,
			DependencyRequestFactory.DepType type, String fileName) throws RequestException, ConnectionException,
			CommunicationException;

	public void init(Button localCopyButton, Text storePathText, Text fileNameText, Button svgButton,
			Button zestButton, Button smartGraphButton, Button erlangButton, Button allButton, Button cyclesButton) {
		this.localCopyButton = localCopyButton;
		this.storePathText = storePathText;
		this.fileNameText = fileNameText;
		this.svgButton = svgButton;
		this.zestButton = zestButton;
		this.smartGraphButton = smartGraphButton;
		this.erlangButton = erlangButton;
		this.allButton = allButton;
		this.cyclesButton = cyclesButton;
	}

	/**
	 * Gathers the data from all the widgets and executes the right dependency
	 * request based on the data.
	 */
	public void performFinish() {

		
		for (Control control : new Control[] { localCopyButton, storePathText, fileNameText, svgButton, 
				/*zestButton,*/ // NOTE disabled until gef4 plugin integration 
				smartGraphButton, erlangButton, allButton, cyclesButton }) {
			if (control == null)
				throw new IllegalStateException(getClass() + Messages.ControlPanel_0);
		}

		final boolean createLocalCopy = localCopyButton.getSelection();
		final String outputDir = storePathText.getText();
		final String fileName = fileNameText.getText();

		final Map<Button, OutputFormat> buttonFormats = new HashMap<>();
		buttonFormats.put(svgButton, OutputFormat.SVG);
		
		// NOTE disabled until gef4 plugin integration 
//		buttonFormats.put(zestButton, OutputFormat.ZEST);
		buttonFormats.put(smartGraphButton, OutputFormat.HTML);
		buttonFormats.put(erlangButton, OutputFormat.ERL);
		OutputFormat outputFormat0 = null;
		for (Button b : buttonFormats.keySet()) {
			if (b.getSelection()) {
				outputFormat0 = buttonFormats.get(b);
			}
		}

		Map<Button, DependencyRequestFactory.DepType> buttonTypes = new HashMap<>();
		buttonTypes.put(allButton, DependencyRequestFactory.DepType.ALL);
		buttonTypes.put(cyclesButton, DependencyRequestFactory.DepType.CYCLIC);
		DependencyRequestFactory.DepType type0 = null;
		for (Button b : buttonTypes.keySet()) {
			if (b.getSelection()) {
				type0 = buttonTypes.get(b);
			}
		}

		if (outputFormat0 == null || type0 == null) {
			MessageDialog.openWarning(parent.getShell(), Messages.ControlPanel_1, Messages.ControlPanel_2);
			return;
		}
		final OutputFormat outputFormat = outputFormat0;
		final DependencyRequestFactory.DepType type = type0;

		// NOTE disabled until gef4 plugin integration 
		// boolean zestInstalled = Platform
		// .getBundle("org.eclipse.gef4.zest.dot.ui") != null;
		boolean zestInstalled = false;	
		if (outputFormat.equals(OutputFormat.ZEST) && !zestInstalled){
			MessageDialog.openInformation(parent.getShell(), Messages.ControlPanel_10,
				Messages.ControlPanel_11);
			return;
		}
		
		File outputDirFile0 = null;
		if (createLocalCopy) {
			final String errTitle = Messages.ControlPanel_3;
			final String errMessage = Messages.ControlPanel_4;
			outputDirFile0 = new File(outputDir);
			if (!outputDirFile0.exists()) {
				if (!outputDirFile0.mkdir()) {
					MessageDialog.openWarning(parent.getShell(), errTitle, errMessage);
					return;
				}
			} else if (outputDirFile0.isFile()) {
				MessageDialog.openWarning(parent.getShell(), errTitle, errMessage);
				return;
			}

		}
		final File outputDirFile = outputDirFile0;

		final Map<OutputFormat, DependencyRequestFactory.DepOutputFormat> formatDict = new HashMap<>();
		formatDict.put(OutputFormat.ERL, DependencyRequestFactory.DepOutputFormat.ERL);
		formatDict.put(OutputFormat.HTML, DependencyRequestFactory.DepOutputFormat.HTML);
		formatDict.put(OutputFormat.SVG, DependencyRequestFactory.DepOutputFormat.DOT);
		formatDict.put(OutputFormat.ZEST, DependencyRequestFactory.DepOutputFormat.DOT);

		new Job(Messages.ControlPanel_5) {

			@Override
			protected IStatus run(IProgressMonitor monitor) {
				String fileContent0 = null;
				try {

					fileContent0 = makeDependencyRequest(formatDict.get(outputFormat), type, fileName);
				} catch (RequestException | ConnectionException | CommunicationException e) {
					Activator.showErrorDialog(e, parent.getShell());
				}

				final String fileContent = fileContent0;

				parent.getDisplay().syncExec(new Runnable() {

					@Override
					public void run() {
						switch (outputFormat) {
						case ERL:
							IStorageEditorInput input = new StringInput(new StringContentStorage(fileName, fileContent));
							try {
								PlatformUI.getWorkbench().getActiveWorkbenchWindow().getActivePage()
										.openEditor(input, "org.eclipse.ui.DefaultTextEditor"); //$NON-NLS-1$
							} catch (PartInitException e1) {
								
								e1.printStackTrace();
							}

							break;
						case HTML:
							try {
								File tmpFile;
								if (!createLocalCopy) {									
									tmpFile = File.createTempFile(fileName, ".html", Environment.getTempDir()); //$NON-NLS-1$
									tmpFile.deleteOnExit();
								} else {
									tmpFile = File.createTempFile(fileName, ".html", outputDirFile); //$NON-NLS-1$
								}
								Files.write(tmpFile.toPath(), fileContent.getBytes());
								openInBrowser(tmpFile.getAbsolutePath());
							} catch (IOException e) {
								Activator.showErrorDialog(e, parent.getShell());
							}
							break;
						case SVG:
							parent.getDisplay().asyncExec(new Runnable() {

								@Override
								public void run() {
									File svgFile;
									try {
										svgFile = createLocalCopy ? GraphvizSupport.createSVG(parent.getShell(),
												fileContent, outputDir, fileName) : GraphvizSupport.createSVG(
												parent.getShell(), fileContent);

										if (svgFile != null) {
												openInBrowser(svgFile.getAbsolutePath());

												// see definition
//												openInImageView(svgFile.getAbsolutePath());
										}

									} catch (IOException e) {
										Activator.showErrorDialog(e, parent.getShell());

									}

								}
							});

							break;
						case ZEST:							
								try {
									ZestView zestView = (ZestView) PlatformUI.getWorkbench().getActiveWorkbenchWindow()
											.getActivePage().showView("com.refactorerl.ui.zestView"); //$NON-NLS-1$
									zestView.showDotByDefinition(fileContent, fileName);

								} catch (PartInitException e) {
									Activator.showErrorDialog(e, parent.getShell());	
								}
							break;
						default:
							break;

						}
					}
				});

				return Status.OK_STATUS;
			}

		}.schedule();

	};

	private void openInBrowser(String filePath) {
		try {
			BrowserView browserView = (BrowserView) PlatformUI.getWorkbench().getActiveWorkbenchWindow()
					.getActivePage().showView("com.refactorerl.ui.browserView"); //$NON-NLS-1$

			browserView.setUrl(filePath);
		} catch (PartInitException e) {
			Activator.showErrorDialog(e, parent.getShell());
		}
	}

	// won't work in Windows (Eclispe bug?). openInBrowser provides the same result 
	private void openInImageView(String filePath) {
		try {
			ImageView browserView = (ImageView) PlatformUI.getWorkbench().getActiveWorkbenchWindow().getActivePage()
					.showView("com.refactorerl.ui.imageView"); //$NON-NLS-1$

			browserView.setImage(filePath);
		} catch (PartInitException e) {
			Activator.showErrorDialog(e, parent.getShell());
		}
	}

	protected org.eclipse.swt.widgets.List createLabelledListOnGridLayout(Composite moduleControl,
			final List<String> modules, final String lab, List<Control>... hideLists) {
		Label label = toolkit.createLabel(moduleControl, lab);

		return createListOnGridLayout(moduleControl, modules, label, hideLists);
	}

	protected org.eclipse.swt.widgets.List createListOnGridLayout(Composite moduleControl, final List<String> modules,
			final Control ctrl, List<Control>... hideLists) {

		Text text = toolkit.createText(moduleControl, "", SWT.SINGLE //$NON-NLS-1$
				| SWT.BORDER);

		toolkit.createLabel(moduleControl, ""); // placeHolder //$NON-NLS-1$

		org.eclipse.swt.widgets.List list = new org.eclipse.swt.widgets.List(moduleControl, SWT.BORDER | SWT.MULTI
				| SWT.V_SCROLL);

		// toolkit.adapt(list);
		// toolkit.paintBordersFor(list);

		createAutoCompleteFilledList(text, list, modules);

		for (List<Control> hides : hideLists) {
			hides.add(ctrl);
			hides.add(text);
			hides.add(list);
		}

		configureGridData(ctrl, false);
		configureGridData(text, true);
		configureGridData(list, true);

		return list;

	}

	static protected GridData configureGridData(Control ctrl, boolean rowEnd) {
		GridData gd = new GridData(SWT.FILL, SWT.BEGINNING, rowEnd, false);
		gd.exclude = false;
		ctrl.setLayoutData(gd);
		return gd;
	}

	protected void createAutoCompleteFilledList(final Text text, final org.eclipse.swt.widgets.List list, List<String> proposals) {

		new GeneralContentProposalAdapter(text, proposals);
		list.addSelectionListener(new SelectionListener() {

			@Override
			public void widgetSelected(SelectionEvent e) {
				int idx = list.getSelectionIndex();
				list.remove(idx);
			}

			@Override
			public void widgetDefaultSelected(SelectionEvent e) {
				widgetSelected(e);

			}
		});

		text.addTraverseListener(new TraverseListener() {
			@Override
			public void keyTraversed(TraverseEvent e) {

				if (e.keyCode == SWT.CR) {

					String s = text.getText();
					// if (proposals.contains(s)) {
					list.add(s);
					text.setText(""); //$NON-NLS-1$
					// }

					e.doit = false;
				}

			}
		});
	}

}