/*******************************************************************************
 * Copyright (c) 2015, 2019 Red Hat Inc. and others.
 * 
 * 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.docker.ui.wizards;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

import org.eclipse.core.databinding.DataBindingContext;
import org.eclipse.core.databinding.beans.typed.BeanProperties;
import org.eclipse.core.databinding.observable.value.IValueChangeListener;
import org.eclipse.jface.databinding.swt.ISWTObservableValue;
import org.eclipse.jface.databinding.swt.typed.WidgetProperties;
import org.eclipse.jface.dialogs.Dialog;
import org.eclipse.jface.dialogs.IDialogConstants;
import org.eclipse.jface.fieldassist.ComboContentAdapter;
import org.eclipse.jface.fieldassist.ContentProposal;
import org.eclipse.jface.fieldassist.ContentProposalAdapter;
import org.eclipse.jface.fieldassist.IContentProposal;
import org.eclipse.jface.fieldassist.IContentProposalProvider;
import org.eclipse.jface.layout.GridDataFactory;
import org.eclipse.jface.layout.GridLayoutFactory;
import org.eclipse.jface.viewers.ArrayContentProvider;
import org.eclipse.jface.viewers.ComboViewer;
import org.eclipse.linuxtools.docker.core.IDockerConnection;
import org.eclipse.linuxtools.docker.core.IDockerContainer;
import org.eclipse.linuxtools.internal.docker.ui.databinding.BaseDatabindingModel;
import org.eclipse.linuxtools.internal.docker.ui.wizards.ImageRunSelectionModel.ContainerLinkModel;
import org.eclipse.swt.SWT;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Combo;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.swt.widgets.Text;

public class ContainerLinkDialog extends Dialog {

	private final IDockerConnection connection;

	private final ContainerLinkDialogModel model;

	private final List<String> containerNames;

	private final DataBindingContext dbc = new DataBindingContext();

	public ContainerLinkDialog(final Shell shell,
			final IDockerConnection connection) {
		super(shell);
		this.connection = connection;
		this.model = new ContainerLinkDialogModel();
		this.containerNames = WizardUtils.getContainerNames(connection);
	}

	public ContainerLinkDialog(final Shell shell,
			final IDockerConnection connection,
			final ContainerLinkModel selectedContainerLink) {
		super(shell);
		this.connection = connection;
		this.model = new ContainerLinkDialogModel(selectedContainerLink);
		this.containerNames = WizardUtils.getContainerNames(connection);
	}

	@Override
	protected void configureShell(final Shell shell) {
		super.configureShell(shell);
		setShellStyle(getShellStyle() | SWT.RESIZE);
		shell.setText(WizardMessages.getString("ContainerLinkDialog.title")); //$NON-NLS-1$
	}

	@Override
	protected Point getInitialSize() {
		return new Point(400, super.getInitialSize().y);
	}

	/**
	 * Disable the 'OK' button by default
	 */
	@Override
	protected Button createButton(Composite parent, int id, String label,
			boolean defaultButton) {
		final Button button = super.createButton(parent, id, label,
				defaultButton);
		if (id == IDialogConstants.OK_ID) {
			button.setEnabled(false);
		}
		return button;
	}

	@Override
	protected Control createDialogArea(final Composite parent) {
		final int COLUMNS = 2;
		final Composite container = new Composite(parent, SWT.NONE);
		GridDataFactory.fillDefaults().align(SWT.FILL, SWT.FILL)
				.span(COLUMNS, 1).grab(true, false).applyTo(container);
		GridLayoutFactory.fillDefaults().numColumns(COLUMNS).margins(10, 10)
				.applyTo(container);
		final Label explanationLabel = new Label(container, SWT.NONE);
		explanationLabel.setText(WizardMessages
				.getString("ContainerLinkDialog.explanationLabel")); //$NON-NLS-1$
		GridDataFactory.fillDefaults().align(SWT.FILL, SWT.CENTER)
				.span(COLUMNS, 1).grab(false, false).applyTo(explanationLabel);
		final Label containerLabel = new Label(container, SWT.NONE);
		containerLabel.setText(
				WizardMessages.getString("ContainerLinkDialog.containerLabel")); //$NON-NLS-1$
		GridDataFactory.fillDefaults().align(SWT.FILL, SWT.CENTER)
				.grab(false, false).applyTo(containerLabel);
		final Combo containerSelectionCombo = new Combo(container, SWT.NONE);
		GridDataFactory.fillDefaults().align(SWT.FILL, SWT.CENTER)
				.grab(true, false).applyTo(containerSelectionCombo);
		final ComboViewer containerSelectionComboViewer = new ComboViewer(
				containerSelectionCombo);
		containerSelectionComboViewer
				.setContentProvider(new ArrayContentProvider());
		containerSelectionComboViewer.setInput(model.getContainerNames());
		new ContentProposalAdapter(containerSelectionCombo,
				new ComboContentAdapter() {
					@Override
					public void insertControlContents(Control control,
							String text, int cursorPosition) {
						final Combo combo = (Combo) control;
						final Point selection = combo.getSelection();
						combo.setText(text);
						selection.x = text.length();
						selection.y = selection.x;
						combo.setSelection(selection);
					}
				}, getContainerNameContentProposalProvider(
						containerSelectionCombo),
				null, null);
		final Label aliasLabel = new Label(container, SWT.NONE);
		aliasLabel.setText(
				WizardMessages.getString("ContainerLinkDialog.aliasLabel")); //$NON-NLS-1$
		GridDataFactory.fillDefaults().align(SWT.FILL, SWT.CENTER)
				.grab(false, false).applyTo(aliasLabel);
		final Text containerAliasText = new Text(container, SWT.BORDER);
		GridDataFactory.fillDefaults().align(SWT.FILL, SWT.CENTER)
				.grab(true, false).applyTo(containerAliasText);
		// error message
		final Label errorMessageLabel = new Label(container, SWT.NONE);
		GridDataFactory.fillDefaults().align(SWT.FILL, SWT.CENTER)
				.span(COLUMNS, 1).grab(true, false).applyTo(errorMessageLabel);

		final ISWTObservableValue<?> containerNameObservable = WidgetProperties
				.widgetSelection().observe(containerSelectionComboViewer.getCombo());

		dbc.bindValue(containerNameObservable,
				BeanProperties
						.value(ContainerLinkDialogModel.class,
								ContainerLinkDialogModel.CONTAINER_NAME)
						.observe(model));
		final ISWTObservableValue<?> containerAliasObservable = WidgetProperties
				.text(SWT.Modify).observe(containerAliasText);

		dbc.bindValue(containerAliasObservable, BeanProperties
				.value(ContainerLinkDialogModel.class, ContainerLinkDialogModel.CONTAINER_ALIAS).observe(model));
		containerNameObservable.addValueChangeListener(onContainerLinkSettingsChanged());
		containerAliasObservable.addValueChangeListener(onContainerLinkSettingsChanged());
		return container;
	}

	private IValueChangeListener onContainerLinkSettingsChanged() {
		return event -> validateInput();
	}

	public String getContainerName() {
		return model.getContainerName();
	}

	public String getContainerAlias() {
		return model.getContainerAlias();
	}

	/**
	 * Creates an {@link IContentProposalProvider} to propose
	 * {@link IDockerContainer} names based on the current text.
	 * 
	 * @param items
	 * @return
	 */
	private IContentProposalProvider getContainerNameContentProposalProvider(
			final Combo containerSelectionCombo) {
		return (contents, position) -> {
			final List<IContentProposal> proposals = new ArrayList<>();
			for (String containerName : containerSelectionCombo.getItems()) {
				if (containerName.contains(contents)) {
					proposals.add(new ContentProposal(containerName,
							containerName, containerName, position));
				}
			}
			return proposals.toArray(new IContentProposal[0]);
		};
	}

	private void validateInput() {
		final String selectedContainerName = model.getContainerName();
		final Object[] containerNames = model.getContainerNames().toArray();
		final String containerAlias = model.getContainerAlias();
		if (selectedContainerName == null || selectedContainerName.isEmpty()) {
			setOkButtonEnabled(false);
		} else if (Arrays.binarySearch(containerNames, 0, containerNames.length,
				selectedContainerName) < 0) {
			setOkButtonEnabled(false);
		} else if (containerAlias == null || containerAlias.isEmpty()) {
			setOkButtonEnabled(false);
			return;
		} else {
			setOkButtonEnabled(true);
		}
	}

	private void setOkButtonEnabled(final boolean enabled) {
		getButton(IDialogConstants.OK_ID).setEnabled(enabled);
	}

	class ContainerLinkDialogModel extends BaseDatabindingModel {

		public static final String CONTAINER_NAME = "containerName"; //$NON-NLS-1$

		public static final String CONTAINER_ALIAS = "containerAlias"; //$NON-NLS-1$

		private String containerName;

		private String containerAlias;

		public ContainerLinkDialogModel() {
		}

		public ContainerLinkDialogModel(
				final ContainerLinkModel selectedContainerLink) {
			this();
			this.containerName = selectedContainerLink.getContainerName();
			this.containerAlias = selectedContainerLink.getContainerAlias();
		}

		public IDockerConnection getConnection() {
			return connection;
		}

		private List<String> getContainerNames() {
			return containerNames;
		}

		public List<IDockerContainer> getContainers() {
			return getConnection().getContainers();
		}

		public String getContainerName() {
			return containerName;
		}

		public void setContainerName(final String containerName) {
			firePropertyChange(CONTAINER_NAME, this.containerName,
					this.containerName = containerName);
		}

		public String getContainerAlias() {
			return containerAlias;
		}

		public void setContainerAlias(final String containerAlias) {
			firePropertyChange(CONTAINER_ALIAS, this.containerAlias,
					this.containerAlias = containerAlias);
		}

		@Override
		public int hashCode() {
			final int prime = 31;
			int result = 1;
			result = prime * result + getOuterType().hashCode();
			result = prime * result + ((containerAlias == null) ? 0
					: containerAlias.hashCode());
			result = prime * result
					+ ((containerName == null) ? 0 : containerName.hashCode());
			return result;
		}

		@Override
		public boolean equals(Object obj) {
			if (this == obj)
				return true;
			if (obj == null)
				return false;
			if (getClass() != obj.getClass())
				return false;
			ContainerLinkDialogModel other = (ContainerLinkDialogModel) obj;
			if (!getOuterType().equals(other.getOuterType()))
				return false;
			if (containerAlias == null) {
				if (other.containerAlias != null)
					return false;
			} else if (!containerAlias.equals(other.containerAlias))
				return false;
			if (containerName == null) {
				if (other.containerName != null)
					return false;
			} else if (!containerName.equals(other.containerName))
				return false;
			return true;
		}

		private ContainerLinkDialog getOuterType() {
			return ContainerLinkDialog.this;
		}

	}

}
