/*******************************************************************************
 * Copyright (c) 2016, 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.docker.ui.preferences;

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

import org.eclipse.jface.preference.PreferencePage;
import org.eclipse.jface.resource.ImageDescriptor;
import org.eclipse.jface.viewers.ColumnWeightData;
import org.eclipse.jface.viewers.ILabelProviderListener;
import org.eclipse.jface.viewers.IStructuredContentProvider;
import org.eclipse.jface.viewers.ITableLabelProvider;
import org.eclipse.jface.viewers.TableLayout;
import org.eclipse.jface.viewers.TableViewer;
import org.eclipse.jface.viewers.Viewer;
import org.eclipse.jface.window.Window;
import org.eclipse.linuxtools.docker.core.IRegistryAccount;
import org.eclipse.linuxtools.internal.docker.core.RegistryAccountManager;
import org.eclipse.linuxtools.internal.docker.ui.wizards.RegistryAccountDialog;
import org.eclipse.swt.SWT;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Event;
import org.eclipse.swt.widgets.Group;
import org.eclipse.swt.widgets.Listener;
import org.eclipse.swt.widgets.Table;
import org.eclipse.swt.widgets.TableColumn;
import org.eclipse.ui.IWorkbench;
import org.eclipse.ui.IWorkbenchPreferencePage;

/**
 * @since 2.0
 */
public class DockerRegistryAccountPreferencePage extends PreferencePage
		implements IWorkbenchPreferencePage, Listener {

	/**
	 * @since 2.1
	 */
	public static final String ACCOUNT_PREFERENCE_PAGE_ID = "org.eclipse.linuxtools.docker.ui.preferences.DockerRegistryAccountPreferencePage"; //$NON-NLS-1$

	/**
	 * Inner class to keep track of password modifications (without committing
	 * them to the keyring) while the user modifies the preferences.
	 */
	protected class PasswordModification {
		protected static final int ADD = 1;
		protected static final int DELETE = 2;

		protected int changeFlag;
		protected IRegistryAccount info;

		protected PasswordModification(int changeFlag,
				IRegistryAccount info) {
			this.changeFlag = changeFlag;
			this.info = info;
		}
	}

	private final class PasswordContentProvider
			implements IStructuredContentProvider, ITableLabelProvider {

		/**
		 * @see org.eclipse.jface.viewers.IStructuredContentProvider#getElements(Object)
		 */
		@Override
		public Object[] getElements(Object inputElement) {
			return passwords.toArray();
		}

		/**
		 * @see org.eclipse.jface.viewers.IContentProvider#dispose()
		 */
		@Override
		public void dispose() {
		}

		/**
		 * @see org.eclipse.jface.viewers.IContentProvider#inputChanged(Viewer,
		 *      Object, Object)
		 */
		@Override
		public void inputChanged(Viewer viewer, Object oldInput,
				Object newInput) {
		}

		/**
		 * @see org.eclipse.jface.viewers.ITableLabelProvider#getColumnImage(Object,
		 *      int)
		 */
		@Override
		public Image getColumnImage(Object element, int columnIndex) {
			return null;
		}

		/**
		 * @see org.eclipse.jface.viewers.ITableLabelProvider#getColumnText(Object,
		 *      int)
		 */
		@Override
		public String getColumnText(Object element, int columnIndex) {
			if (element instanceof IRegistryAccount) {
				IRegistryAccount info = (IRegistryAccount) element;
				switch (columnIndex) {
				case 0:
					return info.getServerAddress();
				case 1:
					return info.getUsername();
				case 2:
					return info.getEmail();
				}
			}

			// Should never get here
			return null;
		}

		/**
		 * @see org.eclipse.jface.viewers.IBaseLabelProvider#addListener(ILabelProviderListener)
		 */
		@Override
		public void addListener(ILabelProviderListener listener) {
		}

		/**
		 * @see org.eclipse.jface.viewers.IBaseLabelProvider#isLabelProperty(Object,
		 *      String)
		 */
		@Override
		public boolean isLabelProperty(Object element, String property) {
			return false;
		}

		/**
		 * @see org.eclipse.jface.viewers.IBaseLabelProvider#removeListener(ILabelProviderListener)
		 */
		@Override
		public void removeListener(ILabelProviderListener listener) {
		}

	}

	public DockerRegistryAccountPreferencePage() {
		noDefaultAndApplyButton();
		provider = new PasswordContentProvider();
		modifications = new ArrayList<>();
	}

	public DockerRegistryAccountPreferencePage(String title) {
		super(title);
	}

	public DockerRegistryAccountPreferencePage(String title,
			ImageDescriptor image) {
		super(title, image);
	}

	// SWT Widgets and content providers
	private Table pwdTable;
	private TableViewer pwdTableViewer;
	private PasswordContentProvider provider;
	private Button addButton, changeButton, removeButton;

	// List of information for table
	private List<IRegistryAccount> passwords;

	// List to keep track of additions / deletions / changes. We need to
	// keep track of these until the user decides whether to cancel the
	// preference
	// page (and we forget about the changes) or press ok (and we commit the
	// changes)
	private List<PasswordModification> modifications;

	/**
	 * @see org.eclipse.jface.preference.PreferencePage#createContents(Composite)
	 */
	@Override
	protected Control createContents(Composite parent) {

		Composite page = createComposite(parent, 1, 2, false, null, -1, -1, GridData.FILL);
		GridData gd = (GridData) page.getLayoutData();
		gd.grabExcessHorizontalSpace = true;
		gd.grabExcessVerticalSpace = true;

		// SystemWidgetHelpers.createLabel(page,
		// SystemResources.RESID_PREF_SIGNON_DESCRIPTION, 2);

		// Password table
		pwdTable = new Table(page, SWT.FULL_SELECTION | SWT.MULTI | SWT.V_SCROLL
				| SWT.H_SCROLL | SWT.BORDER);
		pwdTable.setLinesVisible(true);
		pwdTable.setHeaderVisible(true);
		pwdTable.addListener(SWT.Selection, this);

		TableLayout tableLayout = new TableLayout();
		tableLayout.addColumnData(new ColumnWeightData(100, true));
		tableLayout.addColumnData(new ColumnWeightData(100, true));
		tableLayout.addColumnData(new ColumnWeightData(100, true));
		pwdTable.setLayout(tableLayout);

		gd = new GridData(GridData.FILL_BOTH);
		gd.grabExcessHorizontalSpace = true;
		gd.grabExcessVerticalSpace = true;

		pwdTable.setLayoutData(gd);

		// Server Address column
		TableColumn hostnameColumn = new TableColumn(pwdTable, SWT.NONE);
		hostnameColumn
				.setText("Server Address");

		// Username column
		TableColumn sysTypeColumn = new TableColumn(pwdTable, SWT.NONE);
		sysTypeColumn.setText("Username");

		// Email column
		TableColumn useridColumn = new TableColumn(pwdTable, SWT.NONE);
		useridColumn.setText("Email");

		pwdTableViewer = new TableViewer(pwdTable);
		pwdTableViewer.setContentProvider(provider);
		pwdTableViewer.setLabelProvider(provider);
		pwdTableViewer.setInput(passwords);

		// Create the Button bar for add, change and remove
		Composite buttonBar = createComposite(page, 1, 1, false, null, -1, -1, GridData.FILL);
		gd = (GridData) buttonBar.getLayoutData();
		gd.grabExcessHorizontalSpace = false;
		gd.grabExcessVerticalSpace = true;

		addButton = createPushButton(buttonBar, this,
				"Add",
				"Add a new Docker registry account.");
		changeButton = createPushButton(buttonBar, this,
				"Edit",
				"Edit an existing Docker registry account.");
		removeButton = createPushButton(buttonBar, this,
				"Remove",
				"Remove an existing Docker registry account.");

		changeButton.setEnabled(false);
		removeButton.setEnabled(false);
		return parent;
	}

	/**
	 * @see org.eclipse.ui.IWorkbenchPreferencePage#init(IWorkbench)
	 */
	@Override
	public void init(IWorkbench workbench) {
		// get a local copy of the accounts
		passwords = new ArrayList<>(
				RegistryAccountManager.getInstance().getAccounts());

		// refresh password table
		if (pwdTableViewer != null) {
			pwdTableViewer.refresh();
		}
	}

	/**
	 * @see org.eclipse.swt.widgets.Listener#handleEvent(Event)
	 */
	@Override
	public void handleEvent(Event event) {
		if (event.type == SWT.Selection) {
			if (event.widget == addButton) {
				RegistryAccountDialog dialog = new RegistryAccountDialog(
						getShell(),
						"New Registry Account");
				if (dialog.open() == Window.OK) {
					IRegistryAccount info = dialog
							.getSignonInformation();
					passwords.add(info);
					modifications.add(new PasswordModification(
							PasswordModification.ADD, info));

					pwdTableViewer.refresh();
					pwdTable.select(passwords.size() - 1); // select the new
															// entry
				}

			} else if (event.widget == changeButton) {
				RegistryAccountDialog dialog = new RegistryAccountDialog(
						getShell(),
						"Edit Registry Account");
				int index = pwdTable.getSelectionIndex();
				IRegistryAccount info = passwords
						.get(index);
				dialog.setInputData(info);
				if (dialog.open() == Window.OK) {
					// Remove old and add new
					info = dialog.getSignonInformation();
					IRegistryAccount oldInfo = passwords
							.remove(index);
					passwords.add(index, info);

					modifications.add(new PasswordModification(
							PasswordModification.DELETE, oldInfo));
					modifications.add(new PasswordModification(
							PasswordModification.ADD, info));

					pwdTableViewer.refresh();
					pwdTable.select(index);
				}

			} else if (event.widget == removeButton) {
				int[] indicies = pwdTable.getSelectionIndices();
				for (int idx = indicies.length - 1; idx >= 0; idx--) {
					RegistryAccountManager.getInstance()
							.remove(passwords.get(indicies[idx]));
					modifications.add(new PasswordModification(
							PasswordModification.DELETE,
							passwords
									.remove(indicies[idx])));
				}

				pwdTableViewer.refresh();
			}

			// Update table buttons based on changes
			switch (pwdTable.getSelectionCount()) {
			case 0:
				changeButton.setEnabled(false);
				removeButton.setEnabled(false);
				break;

			case 1:
				changeButton.setEnabled(true);
				removeButton.setEnabled(true);
				break;

			default:
				changeButton.setEnabled(false);
				removeButton.setEnabled(true);
				break;
			}
		}
	}

	/**
	 * @see org.eclipse.jface.preference.IPreferencePage#performOk()
	 */
	@Override
	public boolean performOk() {

		if (modifications.size() > 0) {
			PasswordModification mod;
			RegistryAccountManager manager = RegistryAccountManager
					.getInstance();

			for (int i = 0; i < modifications.size(); i++) {
				mod = modifications.get(i);

				if (mod.changeFlag == PasswordModification.ADD) {
					manager.add(mod.info);
				} else if (mod.changeFlag == PasswordModification.DELETE) {
					manager.remove(mod.info);
				}
			}

			modifications.clear();
		}

		return super.performOk();
	}

	/**
	 * @see org.eclipse.jface.preference.IPreferencePage#performCancel()
	 */
	@Override
	public boolean performCancel() {
		modifications.clear();
		return super.performCancel();
	}

	private static Composite createComposite(Composite parent, int parentSpan, int numColumns, boolean border, String label, int marginSize, int spacingSize, int verticalAlignment) {
		//border = true;
		boolean borderNeeded = border;
		if (label != null)
			borderNeeded = true; // force the case
		int style = SWT.NULL;
		if (borderNeeded)
			style |= SWT.SHADOW_ETCHED_IN;
		Composite composite = null;
		if (borderNeeded) {
			composite = new Group(parent, style);
			if (label != null)
				 ((Group) composite).setText(label);
		} else {
			composite = new Composite(parent, style);
		}
		//GridLayout
		GridLayout layout = new GridLayout();
		layout.numColumns = numColumns;
		if (marginSize != -1) {
			layout.marginWidth = 0;
			layout.marginHeight = 0;
		}
		if (spacingSize != -1) {
			layout.horizontalSpacing = 0;
			layout.verticalSpacing = 0;
		}
		composite.setLayout(layout);
		//GridData
		GridData data = new GridData();
		data.horizontalSpan = parentSpan;
		data.horizontalAlignment = GridData.FILL;
		data.grabExcessHorizontalSpace = true;

		data.verticalAlignment = verticalAlignment;
		data.grabExcessVerticalSpace = false;

		composite.setLayoutData(data);
		return composite;
	}

	public static Button createPushButton(Composite group, Listener listener, String label, String tooltip) {
		Button button = new Button(group, SWT.PUSH);
		button.setText(label);
		if (listener != null)
			button.addListener(SWT.Selection, listener);
		GridData data = new GridData();
		data.horizontalAlignment = GridData.FILL;
		data.grabExcessHorizontalSpace = true;
		button.setLayoutData(data);
		if (tooltip != null)
			button.setToolTipText(tooltip);
		return button;
	}

}
