/* 
-*- 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.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.eclipse.swt.SWT;
import org.eclipse.swt.custom.ScrolledComposite;
import org.eclipse.swt.custom.StackLayout;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.events.SelectionListener;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.layout.RowLayout;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.DirectoryDialog;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Text;
import org.eclipse.ui.forms.widgets.Form;
import org.eclipse.ui.forms.widgets.FormToolkit;
import org.eclipse.ui.part.ViewPart;

import com.refactorerl.ui.logic.queries.ListQueryRequest;
import com.refactorerl.ui.presentation.Activator;

/**
 * This class creates and manages all the panels and widgets of the dependency
 * view. Switches the module, function and module block panels when the
 * corresponding button is selected. Hides those widgets which shouldn't be
 * shown when Erlang Text output or Smart Graph output or module block analysis
 * is selected,
 * 
 * 
 * @author Daniel Lukacs, 2014 ELTE IK
 *
 */
public class DrawDepView extends ViewPart {

	private FormToolkit toolkit;
	private List<Control> hideWhenErlSelected = new ArrayList<>();
	private List<Control> hideWhenHTMLSelected = new ArrayList<>();
	private List<Control> hideWhenModuleBlockSelected = new ArrayList<>();
	private Composite props;

	private ControlPanel modulePanel;
	private ControlPanel functionPanel;
	private ControlPanel moduleBlockPanel;
	private Button moduleButton;
	private Button functionButton;
	private Button moduleBlockButton;
	private ScrolledComposite scrolledCmp;
	private Form form;
	private Button localCopyButton;
	private Text storePathText;
	private Text fileNameText;
	private Button svgButton;
	
	// NOTE disabled until gef4 plugin integration 
		// see createOutputComposite(Composite)
	private Button zestButton; 
	private Button smartGraphButton;
	private Button erlangButton;
	private Button allButton;
	private Button cyclesButton;

	@Override
	public void createPartControl(Composite parent) {

		List<String> mods = new ArrayList<>();
		List<String> funs = new ArrayList<>();
		List<String> dirs = new ArrayList<>();

		List<String> mods0 = Activator.executeRequest(
				ListQueryRequest.createModuleListQueryRequest(Activator.getProxy(), null), getSite().getShell());

		for (String modFile : mods0) {
			if (modFile.indexOf('.') >= 0)
				mods.add(modFile.substring(0, modFile.lastIndexOf('.')));
			else
				mods.add(modFile);
		}
		funs = Activator.executeRequest(ListQueryRequest.createFunctionListQueryRequest(Activator.getProxy(), null),
				getSite().getShell());
		dirs = Activator.executeRequest(ListQueryRequest.createDirectoryListQueryRequest(Activator.getProxy(), null),
				getSite().getShell());

		String defaultFileName = Activator.generateRandomName();

		String defaultOutputDir = Activator.getDefault().getPreferenceStore().getString("data_dir"); //$NON-NLS-1$

		scrolledCmp = new ScrolledComposite(parent, SWT.H_SCROLL | SWT.V_SCROLL);

		toolkit = new FormToolkit(scrolledCmp.getDisplay());

		form = toolkit.createForm(scrolledCmp);
		scrolledCmp.setContent(form);
		scrolledCmp.setExpandVertical(true);
		scrolledCmp.setExpandHorizontal(true);

		form.setText(Messages.DrawDepView_1);

		toolkit.decorateFormHeading(form);
		Composite headClient = toolkit.createComposite(form.getHead());
		form.setHeadClient(headClient);
		headClient.setLayout(new RowLayout(SWT.VERTICAL));

		Composite cmp = form.getBody();

		cmp.setLayout(new GridLayout(1, false));

		Composite lc = createLevelComposite(cmp);
		configureGridData(lc, true);
		props = createPropertyComposite(cmp, mods, funs, dirs);
		configureGridData(props, true);
		Composite tc = createTypeComposite(cmp);
		configureGridData(tc, true);
		hideWhenErlSelected.add(tc);

		Composite oc = createOutputComposite(cmp);
		configureGridData(oc, true);
		Composite fc = createFileComposite(cmp, defaultOutputDir, defaultFileName);
		configureGridData(fc, true);

		modulePanel.init(localCopyButton, storePathText, fileNameText, svgButton, zestButton, smartGraphButton,
				erlangButton, allButton, cyclesButton);
		functionPanel.init(localCopyButton, storePathText, fileNameText, svgButton, zestButton, smartGraphButton,
				erlangButton, allButton, cyclesButton);
		moduleBlockPanel.init(localCopyButton, storePathText, fileNameText, svgButton, zestButton, smartGraphButton,
				erlangButton, allButton, cyclesButton);

		Button finishButton = toolkit.createButton(headClient, Messages.DrawDepView_2, SWT.PUSH);
		final Map<Button, ControlPanel> pages = new HashMap<>();
		pages.put(moduleButton, modulePanel);
		pages.put(functionButton, functionPanel);
		pages.put(moduleBlockButton, moduleBlockPanel);
		finishButton.addSelectionListener(new SelectionListener() {

			@Override
			public void widgetSelected(SelectionEvent e) {

				for (Button b : pages.keySet()) {
					if (b.getSelection()) {
						pages.get(b).performFinish();

						fileNameText.setText(Activator.generateRandomName());

						return;
					}

				}
			}

			@Override
			public void widgetDefaultSelected(SelectionEvent e) {
				// not called
			}
		});

		scrolledCmp.setMinSize(SWT.DEFAULT, form.computeSize(SWT.DEFAULT, SWT.DEFAULT).y);
		scrolledCmp.pack();
		scrolledCmp.layout();

	}

	/**
	 * Creates the composite which holds the buttons by which the user can
	 * choose between module, function and module block analysis.
	 * 
	 * @param parent
	 * @return
	 */
	private Composite createLevelComposite(Composite parent) {
		Composite levelControl = toolkit.createComposite(parent);

		levelControl.setLayout(new RowLayout(SWT.VERTICAL));

		toolkit.createLabel(levelControl, Messages.DrawDepView_3);

		Composite levelButtons = toolkit.createComposite(levelControl);
		levelButtons.setLayout(new RowLayout());
		moduleButton = toolkit.createButton(levelButtons, Messages.DrawDepView_4, SWT.RADIO);
		functionButton = toolkit.createButton(levelButtons, Messages.DrawDepView_5, SWT.RADIO);
		moduleBlockButton = toolkit.createButton(levelButtons, Messages.DrawDepView_6, SWT.RADIO);

		hideWhenErlSelected.add(moduleBlockButton);
		hideWhenHTMLSelected.add(moduleBlockButton);

		moduleBlockButton.addSelectionListener(new HideOnSelectionListener(hideWhenModuleBlockSelected));

		moduleButton.setSelection(true);
		return levelControl;
	}

	/**
	 * Creates the module, function and module block panels an stacks them on
	 * top of each other. By default, the module panel will be on top.
	 * 
	 * @param parent
	 * @param mods
	 *            A list of module names for auto-completion
	 * @param funs
	 *            A list of function names for auto-completion
	 * @param dirs
	 *            A list of directories for auto-completion
	 * @return
	 */
	private Composite createPropertyComposite(Composite parent, List<String> mods, List<String> funs, List<String> dirs) {
		final StackLayout propsLayout = new StackLayout();
		Composite props = toolkit.createComposite(parent);
		props.setLayout(propsLayout);

		modulePanel = new ModuleControlPanel(props, toolkit, hideWhenErlSelected, hideWhenHTMLSelected,
				hideWhenModuleBlockSelected, mods);

		functionPanel = new FunctionControlPanel(props, toolkit, hideWhenErlSelected, hideWhenHTMLSelected,
				hideWhenModuleBlockSelected, funs);
		moduleBlockPanel = new ModuleBlockControlPanel(props, toolkit, hideWhenErlSelected, hideWhenHTMLSelected,
				hideWhenModuleBlockSelected, dirs);
		propsLayout.topControl = modulePanel.getComposite();

		final Map<Button, Composite> pages = new HashMap<>();
		pages.put(moduleButton, modulePanel.getComposite());
		pages.put(functionButton, functionPanel.getComposite());
		pages.put(moduleBlockButton, moduleBlockPanel.getComposite());

		SelectionListener levelSelectionListener = new SelectionListener() {
			@Override
			public void widgetSelected(SelectionEvent e) {

				if (!(e.widget instanceof Button))
					return;
				Button b = (Button) e.widget;

				if (b.getSelection()) {
					propsLayout.topControl = pages.get(b);
					refreshPropsComposite();
				}
			}

			@Override
			public void widgetDefaultSelected(SelectionEvent e) {
				// not called
			}
		};

		moduleButton.addSelectionListener(levelSelectionListener);
		functionButton.addSelectionListener(levelSelectionListener);
		moduleBlockButton.addSelectionListener(levelSelectionListener);

		return props;
	}

	/**
	 * Creates the composite which holds the buttons by which the user can
	 * choose between showing all dependencies or just cyclic dependencies.
	 * 
	 * @param parent
	 * @return
	 */
	private Composite createTypeComposite(Composite parent) {
		Composite typeControl = toolkit.createComposite(parent);
		typeControl.setLayout(new RowLayout(SWT.VERTICAL));
		toolkit.createLabel(typeControl, Messages.DrawDepView_7);

		Composite typeButtons = toolkit.createComposite(typeControl);
		typeButtons.setLayout(new RowLayout());

		allButton = toolkit.createButton(typeButtons, Messages.DrawDepView_8, SWT.RADIO);
		cyclesButton = toolkit.createButton(typeButtons, Messages.DrawDepView_9, SWT.RADIO);

		allButton.setSelection(true);

		return typeControl;

	}

	/**
	 * Creates the composite which holds the buttons by which the user can
	 * select the output format.
	 * 
	 * @param parent
	 * @return
	 */
	private Composite createOutputComposite(Composite parent) {
		Composite outputControl = toolkit.createComposite(parent);
		outputControl.setLayout(new RowLayout(SWT.VERTICAL));
		toolkit.createLabel(outputControl, Messages.DrawDepView_10);

		Composite outputButtons = toolkit.createComposite(outputControl);
		outputButtons.setLayout(new RowLayout());

		svgButton = toolkit.createButton(outputButtons, Messages.DrawDepView_11, SWT.RADIO);
		svgButton.setText(Messages.DrawDepView_12);

		// NOTE disabled until gef4 plugin integration 
	//	zestButton = toolkit.createButton(outputButtons, Messages.DrawDepView_13, SWT.RADIO);
		zestButton = null;

		smartGraphButton = toolkit.createButton(outputButtons, Messages.DrawDepView_14, SWT.RADIO);

		erlangButton = toolkit.createButton(outputButtons, Messages.DrawDepView_15, SWT.RADIO);

		erlangButton.addSelectionListener(new HideOnSelectionListener(hideWhenErlSelected));
		smartGraphButton.addSelectionListener(new HideOnSelectionListener(hideWhenHTMLSelected));

		svgButton.setSelection(true);

		hideWhenModuleBlockSelected.add(smartGraphButton);
		hideWhenModuleBlockSelected.add(erlangButton);

		return outputControl;
	}

	/**
	 * Creates the composite which holds the field by which the user can specify
	 * the name and parent directory of the output file.
	 * 
	 * @param parent
	 * @return
	 */
	private Composite createFileComposite(Composite parent, String defaultOutputDir, String defaultFileName) {
		Composite fileControl = toolkit.createComposite(parent);
		fileControl.setLayout(new GridLayout(2, false));

		Label localCopyLabel = toolkit.createLabel(fileControl, Messages.DrawDepView_16);
		configureGridData(localCopyLabel, false);
		hideWhenErlSelected.add(localCopyLabel);
		localCopyButton = toolkit.createButton(fileControl, "", SWT.CHECK); //$NON-NLS-1$
		localCopyButton.setSelection(true);
		configureGridData(localCopyButton, true);
		hideWhenErlSelected.add(localCopyButton);

		final Button storePathButton = toolkit.createButton(fileControl, Messages.DrawDepView_18, SWT.PUSH);
		configureGridData(storePathButton, false);
		hideWhenErlSelected.add(storePathButton);

		storePathText = toolkit.createText(fileControl, defaultOutputDir, SWT.BORDER);
		configureGridData(storePathText, true);
		hideWhenErlSelected.add(storePathText);
		storePathButton.addSelectionListener(new SelectionListener() {

			@Override
			public void widgetSelected(SelectionEvent e) {
				DirectoryDialog dialog = new DirectoryDialog(getSite().getShell());
				String path = dialog.open();
				if (path == null)
					return;

				storePathText.setText(path);
			}

			@Override
			public void widgetDefaultSelected(SelectionEvent e) {
				// not called

			}
		});

		Label fileNameLabel = toolkit.createLabel(fileControl, Messages.DrawDepView_19);
		configureGridData(fileNameLabel, false);
		hideWhenErlSelected.add(fileNameLabel);

		fileNameText = toolkit.createText(fileControl, defaultFileName, SWT.BORDER);
		configureGridData(fileNameText, true);
		hideWhenErlSelected.add(fileNameText);

		localCopyButton.addSelectionListener(new SelectionListener() {

			@Override
			public void widgetSelected(SelectionEvent e) {
				if (localCopyButton.getSelection()) {
					fileNameText.setEnabled(true);
					storePathText.setEnabled(true);
					storePathButton.setEnabled(true);
				} else {
					fileNameText.setEnabled(false);
					storePathText.setEnabled(false);
					storePathButton.setEnabled(false);
				}

			}

			@Override
			public void widgetDefaultSelected(SelectionEvent e) {
				// not called

			}
		});

		return fileControl;
	}

	@Override
	public void setFocus() {

	}

	/**
	 * Refreshes and sets the optimal size for the module, function and module block panels.
	 */
	private void refreshPropsComposite() {
		props.setSize(((StackLayout) props.getLayout()).topControl.computeSize(SWT.DEFAULT, SWT.DEFAULT));

		// Eclipse bug workaround
		((GridData) props.getLayoutData()).heightHint = props.getSize().y;
		props.layout();

		props.getParent().layout();

		scrolledCmp.setMinSize(SWT.DEFAULT, form.computeSize(SWT.DEFAULT, SWT.DEFAULT).y);
		scrolledCmp.layout();

	}

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

	enum OutputFormat {
		SVG, ZEST, HTML, ERL
	}

	private class HideOnSelectionListener implements SelectionListener {

		private List<Control> hideWhenSelected;

		public HideOnSelectionListener(List<Control> hideWhenSelected) {
			this.hideWhenSelected = hideWhenSelected;
		}

		@Override
		public void widgetSelected(SelectionEvent e) {
			Button b = (Button) e.widget;

			for (Control control : hideWhenSelected) {
				boolean selected = b.getSelection();
				control.setVisible(!selected);
				GridData data = (GridData) control.getLayoutData();
				if (data != null) {
					data.exclude = selected;
				}
				refreshPropsComposite();
			}
		}

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

		}
	}

}
