/*******************************************************************************
 *  Copyright (c) 2011 Christian Trutz
 *  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:
 *    Christian Trutz - initial API and implementation
 *******************************************************************************/
package org.eclipse.mylyn.internal.github.ui;

import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.text.MessageFormat;
import java.util.Collections;
import java.util.List;

import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.egit.github.core.Language;
import org.eclipse.egit.github.core.Repository;
import org.eclipse.egit.github.core.SearchRepository;
import org.eclipse.egit.github.core.service.RepositoryService;
import org.eclipse.egit.github.core.util.UrlUtils;
import org.eclipse.egit.ui.internal.UIIcons;
import org.eclipse.egit.ui.internal.provisional.wizards.GitRepositoryInfo;
import org.eclipse.egit.ui.internal.provisional.wizards.IRepositorySearchResult;
import org.eclipse.egit.ui.internal.provisional.wizards.NoRepositoryInfoException;
import org.eclipse.jface.layout.GridDataFactory;
import org.eclipse.jface.layout.GridLayoutFactory;
import org.eclipse.jface.operation.IRunnableWithProgress;
import org.eclipse.jface.viewers.ArrayContentProvider;
import org.eclipse.jface.viewers.DelegatingStyledCellLabelProvider;
import org.eclipse.jface.viewers.DelegatingStyledCellLabelProvider.IStyledLabelProvider;
import org.eclipse.jface.viewers.ILabelProviderListener;
import org.eclipse.jface.viewers.ISelection;
import org.eclipse.jface.viewers.ISelectionChangedListener;
import org.eclipse.jface.viewers.IStructuredSelection;
import org.eclipse.jface.viewers.SelectionChangedEvent;
import org.eclipse.jface.viewers.StructuredSelection;
import org.eclipse.jface.viewers.StyledString;
import org.eclipse.jface.viewers.TableViewer;
import org.eclipse.jface.wizard.WizardPage;
import org.eclipse.mylyn.internal.github.core.GitHub;
import org.eclipse.mylyn.internal.github.core.GitHubException;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.ModifyEvent;
import org.eclipse.swt.events.ModifyListener;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Combo;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Text;
import org.eclipse.ui.PlatformUI;

/**
 * Search for GitHub repositories wizard page.
 */
@SuppressWarnings("restriction")
public class RepositorySearchWizardPage extends WizardPage implements
		IRepositorySearchResult {

	private SearchRepository[] repositories = null;

	private final RepositoryService repositoryService;

	private Text searchText;

	/**
	 *
	 */
	public RepositorySearchWizardPage() {
		super("repoSearchPage", Messages.RepositorySearchWizardPage_Title, null); //$NON-NLS-1$
		setDescription(Messages.RepositorySearchWizardPage_Description);
		setPageComplete(false);

		repositoryService = new RepositoryService();
		GitHub.configureClient(repositoryService.getClient());
	}

	/**
	 * Get selected repositories
	 *
	 * @return repositories
	 */
	protected SearchRepository[] getRepositories() {
		return repositories;
	}

	/**
	 *
	 */
	public void createControl(Composite parent) {
		final Composite root = new Composite(parent, SWT.NONE);
		GridLayoutFactory.swtDefaults().numColumns(1).applyTo(root);

		Composite rowOne = new Composite(root, SWT.NONE);
		GridLayoutFactory.fillDefaults().numColumns(2).applyTo(rowOne);

		Label searchForLabel = new Label(rowOne, SWT.NONE);
		searchForLabel
				.setText(Messages.RepositorySearchWizardPage_SearchForRepositories);

		final Combo languageCombo = new Combo(rowOne, SWT.READ_ONLY
				| SWT.DROP_DOWN);
		languageCombo.add(Messages.RepositorySearchWizardPage_AnyLanguage);

		for (Language language : Language.values())
			languageCombo.add(language.getValue());

		languageCombo.select(0);

		Composite rowTwo = new Composite(root, SWT.NONE);
		GridLayoutFactory.fillDefaults().numColumns(2).applyTo(rowTwo);
		GridDataFactory.fillDefaults().grab(true, false).applyTo(rowTwo);

		searchText = new Text(rowTwo, SWT.SINGLE | SWT.BORDER);
		GridDataFactory.swtDefaults().align(SWT.FILL, SWT.CENTER)
				.grab(true, false).applyTo(searchText);

		final Button searchButton = new Button(rowTwo, SWT.NONE);
		searchButton.setText(Messages.RepositorySearchWizardPage_SearchButton);
		searchButton.setEnabled(false);

		final TableViewer repoListViewer = new TableViewer(root);
		GridDataFactory.fillDefaults().grab(true, true)
				.applyTo(repoListViewer.getControl());
		repoListViewer.setContentProvider(new ArrayContentProvider());

		repoListViewer.setLabelProvider(new DelegatingStyledCellLabelProvider(
				new IStyledLabelProvider() {

					private Image repoImage = UIIcons.REPOSITORY.createImage();

					public void removeListener(ILabelProviderListener listener) {
					}

					public boolean isLabelProperty(Object element,
							String property) {
						return false;
					}

					public void dispose() {
						repoImage.dispose();
					}

					public void addListener(ILabelProviderListener listener) {
					}

					public StyledString getStyledText(Object element) {
						StyledString styled = new StyledString();
						SearchRepository repo = (SearchRepository) element;
						styled.append(repo.getOwner() + "/" + repo.getName()); //$NON-NLS-1$
						String language = repo.getLanguage();
						if (language != null && language.length() > 0)
							styled.append(" (" + language + ")", //$NON-NLS-1$ //$NON-NLS-2$
									StyledString.QUALIFIER_STYLER);

						int forks = repo.getForks();
						if (forks != 1)
							styled.append(MessageFormat.format(
									Messages.RepositorySearchWizardPage_Forks,
									forks), StyledString.COUNTER_STYLER);
						else
							styled.append(
									Messages.RepositorySearchWizardPage_Fork,
									StyledString.COUNTER_STYLER);

						int watchers = repo.getWatchers();
						if (watchers != 1)
							styled.append(
									MessageFormat
											.format(Messages.RepositorySearchWizardPage_Watchers,
													watchers),
									StyledString.COUNTER_STYLER);
						else
							styled.append(
									Messages.RepositorySearchWizardPage_Watcher,
									StyledString.COUNTER_STYLER);

						return styled;
					}

					public Image getImage(Object element) {
						return repoImage;
					}
				}));

		repoListViewer
				.addSelectionChangedListener(new ISelectionChangedListener() {

					public void selectionChanged(SelectionChangedEvent event) {
						validate(repoListViewer);
					}
				});

		searchText.addModifyListener(new ModifyListener() {

			public void modifyText(ModifyEvent e) {
				searchButton
						.setEnabled(searchText.getText().trim().length() != 0);
			}
		});

		searchButton.addSelectionListener(new SelectionAdapter() {

			public void widgetSelected(SelectionEvent selectionEvent) {
				String language = null;
				if (languageCombo.getSelectionIndex() > 0)
					language = languageCombo.getText();
				search(language, searchText.getText().trim(), repoListViewer);
			}
		});

		setControl(root);
	}

	private void validate(TableViewer viewer) {
		ISelection selection = viewer.getSelection();
		if (selection instanceof IStructuredSelection) {
			Object[] selected = ((IStructuredSelection) selection).toArray();
			repositories = new SearchRepository[selected.length];
			System.arraycopy(selected, 0, repositories, 0, selected.length);
		}
		setPageComplete(!selection.isEmpty());
	}

	public void setVisible(boolean visible) {
		super.setVisible(visible);
		if (visible)
			searchText.setFocus();
	}

	private void search(final String language, final String text,
			final TableViewer viewer) {
		viewer.setSelection(StructuredSelection.EMPTY);
		try {
			getContainer().run(true, true, new IRunnableWithProgress() {

				public void run(IProgressMonitor monitor)
						throws InvocationTargetException, InterruptedException {
					monitor.beginTask(
							MessageFormat
									.format(Messages.RepositorySearchWizardPage_Searching,
											text), 10);
					try {
						final List<SearchRepository> repositories = repositoryService
								.searchRepositories(text.trim(), language);
						PlatformUI.getWorkbench().getDisplay()
								.syncExec(new Runnable() {

									public void run() {
										if (viewer.getControl().isDisposed())
											return;
										setMessage(
												MessageFormat
														.format(Messages.RepositorySearchWizardPage_Found,
																repositories
																		.size()),
												INFORMATION);
										viewer.setInput(repositories);
										validate(viewer);
									}
								});
					} catch (IOException e) {
						throw new InvocationTargetException(GitHubException
								.wrap(e));
					}
				}
			});
			setErrorMessage(null);
		} catch (InvocationTargetException e) {
			viewer.setInput(Collections.emptyList());
			Throwable cause = e.getCause();
			if (cause == null)
				cause = e;
			setErrorMessage(MessageFormat.format(
					Messages.RepositorySearchWizardPage_Error,
					cause.getLocalizedMessage()));
		} catch (InterruptedException e) {
			GitHubUi.logError(e);
		}
	}

	public GitRepositoryInfo getGitRepositoryInfo()
			throws NoRepositoryInfoException {
		String cloneUrl = null;
		try {
			Repository fullRepo = repositoryService
					.getRepository(repositories[0]);
			cloneUrl = fullRepo.getCloneUrl();
		} catch (IOException e) {
			throw new NoRepositoryInfoException(e.getMessage(), e);
		}
		return new GitRepositoryInfo(cloneUrl);
	}
}
