/*******************************************************************************
 * Copyright (c) 2017, 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 org.eclipse.core.databinding.Binding;
import org.eclipse.core.databinding.DataBindingContext;
import org.eclipse.core.databinding.UpdateValueStrategy;
import org.eclipse.core.databinding.beans.typed.BeanProperties;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.debug.core.ILaunchConfiguration;
import org.eclipse.jface.databinding.swt.typed.WidgetProperties;
import org.eclipse.jface.dialogs.IMessageProvider;
import org.eclipse.jface.layout.GridDataFactory;
import org.eclipse.jface.layout.GridLayoutFactory;
import org.eclipse.jface.wizard.WizardPage;
import org.eclipse.linuxtools.docker.core.DockerException;
import org.eclipse.linuxtools.docker.core.IDockerConnection;
import org.eclipse.linuxtools.docker.core.IDockerImage;
import org.eclipse.linuxtools.docker.ui.Activator;
import org.eclipse.linuxtools.internal.docker.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.layout.GridLayout;
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.Text;

/**
 * A {@link WizardPage} to let the user select the CPU and memory allocation for
 * the container, as well as the volumes to mount.
 */
public class ImageRunNetworkPage extends WizardPage {

	private final int COLUMNS = 2;

	private final static String NETWORK_MODE = "networkMode"; //$NON-NLS-1$

	private final DataBindingContext dbc = new DataBindingContext();
	private final ImageRunNetworkModel model;
	private final ILaunchConfiguration lastLaunchConfiguration;

	private Control containerControl; // control selecting container
	private Control otherControl; // control selecting other selection

	/**
	 * Default constructor.
	 *
	 * @param connection
	 *            the {@link IDockerConnection} to use
	 * @throws DockerException
	 *             if obtaining info from the given {@link IDockerConnection}
	 *             failed
	 *
	 */
	public ImageRunNetworkPage(final IDockerConnection connection) {
		super("ImageRunNetworkPage", //$NON-NLS-1$
				WizardMessages.getString("ImageRunNetworkPage.title"), //$NON-NLS-1$
				SWTImagesFactory.DESC_BANNER_REPOSITORY);
		setPageComplete(true);
		this.model = new ImageRunNetworkModel(connection);
		this.lastLaunchConfiguration = null;
	}

	/**
	 * Default constructor.
	 *
	 * @param selectedImage
	 *            the {@link IDockerImage} to run
	 * @param connection
	 *            the {@link IDockerConnection} to use
	 * @throws DockerException
	 *             if obtaining info from the given {@link IDockerConnection}
	 *             failed
	 *
	 */
	public ImageRunNetworkPage(
			final IDockerImage selectedImage,
			final ILaunchConfiguration lastLaunchConfiguration) {
		super("ImageRunNetworkPage", //$NON-NLS-1$
				WizardMessages.getString("ImageRunNetworkPage.title"), //$NON-NLS-1$
				SWTImagesFactory.DESC_BANNER_REPOSITORY);
		setPageComplete(true);
		this.model = new ImageRunNetworkModel(selectedImage);
		this.lastLaunchConfiguration = lastLaunchConfiguration;
	}

	public ImageRunNetworkModel getModel() {
		return model;
	}

	@Override
	public void createControl(Composite parent) {
		final 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);
		container.setLayout(new GridLayout());
		GridDataFactory.fillDefaults().align(SWT.FILL, SWT.FILL).span(1, 1)
				.grab(true, false).applyTo(container);
		GridLayoutFactory.fillDefaults().numColumns(COLUMNS).margins(6, 6)
				.applyTo(container);
		createNetworkModeContainer(container);
		setDefaultValues();

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

	private void createNetworkModeContainer(final Composite parent) {
		final Composite container = new Composite(parent, SWT.NONE);
		container.setLayout(new GridLayout());
		GridDataFactory.fillDefaults().align(SWT.FILL, SWT.FILL)
				.span(COLUMNS, 1).grab(true, false).applyTo(container);
		GridLayoutFactory.fillDefaults().spacing(10, 2).applyTo(container);

		// specify default network
		final Label modeLabel = new Label(container, SWT.NONE);

		modeLabel.setText(WizardMessages.getString("ImageRunNetworkPage.mode")); //$NON-NLS-1$
		// $NON-NLS-1$
		GridDataFactory.fillDefaults().align(SWT.FILL, SWT.CENTER).span(2, 1)
				.grab(false, false).applyTo(modeLabel);

		final int COLUMNS = 2;
		final int INDENT = 20;
		final Composite subContainer = new Composite(container, SWT.NONE);
		GridDataFactory.fillDefaults().align(SWT.FILL, SWT.FILL)
				.indent(INDENT, 0).span(COLUMNS, 1).grab(true, false)
				.applyTo(subContainer);
		GridLayoutFactory.fillDefaults().numColumns(COLUMNS).margins(6, 6)
				.spacing(10, 5).applyTo(subContainer);


		modeLabel.setEnabled(true);
		final Button defaultButton = new Button(subContainer, SWT.RADIO);
		bindButton(defaultButton, ImageRunNetworkModel.DEFAULT_MODE);
		defaultButton.setText(
				WizardMessages.getString("ImageRunNetworkPage.mode.default")); //$NON-NLS-1$
		GridDataFactory.fillDefaults().align(SWT.FILL, SWT.CENTER).span(2, 1)
				.applyTo(defaultButton);
		final Button bridgeButton = new Button(subContainer, SWT.RADIO);
		bridgeButton.setText(
				WizardMessages.getString("ImageRunNetworkPage.mode.bridge")); //$NON-NLS-1$
		bindButton(bridgeButton, ImageRunNetworkModel.BRIDGE_MODE);
		GridDataFactory.fillDefaults().align(SWT.FILL, SWT.CENTER).span(2, 1)
				.applyTo(bridgeButton);
		final Button hostButton = new Button(subContainer, SWT.RADIO);
		hostButton.setText(
				WizardMessages.getString("ImageRunNetworkPage.mode.host")); //$NON-NLS-1$
		bindButton(hostButton, ImageRunNetworkModel.HOST_MODE);
		GridDataFactory.fillDefaults().align(SWT.FILL, SWT.CENTER).span(2, 1)
				.applyTo(hostButton);
		final Button noneButton = new Button(subContainer, SWT.RADIO);
		noneButton.setText(
				WizardMessages.getString("ImageRunNetworkPage.mode.none")); // $NON-NLS-1$
		bindButton(noneButton, ImageRunNetworkModel.NONE_MODE);
		GridDataFactory.fillDefaults().align(SWT.FILL, SWT.CENTER).span(2, 1)
				.applyTo(noneButton);

		final Button containerButton = new Button(subContainer, SWT.RADIO);
		containerButton.setText(
				WizardMessages.getString("ImageRunNetworkPage.mode.container")); //$NON-NLS-1$
		// $NON-NLS-1$
		bindButton(containerButton, ImageRunNetworkModel.CONTAINER_MODE);
		GridDataFactory.fillDefaults().align(SWT.FILL, SWT.CENTER)
				.applyTo(containerButton);


		// Container list
		final Combo containerList = new Combo(subContainer, SWT.BORDER);

		setContainerControl(containerList);

		containerList
				.setItems(model.getContainerNames().toArray(new String[] {}));
		if (containerList.getItemCount() > 0)
			containerList.select(0);
		containerList.setEnabled(false);
		GridDataFactory.fillDefaults().align(SWT.FILL, SWT.CENTER)
				.grab(true, false).applyTo(containerList);
		dbc.bindValue(WidgetProperties.widgetSelection().observe(containerList),
				BeanProperties
						.value(ImageRunNetworkModel.class,
								ImageRunNetworkModel.SELECTED_CONTAINER)
						.observe(model));

		containerButton.addSelectionListener(
				onContainerSelection(containerButton, containerList));

		final Button otherButton = new Button(subContainer, SWT.RADIO);
		otherButton.setText(
				WizardMessages.getString("ImageRunNetworkPage.mode.other"));
		// $NON-NLS-1$
		bindButton(otherButton, ImageRunNetworkModel.OTHER_MODE);
		GridDataFactory.fillDefaults().align(SWT.FILL, SWT.CENTER)
				.applyTo(otherButton);

		// Container list
		final Text otherText = new Text(subContainer, SWT.SINGLE | SWT.BORDER);

		setOtherControl(otherText);

		otherText.setEnabled(false);

		GridDataFactory.fillDefaults().align(SWT.FILL, SWT.CENTER)
				.grab(true, false).applyTo(otherText);
		dbc.bindValue(WidgetProperties.text(SWT.Modify).observe(otherText),
				BeanProperties
						.value(ImageRunNetworkModel.class,
								ImageRunNetworkModel.OTHER_STRING)
						.observe(model));

		otherButton
				.addSelectionListener(onOtherSelection(otherButton, otherText));
	}

	@Override
	public boolean isPageComplete() {
		if (model == null)
			return false;
		if (ImageRunNetworkModel.CONTAINER_MODE.equals(model.getNetworkMode())) {
			if (((Combo)getContainerControl()).getText().isEmpty()) {
				setMessage(
						WizardMessages
								.getString("WarningNoContainerSelected.msg"), //$NON-NLS-1$
						IMessageProvider.WARNING);
				return false;
			} else if (ImageRunNetworkModel.OTHER_MODE
					.equals(model.getNetworkMode())) {
				if (((Text) getOtherControl()).getText().isEmpty()) {
					setMessage(
							WizardMessages
									.getString("WarningOtherFieldEmpty.msg"), //$NON-NLS-1$
							IMessageProvider.WARNING);
					return false;
				}
			}
		}
		setMessage(null, IMessageProvider.WARNING);
		return true;
	}

	private Control getContainerControl() {
		return containerControl;
	}

	private void setContainerControl(Control containerControl) {
		this.containerControl = containerControl;
	}

	private Control getOtherControl() {
		return otherControl;
	}

	private void setOtherControl(Control containerControl) {
		this.otherControl = containerControl;
	}

	/**
	 * Binds the given <code>network mode</code> value to the given
	 * {@link Button} when it is selected.
	 *
	 * @param button
	 *            the {@link Button} to bind
	 * @param strValue
	 *            the <code>network mode</code> to bind to the {@link Button}
	 * @return
	 */
	@SuppressWarnings("unchecked")
	private Binding bindButton(final Button button, final String strValue) {
		return dbc.bindValue(WidgetProperties.widgetSelection().observe(button),
				BeanProperties
						.value(ImageRunNetworkModel.class,
								ImageRunNetworkModel.NETWORK_MODE)
						.observe(model),
				new UpdateValueStrategy() {
					@Override
					public Object convert(Object value) {
						if (value.equals(Boolean.TRUE)) {
							return strValue;
						}
						return null;
					}

				}, new UpdateValueStrategy() {
					@Override
					public Object convert(final Object value) {
						return value.equals(strValue);
					}
				});
	}

	private SelectionListener onContainerSelection(final Button containerButton,
			final Combo containerList) {
		return SelectionListener.widgetSelectedAdapter(e -> {
			final boolean selection = containerButton.getSelection();
			containerList.setEnabled(selection);
		});
	}

	private SelectionListener onOtherSelection(final Button otherButton,
			final Text otherText) {
		return SelectionListener.widgetSelectedAdapter(e -> {
			final boolean selection = otherButton.getSelection();
			otherText.setEnabled(selection);
		});
	}

	private void setDefaultValues() {
		try {
			if (lastLaunchConfiguration != null) {
				String networkModeString = lastLaunchConfiguration.getAttribute(
						NETWORK_MODE, ImageRunNetworkModel.DEFAULT_MODE);
				if (networkModeString
						.startsWith(ImageRunNetworkModel.CONTAINER_MODE)) {
					String[] token = networkModeString.split(":");
					model.setNetworkMode(ImageRunNetworkModel.CONTAINER_MODE);
					if (token.length > 1)
						model.setSelectedContainer(token[1]);
					getContainerControl().setEnabled(true);
				} else {
					switch (networkModeString) {
					case ImageRunNetworkModel.BRIDGE_MODE:
					case ImageRunNetworkModel.HOST_MODE:
					case ImageRunNetworkModel.NONE_MODE:
					case ImageRunNetworkModel.DEFAULT_MODE:
						model.setNetworkMode(networkModeString);
						break;
					default:
						model.setNetworkMode(ImageRunNetworkModel.OTHER_MODE);
						model.setOtherString(networkModeString);
						getOtherControl().setEnabled(true);
						break;
					}
				}
			}
		} catch (CoreException e) {
			Activator.log(e);
		}
	}

}
