/*******************************************************************************
 * Copyright (c) 2012 SAP 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:
 *      Sascha Scholz (SAP) - initial API and implementation
 *******************************************************************************/

package org.eclipse.mylyn.internal.gerrit.ui.egit;

import java.lang.reflect.InvocationTargetException;
import java.net.URISyntaxException;
import java.util.HashMap;

import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.egit.ui.internal.provisional.wizards.GitRepositoryInfo;
import org.eclipse.egit.ui.internal.provisional.wizards.IRepositorySearchResult;
import org.eclipse.jface.layout.GridDataFactory;
import org.eclipse.jface.layout.GridLayoutFactory;
import org.eclipse.jface.operation.IRunnableWithProgress;
import org.eclipse.jface.viewers.DoubleClickEvent;
import org.eclipse.jface.viewers.IDoubleClickListener;
import org.eclipse.jface.viewers.ISelectionChangedListener;
import org.eclipse.jface.viewers.ITreeSelection;
import org.eclipse.jface.viewers.SelectionChangedEvent;
import org.eclipse.jface.viewers.TreePath;
import org.eclipse.jface.viewers.TreeViewer;
import org.eclipse.jface.wizard.WizardPage;
import org.eclipse.jgit.lib.ConfigConstants;
import org.eclipse.jgit.lib.Constants;
import org.eclipse.mylyn.commons.workbench.SubstringPatternFilter;
import org.eclipse.mylyn.internal.gerrit.core.GerritConnector;
import org.eclipse.mylyn.internal.gerrit.core.GerritCorePlugin;
import org.eclipse.mylyn.internal.gerrit.core.GerritUtil;
import org.eclipse.mylyn.internal.gerrit.core.client.GerritClient;
import org.eclipse.mylyn.internal.gerrit.core.client.GerritConfiguration;
import org.eclipse.mylyn.internal.gerrit.core.client.GerritException;
import org.eclipse.mylyn.internal.tasks.ui.actions.AddRepositoryAction;
import org.eclipse.mylyn.tasks.core.TaskRepository;
import org.eclipse.osgi.util.NLS;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.KeyAdapter;
import org.eclipse.swt.events.KeyEvent;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.layout.GridData;
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.Group;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.ui.dialogs.FilteredTree;

import com.google.gerrit.reviewdb.AccountGeneralPreferences.DownloadScheme;
import com.google.gerrit.reviewdb.Project;

/**
 * @author Sascha Scholz
 */
public class GerritRepositorySearchPage extends WizardPage implements IRepositorySearchResult {

	private Combo cloneUriCombo;

	private Button refreshButton;

	public GerritRepositorySearchPage() {
		super(GerritRepositorySearchPage.class.getName());
		setTitle("Source Git Repository");
		setMessage("Select the Gerrit project");
	}

	@Override
	public void createControl(Composite parent) {
		Composite container = new Composite(parent, SWT.NONE);
		GridLayoutFactory.fillDefaults().numColumns(2).applyTo(container);
		GridDataFactory.fillDefaults().grab(true, true).applyTo(container);

		Composite composite = new Composite(container, SWT.NONE);
		GridLayoutFactory.fillDefaults().numColumns(1).applyTo(composite);
		GridDataFactory.fillDefaults().grab(true, true).applyTo(composite);

		FilteredTree tree = new FilteredTree(composite, SWT.SINGLE | SWT.BORDER | SWT.H_SCROLL | SWT.V_SCROLL,
				new SubstringPatternFilter(), true);
		final TreeViewer tv = tree.getViewer();
		GridDataFactory.fillDefaults().grab(true, true).applyTo(tree);
		tv.setContentProvider(new GerritRepositorySearchPageContentProvider());
		tv.setLabelProvider(new GerritRepositorySearchPageLabelProvider());
		tv.setInput(this);
		tv.addSelectionChangedListener(new ISelectionChangedListener() {
			@Override
			public void selectionChanged(SelectionChangedEvent event) {
				ITreeSelection selection = (ITreeSelection) event.getSelection();
				GerritRepositorySearchPage.this.selectionChanged(tv, selection);
			}
		});
		tv.addDoubleClickListener(new IDoubleClickListener() {
			@Override
			public void doubleClick(DoubleClickEvent event) {
				ITreeSelection selection = (ITreeSelection) event.getSelection();
				GerritRepositorySearchPage.this.doubleClick(tv, selection);
			}
		});
		tv.getTree().addKeyListener(new KeyAdapter() {
			@Override
			public void keyPressed(KeyEvent event) {
				if (event.keyCode == SWT.F5) {
					refreshConfigIfTaskRepositoryIsSelected(tv);
				}
			}
		});

		final Composite buttonsComposite = new Composite(composite, SWT.NONE);
		GridLayoutFactory.fillDefaults().numColumns(2).applyTo(buttonsComposite);
		buttonsComposite.setLayoutData(new GridData(GridData.FILL_HORIZONTAL | GridData.GRAB_HORIZONTAL));
		buttonsComposite.setLayout(new GridLayout(2, false));

		Button addButton = new Button(buttonsComposite, SWT.NONE);
		addButton.setText("Add...");
		addButton.addSelectionListener(new SelectionAdapter() {
			@Override
			public void widgetSelected(SelectionEvent e) {
				Shell shell = getShell();
				if (shell != null && !shell.isDisposed()) {
					AddRepositoryAction addRepositoryAction = new AddRepositoryAction();
					addRepositoryAction.setPromptToAddQuery(false);
					TaskRepository addedRepository = addRepositoryAction.showWizard(shell,
							GerritConnector.CONNECTOR_KIND);
					if (addedRepository != null) {
						tv.refresh();
						refreshConfig(tv, addedRepository);
					}
				}
			}
		});

		refreshButton = new Button(buttonsComposite, SWT.NONE);
		refreshButton.setText("Refresh");
		refreshButton.setEnabled(false);
		refreshButton.addSelectionListener(new SelectionAdapter() {
			@Override
			public void widgetSelected(SelectionEvent e) {
				refreshConfigIfTaskRepositoryIsSelected(tv);
			}
		});

		final Group repositoryGroup = new Group(composite, SWT.NONE);
		repositoryGroup.setText("Git Repository");
		GridLayoutFactory.fillDefaults().numColumns(2).applyTo(repositoryGroup);
		repositoryGroup.setLayoutData(new GridData(GridData.FILL_HORIZONTAL | GridData.GRAB_HORIZONTAL));
		repositoryGroup.setLayout(new GridLayout(2, false));

		new Label(repositoryGroup, SWT.NULL).setText("URI:");
		cloneUriCombo = new Combo(repositoryGroup, SWT.DROP_DOWN);
		GridDataFactory.fillDefaults().grab(true, false).applyTo(cloneUriCombo);

		clearCloneUris();
		setControl(container);
	}

	private void selectionChanged(final TreeViewer tv, ITreeSelection selection) {
		clearError();
		if (selection.size() == 1) {
			refreshButton.setEnabled(true);
			if (selection.getFirstElement() instanceof Project) {
				TreePath path = selection.getPaths()[0];
				TaskRepository repository = (TaskRepository) path.getSegment(0);
				Project project = (Project) path.getSegment(1);
				try {
					GerritConfiguration config = GerritCorePlugin.getGerritClient(repository).getConfiguration();
					setCloneUris(GerritUtil.getCloneUris(config, repository, project));
				} catch (URISyntaxException e) {
					String message = NLS.bind("Unable to compute Git clone URI for Gerrit project {0}",
							project.getName());
					showError(e, message);
					clearCloneUris();
				}
			} else if (selection.getFirstElement() instanceof TaskRepository) {
				clearCloneUris();
			}
		} else {
			refreshButton.setEnabled(false);
			clearCloneUris();
		}
	}

	private void showError(Exception e, String message) {
		GerritCorePlugin.logError(message, e);
		setErrorMessage(message);
	}

	private void clearError() {
		setErrorMessage(null);
	}

	private void setCloneUris(HashMap<DownloadScheme, String> uriMap) {
		cloneUriCombo.removeAll();
		addCloneUriIfNotNull(uriMap, DownloadScheme.SSH);
		addCloneUriIfNotNull(uriMap, DownloadScheme.HTTP);
		addCloneUriIfNotNull(uriMap, DownloadScheme.ANON_HTTP);
		addCloneUriIfNotNull(uriMap, DownloadScheme.ANON_GIT);
		if (cloneUriCombo.getItemCount() > 0) {
			cloneUriCombo.select(0);
			cloneUriCombo.setEnabled(true);
			setPageComplete(true);
		} else {
			clearCloneUris();
			showError(null,
					"No download scheme is offered. Additional schemes may be available after a refresh of the repository configuration.");
		}
	}

	private void addCloneUriIfNotNull(HashMap<DownloadScheme, String> uriMap, DownloadScheme scheme) {
		String uri = uriMap.get(scheme);
		if (uri != null) {
			cloneUriCombo.add(uri);
		}
	}

	private void clearCloneUris() {
		cloneUriCombo.removeAll();
		cloneUriCombo.setText(""); //$NON-NLS-1$
		cloneUriCombo.setEnabled(false);
		setPageComplete(false);
	}

	private void doubleClick(TreeViewer tv, ITreeSelection selection) {
		clearError();
		GerritRepositorySearchPage.this.selectionChanged(tv, selection);
		if (selection.size() == 1 && selection.getFirstElement() instanceof TaskRepository) {
			Object element = selection.getFirstElement();
			if (tv.getExpandedState(element)) {
				tv.collapseToLevel(element, 1);
			} else {
				tv.expandToLevel(element, 1);
			}
		}
		if (isPageComplete()) {
			getContainer().showPage(getNextPage());
		}
	}

	private void refreshConfig(TreeViewer tv, final TaskRepository repository) {
		clearError();
		try {
			final GerritClient client = GerritCorePlugin.getGerritClient(repository);
			getContainer().run(true, true, new IRunnableWithProgress() {
				public void run(IProgressMonitor monitor) throws InvocationTargetException, InterruptedException {
					monitor.beginTask("Refreshing " + repository.getRepositoryLabel(), IProgressMonitor.UNKNOWN);
					try {
						client.refreshConfig(monitor);
					} catch (GerritException e) {
						throw new InvocationTargetException(e, e.getMessage());
					}
					monitor.done();
				}
			});
			tv.refresh(repository);
		} catch (InvocationTargetException e) {
			showError(e, e.getMessage());
		} catch (InterruptedException e) {
		}
	}

	private void refreshConfigIfTaskRepositoryIsSelected(final TreeViewer tv) {
		ITreeSelection selection = (ITreeSelection) tv.getSelection();
		Object element = selection.getFirstElement();
		if (element instanceof TaskRepository) {
			refreshConfig(tv, (TaskRepository) element);
		} else if (element instanceof Project) {
			TreePath path = selection.getPaths()[0];
			refreshConfig(tv, (TaskRepository) path.getSegment(0));
		}
	}

	@Override
	public GitRepositoryInfo getGitRepositoryInfo() {
		GitRepositoryInfo gitRepositoryInfo = new GitRepositoryInfo(cloneUriCombo.getText());
		addFetchReviewNotesRefSpec(gitRepositoryInfo);
		addPushToGerritRefSpec(gitRepositoryInfo);
		addCreateGerritChangeIdConfig(gitRepositoryInfo);
		return gitRepositoryInfo;
	}

	private void addFetchReviewNotesRefSpec(GitRepositoryInfo gitRepositoryInfo) {
		String notesRef = Constants.R_NOTES + "*"; //$NON-NLS-1$
		gitRepositoryInfo.addFetchRefSpec("+" + notesRef + ":" + notesRef); //$NON-NLS-1$ //$NON-NLS-2$
	}

	private void addPushToGerritRefSpec(GitRepositoryInfo gitRepositoryInfo) {
		gitRepositoryInfo.addPushInfo("HEAD:refs/for/master", null); //$NON-NLS-1$
	}

	private void addCreateGerritChangeIdConfig(GitRepositoryInfo gitRepositoryInfo) {
		gitRepositoryInfo.addRepositoryConfigProperty(ConfigConstants.CONFIG_GERRIT_SECTION, null,
				ConfigConstants.CONFIG_KEY_CREATECHANGEID, ConfigConstants.CONFIG_KEY_TRUE);
	}
}
