/*******************************************************************************
 * 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.views;

import java.util.List;

import org.eclipse.jface.action.MenuManager;
import org.eclipse.jface.layout.GridDataFactory;
import org.eclipse.jface.layout.GridLayoutFactory;
import org.eclipse.jface.layout.TableColumnLayout;
import org.eclipse.jface.viewers.ColumnLabelProvider;
import org.eclipse.jface.viewers.ColumnWeightData;
import org.eclipse.jface.viewers.ISelection;
import org.eclipse.jface.viewers.IStructuredSelection;
import org.eclipse.jface.viewers.StructuredSelection;
import org.eclipse.jface.viewers.TableViewer;
import org.eclipse.jface.viewers.TableViewerColumn;
import org.eclipse.jface.viewers.Viewer;
import org.eclipse.jface.viewers.ViewerFilter;
import org.eclipse.linuxtools.internal.vagrant.ui.SWTImagesFactory;
import org.eclipse.linuxtools.vagrant.core.EnumVMStatus;
import org.eclipse.linuxtools.vagrant.core.IVagrantBox;
import org.eclipse.linuxtools.vagrant.core.IVagrantConnection;
import org.eclipse.linuxtools.vagrant.core.IVagrantVM;
import org.eclipse.linuxtools.vagrant.core.IVagrantVMListener;
import org.eclipse.linuxtools.vagrant.core.VagrantService;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.ModifyListener;
import org.eclipse.swt.events.SelectionListener;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Menu;
import org.eclipse.swt.widgets.Table;
import org.eclipse.swt.widgets.TableColumn;
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;

public class VagrantVMView extends ViewPart implements IVagrantVMListener {

	public static final String VIEW_ID = "org.eclipse.linuxtools.vagrant.ui.vagrantVMView"; //$NON-NLS-1$

	private final static String DaemonMissing = "ViewerDaemonMissing.msg"; //$NON-NLS-1$

	private Form form;
	private Text search;
	private TableViewer viewer;
	private IVagrantConnection connection;

	@Override
	public void setFocus() {
	}

	@Override
	public void dispose() {
		connection.removeVMListener(this);
		super.dispose();
	}

	private void hookContextMenu() {
		MenuManager menuMgr = new MenuManager("#PopupMenu"); //$NON-NLS-1$
		menuMgr.setRemoveAllWhenShown(true);
		Menu menu = menuMgr.createContextMenu(viewer.getControl());
		viewer.getControl().setMenu(menu);
		getSite().registerContextMenu(menuMgr, viewer);
	}

	@Override
	public void createPartControl(final Composite parent) {
		final FormToolkit toolkit = new FormToolkit(parent.getDisplay());
		form = toolkit.createForm(parent);
		form.setText(DVMessages.getString(DaemonMissing));
		final Composite container = form.getBody();
		GridLayoutFactory.fillDefaults().numColumns(1).margins(0, 0).applyTo(container);
		GridDataFactory.fillDefaults().align(SWT.FILL, SWT.FILL).grab(true, true).applyTo(container);
		createTableViewer(container);
		getSite().registerContextMenu(new MenuManager(), null);
		hookContextMenu();
	}

	private void createTableViewer(final Composite container) {
		search = new Text(container, SWT.SEARCH | SWT.ICON_SEARCH);
		GridDataFactory.fillDefaults().align(SWT.FILL, SWT.FILL).grab(true, false).applyTo(search);
		search.addModifyListener(onSearch());
		Composite tableArea = new Composite(container, SWT.NONE);
		GridLayoutFactory.fillDefaults().numColumns(1).margins(0,  0).applyTo(tableArea);
		GridDataFactory.fillDefaults().align(SWT.FILL, SWT.FILL).grab(true, true).applyTo(tableArea);

		final TableColumnLayout tableLayout = new TableColumnLayout();
		tableArea.setLayout(tableLayout);
		this.viewer = new TableViewer(tableArea, SWT.FULL_SELECTION | SWT.MULTI | SWT.H_SCROLL | SWT.V_SCROLL);
		this.viewer.setContentProvider(new VagrantVMContentProvider());
		final Table table = viewer.getTable();
		GridLayoutFactory.fillDefaults().numColumns(1).margins(0,  0).applyTo(table);
		GridDataFactory.fillDefaults().align(SWT.FILL, SWT.FILL).grab(true, true).applyTo(table);
		table.setLinesVisible(true);
		table.setHeaderVisible(true);
		// 'Name' column
		final TableViewerColumn nameColumn = createColumn(DVMessages.getString("NAME")); //$NON-NLS-1$
		setLayout(nameColumn, tableLayout, 150);
		nameColumn.setLabelProvider(new ColumnLabelProvider() {
			@Override
			public String getText(final Object element) {
				if (element instanceof IVagrantVM) {
					return ((IVagrantVM)element).name();
				}
				return super.getText(element);
			}

			@Override
			public Image getImage(Object element) {
				if (element instanceof IVagrantVM) {
					final IVagrantVM container = (IVagrantVM) element;
					final String state = container.state();
					if (EnumVMStatus.RUNNING
							.equals(EnumVMStatus.fromStatusMessage(state))) {
						return SWTImagesFactory.DESC_CONTAINER_STARTED
								.createImage();
					} else {
						return SWTImagesFactory.DESC_CONTAINER_STOPPED
								.createImage();
					}
				}
				return super.getImage(element);
			}
		});
		// 'Provider' column
		final TableViewerColumn imageColumn = createColumn(DVMessages.getString("PROVIDER")); //$NON-NLS-1$
		setLayout(imageColumn, tableLayout, 150);
		imageColumn.setLabelProvider(new ColumnLabelProvider() {
			@Override
			public String getText(final Object element) {
				if (element instanceof IVagrantVM) {
					return ((IVagrantVM) element).provider();
				}
				return super.getText(element);
			}
		});
		// 'State' column
		final TableViewerColumn creationDateColumn = createColumn(DVMessages.getString("STATE")); //$NON-NLS-1$
		setLayout(creationDateColumn, tableLayout, 150);
		creationDateColumn.setLabelProvider(new ColumnLabelProvider() {
			@Override
			public String getText(final Object element) {
				if (element instanceof IVagrantVM) {
					return ((IVagrantVM) element).state();
				}
				return super.getText(element);
			}
		});
		// 'State Description' column
		final TableViewerColumn commandColumn = createColumn(DVMessages.getString("STATE_DESC")); //$NON-NLS-1$
		setLayout(commandColumn, tableLayout, 150);
		commandColumn.setLabelProvider(new ColumnLabelProvider() {
			@Override
			public String getText(final Object element) {
				if (element instanceof IVagrantVM) {
					return ((IVagrantVM) element).state_desc();
				}
				return super.getText(element);
			}
		});
		// comparator
		final VagrantVMComparator comparator = new VagrantVMComparator(this.viewer);
		viewer.setComparator(comparator);
		// apply search filter
		this.viewer.addFilter(getContainersFilter());
		setConnection(VagrantService.getInstance());
		connection.addVMListener(this);
		// get the current selection in the tableviewer
		getSite().setSelectionProvider(viewer);
	}

	private TableViewerColumn createColumn(final String title) {
		final TableViewerColumn propertyColumn = new TableViewerColumn(viewer, SWT.BORDER);
		propertyColumn.getColumn().setText(title);
		propertyColumn.getColumn().addSelectionListener(onColumnSelected());
		return propertyColumn;
	}

	private SelectionListener onColumnSelected() {
		return SelectionListener.widgetSelectedAdapter(e -> {
			final TableColumn sortColumn = (TableColumn) e.getSource();
			final VagrantVMComparator comparator = (VagrantVMComparator) viewer
					.getComparator();
			comparator.setColumn(sortColumn);
			viewer.refresh();
		});
	}

	private void setLayout(final TableViewerColumn viewerColumn, final TableColumnLayout tableLayout, final int weight) {
		tableLayout.setColumnData(viewerColumn.getColumn(), new ColumnWeightData(weight, true));
	}

	/**
	 * Filters {@link IVagrantVM} and {@link IVagrantBox} using the input text in the search widget of this view.
	 * @return
	 */
	private ModifyListener onSearch() {
		return e -> VagrantVMView.this.viewer.refresh();
	}

	/**
	 * @return a {@link ViewerFilter} that will retain {@link IVagrantVM} that match the
	 * content of the {@link VagrantVMView#search} text widget.
	 */
	private ViewerFilter getContainersFilter() {
		return new ViewerFilter() {

			@Override
			public boolean select(Viewer viewer, Object parentElement, Object element) {
				if(element instanceof IVagrantVM) {
					return element.toString().contains(VagrantVMView.this.search.getText());
				}
				return false;
			}
		};
	}

	@Override
	public void listChanged(final IVagrantConnection connection,
			final List<IVagrantVM> containers) {
		if (!viewer.getControl().isDisposed()) {
			Display.getDefault().asyncExec(() -> {
				// remember the current selection before the viewer is refreshed
				final ISelection currentSelection = VagrantVMView.this.viewer
						.getSelection();
				VagrantVMView.this.viewer.refresh();
				refreshViewTitle();
				// restore the selection
				if (currentSelection != null) {
					VagrantVMView.this.viewer.setSelection(currentSelection);
				}
			});
		}
	}

	/**
	 * @return the {@link IVagrantConnection} used to display the current {@link IVagrantVM}
	 */
	public IVagrantConnection getConnection() {
		return connection;
	}

	public void setConnection(IVagrantConnection conn) {
		this.connection = conn;
		if (conn != null) {
			viewer.setInput(conn);
			refreshViewTitle();
		} else {
			viewer.setInput(new IVagrantVM[0]);
			form.setText(DVMessages.getString(DaemonMissing));
		}
	}

	/**
	 * @return the current selection
	 */
	public IStructuredSelection getStructuredSelection() {
		if (this.viewer != null) {
			return this.viewer.getStructuredSelection();
		}
		return StructuredSelection.EMPTY;
	}

	/**
	 * @return the {@link TableViewer} showing the containers
	 */
	public TableViewer getViewer() {
		return this.viewer;
	}

	private void refreshViewTitle() {
		if (this.viewer == null || this.viewer.getControl().isDisposed()
				|| this.form == null || this.connection == null) {
			return;
		} else if (!this.connection.isVMsLoaded()) {
			form.setText(connection.getName());
		} else {
			this.form.setText(DVMessages.getFormattedString(
					"VagrantVMViewTitle.all.msg", connection.getName(), //$NON-NLS-1$
					Integer.toString(connection.getVMs().size())));
		}
	}

}
