/*******************************************************************************
 * Copyright (c) 2010 IBM Corporation and others.
 * 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:
 *     IBM Corporation - initial API and implementation
 *     Dariusz Luksza <dariusz@luksza.org>
 *******************************************************************************/
package org.eclipse.egit.ui.internal.synchronize;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IWorkspaceRoot;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.egit.core.project.RepositoryMapping;
import org.eclipse.egit.ui.Activator;
import org.eclipse.egit.ui.UIPreferences;
import org.eclipse.egit.ui.internal.CommonUtils;
import org.eclipse.egit.ui.internal.UIIcons;
import org.eclipse.egit.ui.internal.UIText;
import org.eclipse.jface.dialogs.Dialog;
import org.eclipse.jface.layout.GridDataFactory;
import org.eclipse.jface.preference.JFacePreferences;
import org.eclipse.jface.resource.JFaceResources;
import org.eclipse.jface.viewers.CellEditor;
import org.eclipse.jface.viewers.ColumnLabelProvider;
import org.eclipse.jface.viewers.ComboBoxCellEditor;
import org.eclipse.jface.viewers.EditingSupport;
import org.eclipse.jface.viewers.ITreeContentProvider;
import org.eclipse.jface.viewers.StyledCellLabelProvider;
import org.eclipse.jface.viewers.TreeSelection;
import org.eclipse.jface.viewers.TreeViewer;
import org.eclipse.jface.viewers.TreeViewerColumn;
import org.eclipse.jface.viewers.Viewer;
import org.eclipse.jface.viewers.ViewerCell;
import org.eclipse.jface.wizard.WizardPage;
import org.eclipse.jgit.lib.Ref;
import org.eclipse.jgit.lib.Repository;
import org.eclipse.swt.SWT;
import org.eclipse.swt.custom.CCombo;
import org.eclipse.swt.custom.StyleRange;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.graphics.Color;
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;

class GitSynchronizeWizardPage extends WizardPage {

	private static final IWorkspaceRoot ROOT = ResourcesPlugin.getWorkspace()
			.getRoot();

	private boolean forceFetch;

	private boolean shouldIncludeLocal = false;

	private TreeViewer treeViewer;

	private final Map<Repository, Set<IProject>> resources = new HashMap<Repository, Set<IProject>>();

	private final Map<Repository, String> selectedBranches = new HashMap<Repository, String>();

	private final Image branchImage = UIIcons.BRANCH.createImage();

	private final Image repositoryImage = UIIcons.REPOSITORY.createImage();

	GitSynchronizeWizardPage() {
		super(GitSynchronizeWizardPage.class.getName());
		setTitle(UIText.GitBranchSynchronizeWizardPage_title);
		setMessage(UIText.GitBranchSynchronizeWizardPage_description, WARNING);
	}

	public void createControl(Composite parent) {
		Composite composite = new Composite(parent, SWT.NONE);
		GridLayout layout = new GridLayout(1, false);
		layout.marginWidth = 0;
		layout.marginHeight = 0;
		composite.setLayout(layout);

		for (IProject project : ROOT.getProjects()) {
			RepositoryMapping repositoryMapping = RepositoryMapping
					.getMapping(project);
			if (repositoryMapping == null)
				continue;
			Repository repo = repositoryMapping.getRepository();
			Set<IProject> projects = resources.get(repo);
			if (projects == null) {
				projects = new HashSet<IProject>();
				resources.put(repo, projects);
			}
			projects.add(project);
		}

		treeViewer = new TreeViewer(composite, SWT.BORDER | SWT.V_SCROLL
				| SWT.H_SCROLL | SWT.FULL_SELECTION);
		treeViewer.getTree().setLinesVisible(true);
		treeViewer.getTree().setHeaderVisible(true);
		treeViewer.getTree().setLayoutData(
				new GridData(SWT.FILL, SWT.FILL, true, true));

		TreeViewerColumn projectsColumn = new TreeViewerColumn(treeViewer,
				SWT.LEAD);
		projectsColumn.getColumn().setText(
				UIText.GitBranchSynchronizeWizardPage_repositories);
		projectsColumn.getColumn().setImage(repositoryImage);
		projectsColumn.setLabelProvider(new StyledCellLabelProvider() {
			@Override
			public void update(ViewerCell cell) {
				Object element = cell.getElement();
				if (element instanceof Repository) {
					Repository repo = (Repository) element;
					String descr = ""; //$NON-NLS-1$
					try {
						descr += " [" + repo.getBranch() + "]";//$NON-NLS-1$ //$NON-NLS-2$
					} catch (IOException e) {
						Activator.logError(e.getMessage(), e);
					}

					Color decorationsColor = JFaceResources.getColorRegistry()
							.get(JFacePreferences.DECORATIONS_COLOR);

					String repoName = repo.getWorkTree().getName();
					int repoNameLen = repoName.length();
					StyleRange styleRange = new StyleRange(repoNameLen,
							repoNameLen + descr.length(), decorationsColor,
							null);

					cell.setImage(repositoryImage);
					cell.setText(repoName + descr);
					cell.setStyleRanges(new StyleRange[] { styleRange });
				}

				super.update(cell);
			}
		});

		TreeViewerColumn dstColumn = new TreeViewerColumn(treeViewer, SWT.LEAD);
		dstColumn.getColumn().setText(
				UIText.GitBranchSynchronizeWizardPage_destination);
		dstColumn.getColumn().setImage(branchImage);
		dstColumn.getColumn().setWidth(200);
		final ComboBoxCellEditor branchesEditor = new ComboBoxCellEditor(
				treeViewer.getTree(), new String[0]);
		branchesEditor
				.setActivationStyle(ComboBoxCellEditor.DROP_DOWN_ON_KEY_ACTIVATION
						| ComboBoxCellEditor.DROP_DOWN_ON_MOUSE_ACTIVATION);
		((CCombo) branchesEditor.getControl()).addSelectionListener(new SelectionAdapter() {
			@Override
			public void widgetSelected(SelectionEvent e) {
				CCombo combo = (CCombo) e.widget;
				TreeSelection sel = (TreeSelection) treeViewer.getSelection();
				int selectedIdx = combo.getSelectionIndex();
				Repository repo = (Repository) sel.getFirstElement();

				if (selectedIdx != -1) {
					selectedBranches.put(repo, combo.getItem(selectedIdx));
					setPageComplete(true);
				} else {
					selectedBranches.put(repo, null);
					setPageComplete(false);
				}
			}
		});
		dstColumn.setEditingSupport(new EditingSupport(treeViewer) {
			@Override
			protected void setValue(Object element, Object value) {
				int intValue = ((Integer) value).intValue();
				if (intValue == -1)
					return;

				CCombo combo = (CCombo) branchesEditor.getControl();
				String branch = combo.getItem(intValue);

				selectedBranches.put((Repository) element, branch);
				treeViewer.refresh(element, true);

				validatePage();
			}

			@Override
			protected Object getValue(Object element) {
				String branch = selectedBranches.get(element);
				CCombo combo = (CCombo) branchesEditor.getControl();
				int index = branch == null ? 0 : combo.indexOf(branch);
				return Integer.valueOf(index);
			}

			@Override
			protected CellEditor getCellEditor(Object element) {
				Repository repo = (Repository) element;
				List<String> refs = new LinkedList<String>(repo.getAllRefs()
						.keySet());

				List<Ref> additionalRefs;
				try {
					additionalRefs = repo.getRefDatabase().getAdditionalRefs();
				} catch (IOException e) {
					additionalRefs = null;
				}
				if (additionalRefs != null)
					for (Ref ref : additionalRefs)
						refs.add(ref.getName());

				Collections.sort(refs, CommonUtils.STRING_ASCENDING_COMPARATOR);

				branchesEditor.setItems(refs.toArray(new String[refs.size()]));

				return branchesEditor;
			}

			@Override
			protected boolean canEdit(Object element) {
				return true;
			}
		});
		dstColumn.setLabelProvider(new ColumnLabelProvider() {
			@Override
			public String getText(Object element) {
				String branch = selectedBranches.get(element);
				return branch == null ? "" : branch; //$NON-NLS-1$
			}
		});

		treeViewer.setContentProvider(new ITreeContentProvider() {
			public void inputChanged(Viewer viewer, Object oldInput,
					Object newInput) {
				// nothing to do
			}

			public void dispose() {
				// nothing to do
			}

			public Object[] getElements(Object inputElement) {
				return (Object[]) inputElement;
			}

			public boolean hasChildren(Object element) {
				return false;
			}

			public Object getParent(Object element) {
				return null;
			}

			public Object[] getChildren(Object parentElement) {
				return new Object[0];
			}
		});

		List<Repository> repositoriesList = new ArrayList<Repository>(
				resources.keySet());
		Collections.sort(repositoriesList, new Comparator<Repository>() {
			public int compare(Repository o1, Repository o2) {
				String name1 = o1.getWorkTree().getName();
				String name2 = o2.getWorkTree().getName();

				return name1.compareToIgnoreCase(name2);
			}
		});

		treeViewer.setInput(repositoriesList
				.toArray(new Repository[repositoriesList.size()]));
		projectsColumn.getColumn().pack();

		Composite buttonsComposite = new Composite(composite, SWT.NONE);
		layout = new GridLayout(4, false);
		layout.numColumns = 1;
		layout.marginWidth = 0;
		layout.marginHeight = 0;
		buttonsComposite.setLayout(layout);
		buttonsComposite.setLayoutData(GridDataFactory.fillDefaults()
				.grab(true, false).create());

		final Button fetchChanges = new Button(buttonsComposite, SWT.CHECK);
		fetchChanges
		.setText(UIText.GitBranchSynchronizeWizardPage_fetchChangesFromRemote);
		fetchChanges.addSelectionListener(new SelectionAdapter() {
			@Override
			public void widgetSelected(SelectionEvent e) {
				forceFetch = fetchChanges.getSelection();
			}
		});
		fetchChanges.setLayoutData(GridDataFactory.fillDefaults()
				.grab(true, false).create());
		fetchChanges.setSelection(Activator.getDefault().getPreferenceStore()
				.getBoolean(UIPreferences.SYNC_VIEW_FETCH_BEFORE_LAUNCH));

		final Button includeLocal = new Button(buttonsComposite, SWT.CHECK);
		includeLocal
				.setText(UIText.GitBranchSynchronizeWizardPage_includeUncommitedChanges);
		includeLocal.addSelectionListener(new SelectionAdapter() {
			@Override
			public void widgetSelected(SelectionEvent e) {
				shouldIncludeLocal = includeLocal.getSelection();
			}
		});
		includeLocal.setLayoutData(GridDataFactory.fillDefaults()
				.grab(true, false).create());

		Dialog.applyDialogFont(composite);
		setPageComplete(false);
		setControl(composite);
	}

	@Override
	public void dispose() {
		if (branchImage != null)
			branchImage.dispose();

		if (repositoryImage != null)
			repositoryImage.dispose();

		super.dispose();
	}

	private void validatePage() {
		setPageComplete(!selectedBranches.isEmpty());
	}

	Map<Repository, String> getSelectedBranches() {
		return selectedBranches;
	}

	Set<IProject> getSelectedProjects() {
		Set<IProject> projects = new HashSet<IProject>();
		for (Repository repo : selectedBranches.keySet())
			projects.addAll(resources.get(repo));

		return projects;
	}

	boolean shouldIncludeLocal() {
		return shouldIncludeLocal;
	}

	boolean forceFetch() {
		return forceFetch;
	}

}
