/*******************************************************************************
 * Copyright (c) 2015, 2018 Red Hat.
 * 
 * This program and the accompanying materials are made
 * available under the terms of the Eclipse Public License 2.0
 * which is available at https://www.eclipse.org/legal/epl-2.0/
 *
 * SPDX-License-Identifier: EPL-2.0
 *
 * Contributors:
 *     Red Hat - Initial Contribution
 *******************************************************************************/
package org.eclipse.linuxtools.internal.vagrant.ui.wizards;

import java.nio.file.Files;
import java.nio.file.Paths;

import org.eclipse.core.databinding.DataBindingContext;
import org.eclipse.core.databinding.UpdateValueStrategy;
import org.eclipse.core.databinding.beans.typed.BeanProperties;
import org.eclipse.core.databinding.observable.IObservable;
import org.eclipse.core.databinding.observable.list.IObservableList;
import org.eclipse.core.databinding.observable.list.WritableList;
import org.eclipse.core.databinding.observable.value.IObservableValue;
import org.eclipse.core.databinding.validation.MultiValidator;
import org.eclipse.core.databinding.validation.ValidationStatus;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.jface.databinding.swt.typed.WidgetProperties;
import org.eclipse.jface.databinding.wizard.WizardPageSupport;
import org.eclipse.jface.layout.GridDataFactory;
import org.eclipse.jface.layout.GridLayoutFactory;
import org.eclipse.jface.wizard.WizardPage;
import org.eclipse.linuxtools.internal.vagrant.ui.SWTImagesFactory;
import org.eclipse.swt.SWT;
import org.eclipse.swt.custom.ScrolledComposite;
import org.eclipse.swt.events.SelectionListener;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.DirectoryDialog;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Text;

public class PackageVMPage extends WizardPage {

	private final PackageVMPageModel model;
	private final DataBindingContext dbc;

	public PackageVMPage() {
		super("PackageVMPage", //$NON-NLS-1$
				WizardMessages.getString("PackageVMPage.label"), //$NON-NLS-1$
				SWTImagesFactory.DESC_BANNER_REPOSITORY);
		setMessage(WizardMessages.getString("PackageVMPage.desc")); //$NON-NLS-1$
		this.model = new PackageVMPageModel();
		this.dbc = new DataBindingContext();
	}

	@Override
	public void dispose() {
		dbc.dispose();
		super.dispose();
	}

	public String getBoxName() {
		return this.model.getBoxName();
	}

	public String getBoxFolder() {
		return this.model.getBoxFolder();
	}

	@Override
	public void createControl(Composite parent) {
		ScrolledComposite scrollTop = new ScrolledComposite(parent, SWT.H_SCROLL | SWT.V_SCROLL);
		scrollTop.setExpandVertical(true);
		scrollTop.setExpandHorizontal(true);

		final Composite container = new Composite(scrollTop, SWT.NONE);
		GridLayoutFactory.fillDefaults().numColumns(3).margins(6, 6)
				.applyTo(container);
		GridDataFactory.fillDefaults().align(SWT.FILL, SWT.CENTER).span(1, 1)
				.grab(true, false).applyTo(container);

		// Box name
		final Label boxNameLabel = new Label(container, SWT.NONE);
		boxNameLabel
				.setText(WizardMessages.getString("ImagePull.name.label")); //$NON-NLS-1$
		GridDataFactory.fillDefaults().align(SWT.FILL, SWT.CENTER)
				.grab(false, false).applyTo(boxNameLabel);

		final Text boxNameText = new Text(container, SWT.BORDER);
		GridDataFactory.fillDefaults().align(SWT.FILL, SWT.CENTER)
				.grab(true, false).span(2, 1).applyTo(boxNameText);
		boxNameText.setToolTipText(
				WizardMessages.getString("ImagePull.name.tooltip")); //$NON-NLS-1$
		// Name binding
		final IObservableValue<String> boxNameObservable = BeanProperties
				.value(PackageVMPageModel.class, PackageVMPageModel.BOX_NAME,
						String.class)
				.observe(model);
		dbc.bindValue(WidgetProperties.text(SWT.Modify).observe(boxNameText),
				boxNameObservable, new UpdateValueStrategy<>(), null);

		// Box folder
		final Label boxFolderLabel = new Label(container, SWT.NONE);
		boxFolderLabel.setText(WizardMessages.getString("PackageVMPage.folder.label")); //$NON-NLS-1$
		GridDataFactory.fillDefaults().align(SWT.FILL, SWT.CENTER)
				.grab(false, false).applyTo(boxFolderLabel);

		final Text boxFolderText = new Text(container, SWT.BORDER);
		GridDataFactory.fillDefaults().align(SWT.FILL, SWT.CENTER)
				.grab(true, false).applyTo(boxFolderText);
		boxFolderText.setToolTipText(
				WizardMessages.getString("PackageVMPage.folder.desc")); //$NON-NLS-1$
		// Folder binding
		final IObservableValue<String> boxFolderObservable = BeanProperties
				.value(PackageVMPageModel.class, PackageVMPageModel.BOX_FOLDER,
						String.class)
				.observe(model);
		dbc.bindValue(WidgetProperties.text(SWT.Modify).observe(boxFolderText),
				boxFolderObservable, new UpdateValueStrategy<>(), null);
		// search
		final Button searchButton = new Button(container, SWT.NONE);
		searchButton
				.setText(WizardMessages.getString("ImagePull.search.label")); //$NON-NLS-1$
		GridDataFactory.fillDefaults().align(SWT.FILL, SWT.CENTER)
				.grab(false, false).applyTo(searchButton);
		searchButton.addSelectionListener(onSearchFolder());

		dbc.addValidationStatusProvider(new PackageVMValidationStatusProvider(
				boxNameObservable, boxFolderObservable));

		// setup validation support
		WizardPageSupport.create(this, dbc);

		scrollTop.setContent(container);
		Point point = container.computeSize(SWT.DEFAULT, SWT.DEFAULT);
		scrollTop.setSize(point);
		scrollTop.setMinSize(point);
		setControl(container);
	}

	/**
	 * Opens the folder selection dialog.
	 * 
	 * @return
	 */
	private SelectionListener onSearchFolder() {
		return SelectionListener.widgetSelectedAdapter(e -> {
			DirectoryDialog fd = new DirectoryDialog(getShell());
			String location = fd.open();
			if (location != null && !location.isEmpty()) {
				model.setBoxFolder(location);
			}
		});
	}

	private class PackageVMValidationStatusProvider extends MultiValidator {

		private IObservableValue<String> boxNameOb, boxFolderOb;

		public PackageVMValidationStatusProvider(
				IObservableValue<String> boxNameOb,
				IObservableValue<String> boxFolderOb) {
			this.boxNameOb = boxNameOb;
			this.boxFolderOb = boxFolderOb;
		}

		@Override
		public IObservableList<IObservable> getTargets() {
			// Work around for NPE triggered by DialogPageSupport.dispose()
			return new WritableList<>();
		}

		@Override
		protected IStatus validate() {
			String boxName = boxNameOb.getValue();
			String boxFolder = boxFolderOb.getValue();
			if (boxName == null || boxName.isEmpty()) {
				return ValidationStatus.error(
						WizardMessages.getString("AddBoxPage.emptyBoxName")); //$NON-NLS-1$
			} else if (boxFolder == null || boxFolder.isEmpty()) {
				return ValidationStatus.error(
						WizardMessages.getString("PackageVMPage.folder.empty")); //$NON-NLS-1$
			} else if (!Paths.get(boxFolder).toFile().exists()) {
				return ValidationStatus.error(WizardMessages
						.getString("PackageVMPage.folder.not.exist")); //$NON-NLS-1$
			} else if (!Files.isWritable(Paths.get(boxFolder))) {
				return ValidationStatus.error(WizardMessages
						.getString("PackageVMPage.folder.not.writeable")); //$NON-NLS-1$
			}
			return ValidationStatus.ok();
		}
	}

}
