/*******************************************************************************
 *  Copyright (c) 2011 GitHub Inc.
 *  All rights reserved. This program and the accompanying materials
 *  are made available under the terms of the Eclipse Public License v1.0
 *  which accompanies this distribution, and is available at
 *  http://www.eclipse.org/legal/epl-v10.html
 *
 *  Contributors:
 *    Kevin Sawicki (GitHub Inc.) - initial API and implementation
 *******************************************************************************/
package org.eclipse.mylyn.internal.github.ui.gist;

import java.io.File;
import java.util.ArrayList;
import java.util.List;

import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.OperationCanceledException;
import org.eclipse.core.runtime.Platform;
import org.eclipse.jface.action.Action;
import org.eclipse.jface.action.IMenuListener;
import org.eclipse.jface.action.IMenuManager;
import org.eclipse.jface.action.MenuManager;
import org.eclipse.jface.action.ToolBarManager;
import org.eclipse.jface.layout.GridDataFactory;
import org.eclipse.jface.viewers.ArrayContentProvider;
import org.eclipse.jface.viewers.ColumnViewerToolTipSupport;
import org.eclipse.jface.viewers.IOpenListener;
import org.eclipse.jface.viewers.OpenEvent;
import org.eclipse.jface.viewers.StructuredSelection;
import org.eclipse.jface.viewers.TableViewer;
import org.eclipse.jface.window.ToolTip;
import org.eclipse.mylyn.commons.ui.CommonImages;
import org.eclipse.mylyn.commons.ui.TableViewerSupport;
import org.eclipse.mylyn.commons.workbench.forms.CommonFormUtil;
import org.eclipse.mylyn.internal.tasks.core.TaskAttachment;
import org.eclipse.mylyn.internal.tasks.ui.TasksUiPlugin;
import org.eclipse.mylyn.internal.tasks.ui.commands.OpenTaskAttachmentHandler;
import org.eclipse.mylyn.internal.tasks.ui.editors.AttachmentTableLabelProvider;
import org.eclipse.mylyn.internal.tasks.ui.editors.EditorUtil;
import org.eclipse.mylyn.internal.tasks.ui.editors.TaskEditorAttachmentPart;
import org.eclipse.mylyn.internal.tasks.ui.util.TasksUiMenus;
import org.eclipse.mylyn.internal.tasks.ui.wizards.TaskAttachmentWizard.Mode;
import org.eclipse.mylyn.tasks.core.ITaskAttachment;
import org.eclipse.mylyn.tasks.core.data.TaskAttribute;
import org.eclipse.mylyn.tasks.ui.editors.AbstractTaskEditorPart;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
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.Label;
import org.eclipse.swt.widgets.Menu;
import org.eclipse.swt.widgets.Table;
import org.eclipse.swt.widgets.TableColumn;
import org.eclipse.swt.widgets.TableItem;
import org.eclipse.ui.IWorkbenchPage;
import org.eclipse.ui.forms.IManagedForm;
import org.eclipse.ui.forms.events.ExpansionAdapter;
import org.eclipse.ui.forms.events.ExpansionEvent;
import org.eclipse.ui.forms.widgets.ExpandableComposite;
import org.eclipse.ui.forms.widgets.FormToolkit;
import org.eclipse.ui.forms.widgets.ScrolledForm;
import org.eclipse.ui.forms.widgets.Section;

/**
 * Gist editor attachment part. Modeled after {@link TaskEditorAttachmentPart}
 * but with less columns.
 */
public class GistAttachmentPart extends AbstractTaskEditorPart {

	private static final String ID_POPUP_MENU = "org.eclipse.mylyn.tasks.ui.editor.menu.attachments"; //$NON-NLS-1$

	private final String[] attachmentsColumns = {
			org.eclipse.mylyn.internal.tasks.ui.editors.Messages.TaskEditorAttachmentPart_Name,
			org.eclipse.mylyn.internal.tasks.ui.editors.Messages.TaskEditorAttachmentPart_Size,
			org.eclipse.mylyn.internal.tasks.ui.editors.Messages.TaskEditorAttachmentPart_Creator };

	private final int[] attachmentsColumnWidths = { 150, 70, 100 };

	private List<TaskAttribute> attachments;

	private boolean hasIncoming;

	private MenuManager menuManager;

	private Composite attachmentsComposite;

	private Table attachmentsTable;

	/**
	 * Create gist editor attachment part
	 */
	public GistAttachmentPart() {
		setPartName(Messages.GistAttachmentPart_PartName);
	}

	/**
	 * @see org.eclipse.mylyn.tasks.ui.editors.AbstractTaskEditorPart#createControl(org.eclipse.swt.widgets.Composite,
	 *      org.eclipse.ui.forms.widgets.FormToolkit)
	 */
	public void createControl(Composite parent, final FormToolkit toolkit) {
		initialize();

		final Section section = createSection(parent, toolkit, hasIncoming);
		section.setText(getPartName() + " (" + attachments.size() + ")"); //$NON-NLS-1$ //$NON-NLS-2$
		if (hasIncoming)
			expandSection(toolkit, section);
		else
			section.addExpansionListener(new ExpansionAdapter() {
				@Override
				public void expansionStateChanged(ExpansionEvent event) {
					if (attachmentsComposite == null) {
						expandSection(toolkit, section);
						getTaskEditorPage().reflow();
					}
				}
			});
		setSection(toolkit, section);
	}

	private void expandSection(FormToolkit toolkit, Section section) {
		attachmentsComposite = toolkit.createComposite(section);
		attachmentsComposite.setLayout(EditorUtil.createSectionClientLayout());
		attachmentsComposite.setLayoutData(new GridData(GridData.FILL_BOTH));

		getTaskEditorPage().registerDefaultDropListener(section);

		if (attachments.size() > 0)
			createAttachmentTable(toolkit, attachmentsComposite);
		else {
			Label label = toolkit
					.createLabel(
							attachmentsComposite,
							org.eclipse.mylyn.internal.tasks.ui.editors.Messages.TaskEditorAttachmentPart_No_attachments);
			getTaskEditorPage().registerDefaultDropListener(label);
		}

		createButtons(attachmentsComposite, toolkit);

		toolkit.paintBordersFor(attachmentsComposite);
		section.setClient(attachmentsComposite);
	}

	/**
	 * @see org.eclipse.ui.forms.AbstractFormPart#dispose()
	 */
	public void dispose() {
		if (menuManager != null)
			menuManager.dispose();
		super.dispose();
	}

	private void createAttachmentTable(FormToolkit toolkit,
			final Composite attachmentsComposite) {
		attachmentsTable = toolkit.createTable(attachmentsComposite, SWT.MULTI
				| SWT.FULL_SELECTION);
		attachmentsTable.setLinesVisible(true);
		attachmentsTable.setHeaderVisible(true);
		attachmentsTable.setLayout(new GridLayout());
		GridDataFactory.fillDefaults().align(SWT.FILL, SWT.FILL)
				.grab(true, false).hint(500, SWT.DEFAULT)
				.applyTo(attachmentsTable);
		attachmentsTable.setData(FormToolkit.KEY_DRAW_BORDER,
				FormToolkit.TREE_BORDER);

		for (int i = 0; i < attachmentsColumns.length; i++) {
			TableColumn column = new TableColumn(attachmentsTable, SWT.LEFT, i);
			column.setText(attachmentsColumns[i]);
			column.setWidth(attachmentsColumnWidths[i]);
			column.setMoveable(true);
			if (i == 0) {
				attachmentsTable.setSortColumn(column);
				attachmentsTable.setSortDirection(SWT.DOWN);
			}
		}
		// size column
		attachmentsTable.getColumn(1).setAlignment(SWT.RIGHT);

		TableViewer attachmentsViewer = new TableViewer(attachmentsTable);
		attachmentsViewer.setUseHashlookup(true);
		attachmentsViewer.setColumnProperties(attachmentsColumns);
		ColumnViewerToolTipSupport.enableFor(attachmentsViewer,
				ToolTip.NO_RECREATE);

		attachmentsViewer.setSorter(new GistAttachmentSorter());

		List<ITaskAttachment> attachmentList = new ArrayList<ITaskAttachment>(
				attachments.size());
		for (TaskAttribute attribute : attachments) {
			ITaskAttachment taskAttachment = new TaskAttachment(getModel()
					.getTaskRepository(), getModel().getTask(), attribute);
			getTaskData().getAttributeMapper().updateTaskAttachment(
					taskAttachment, attribute);
			attachmentList.add(taskAttachment);
		}
		attachmentsViewer.setContentProvider(new ArrayContentProvider());
		attachmentsViewer.setLabelProvider(new AttachmentTableLabelProvider(
				getModel(), getTaskEditorPage().getAttributeEditorToolkit()) {

			public String getColumnText(Object element, int columnIndex) {
				if (columnIndex > 0)
					columnIndex++;
				return super.getColumnText(element, columnIndex);
			}

			public Image getColumnImage(Object element, int columnIndex) {
				if (columnIndex > 0)
					columnIndex++;
				return super.getColumnImage(element, columnIndex);
			}

		});
		attachmentsViewer.addOpenListener(new IOpenListener() {
			public void open(OpenEvent event) {
				openAttachments(event);
			}
		});
		attachmentsViewer.addSelectionChangedListener(getTaskEditorPage());
		attachmentsViewer.setInput(attachmentList.toArray());

		menuManager = new MenuManager();
		menuManager.setRemoveAllWhenShown(true);
		menuManager.addMenuListener(new IMenuListener() {
			public void menuAboutToShow(IMenuManager manager) {
				TasksUiMenus.fillTaskAttachmentMenu(manager);
			}
		});
		getTaskEditorPage().getEditorSite().registerContextMenu(ID_POPUP_MENU,
				menuManager, attachmentsViewer, true);
		Menu menu = menuManager.createContextMenu(attachmentsTable);
		attachmentsTable.setMenu(menu);

		new TableViewerSupport(attachmentsViewer, getStateFile());
	}

	private File getStateFile() {
		IPath stateLocation = Platform.getStateLocation(TasksUiPlugin
				.getDefault().getBundle());
		return stateLocation.append("GistAttachmentPart.xml").toFile(); //$NON-NLS-1$
	}

	private void createButtons(Composite attachmentsComposite,
			FormToolkit toolkit) {
		final Composite attachmentControlsComposite = toolkit
				.createComposite(attachmentsComposite);
		attachmentControlsComposite.setLayout(new GridLayout(2, false));
		attachmentControlsComposite.setLayoutData(new GridData(
				GridData.BEGINNING));

		Button attachFileButton = toolkit
				.createButton(
						attachmentControlsComposite,
						org.eclipse.mylyn.internal.tasks.ui.editors.Messages.TaskEditorAttachmentPart_Attach_,
						SWT.PUSH);
		attachFileButton.setImage(CommonImages
				.getImage(CommonImages.FILE_PLAIN));
		attachFileButton.addSelectionListener(new SelectionAdapter() {
			@Override
			public void widgetSelected(SelectionEvent e) {
				EditorUtil.openNewAttachmentWizard(getTaskEditorPage(),
						Mode.DEFAULT, null);
			}
		});
		getTaskEditorPage().registerDefaultDropListener(attachFileButton);
	}

	private void initialize() {
		attachments = getTaskData().getAttributeMapper().getAttributesByType(
				getTaskData(), TaskAttribute.TYPE_ATTACHMENT);
		for (TaskAttribute attachmentAttribute : attachments)
			if (getModel().hasIncomingChanges(attachmentAttribute)) {
				hasIncoming = true;
				break;
			}
	}

	/**
	 * @see org.eclipse.mylyn.tasks.ui.editors.AbstractTaskEditorPart#fillToolBar(org.eclipse.jface.action.ToolBarManager)
	 */
	protected void fillToolBar(ToolBarManager toolBarManager) {
		Action attachFileAction = new Action() {
			@Override
			public void run() {
				EditorUtil.openNewAttachmentWizard(getTaskEditorPage(),
						Mode.DEFAULT, null);
			}
		};
		attachFileAction
				.setToolTipText(org.eclipse.mylyn.internal.tasks.ui.editors.Messages.TaskEditorAttachmentPart_Attach_);
		attachFileAction.setImageDescriptor(CommonImages.FILE_PLAIN_SMALL);
		toolBarManager.add(attachFileAction);
	}

	protected void openAttachments(OpenEvent event) {
		List<ITaskAttachment> attachments = new ArrayList<ITaskAttachment>();

		StructuredSelection selection = (StructuredSelection) event
				.getSelection();

		List<?> items = selection.toList();
		for (Object item : items)
			if (item instanceof ITaskAttachment)
				attachments.add((ITaskAttachment) item);

		if (attachments.isEmpty())
			return;

		IWorkbenchPage page = getTaskEditorPage().getSite()
				.getWorkbenchWindow().getActivePage();
		try {
			OpenTaskAttachmentHandler.openAttachments(page, attachments);
		} catch (OperationCanceledException e) {
			// canceled
		}
	}

	@Override
	public boolean setFormInput(Object input) {
		if (input instanceof String) {
			if (attachments != null)
				for (TaskAttribute attachmentAttribute : attachments) {
					if (input.equals(attachmentAttribute.getId())) {
						CommonFormUtil.setExpanded(
								(ExpandableComposite) getControl(), true);
						return selectReveal(attachmentAttribute);
					}
				}
		}
		return super.setFormInput(input);
	}

	public boolean selectReveal(TaskAttribute attachmentAttribute) {
		if (attachmentAttribute == null || attachmentsTable == null)
			return false;

		TableItem[] attachments = attachmentsTable.getItems();
		int index = 0;
		for (TableItem attachment : attachments) {
			Object data = attachment.getData();
			if (data instanceof ITaskAttachment) {
				ITaskAttachment attachmentData = ((ITaskAttachment) data);
				if (attachmentData.getTaskAttribute().getValue()
						.equals(attachmentAttribute.getValue())) {
					attachmentsTable.deselectAll();
					attachmentsTable.select(index);
					IManagedForm mform = getManagedForm();
					ScrolledForm form = mform.getForm();
					EditorUtil.focusOn(form, attachmentsTable);
					return true;
				}
			}
			index++;
		}
		return false;
	}

}
