/*******************************************************************************
 * Copyright (c) 2010 Neil Bartlett.
 * 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:
 *     Neil Bartlett - initial API and implementation
 *******************************************************************************/
package bndtools.editor.pkgpatterns;

import java.lang.reflect.InvocationTargetException;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.TreeSet;

import org.bndtools.api.ILogger;
import org.bndtools.api.Logger;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.NullProgressMonitor;
import org.eclipse.jdt.core.IJavaElement;
import org.eclipse.jdt.core.IPackageFragment;
import org.eclipse.jdt.core.search.IJavaSearchConstants;
import org.eclipse.jdt.core.search.IJavaSearchScope;
import org.eclipse.jdt.core.search.SearchEngine;
import org.eclipse.jdt.core.search.SearchMatch;
import org.eclipse.jdt.core.search.SearchParticipant;
import org.eclipse.jdt.core.search.SearchPattern;
import org.eclipse.jdt.core.search.SearchRequestor;
import org.eclipse.jface.fieldassist.IContentProposal;
import org.eclipse.jface.operation.IRunnableContext;
import org.eclipse.jface.operation.IRunnableWithProgress;

import aQute.lib.exceptions.Exceptions;
import bndtools.javamodel.IJavaSearchContext;
import bndtools.utils.CachingContentProposalProvider;

public class PkgPatternsProposalProvider extends CachingContentProposalProvider {
    private static final ILogger logger = Logger.getLogger(PkgPatternsProposalProvider.class);

    private final IJavaSearchContext searchContext;

    public PkgPatternsProposalProvider(IJavaSearchContext searchContext) {
        this.searchContext = searchContext;
    }

    @Override
    protected Collection<? extends IContentProposal> doGenerateProposals(String contents, int position) {
        String prefix = contents.substring(0, position);

        final int replaceFromPos;
        if (prefix.startsWith("!")) { //$NON-NLS-1$
            prefix = prefix.substring(1);
            replaceFromPos = 1;
        } else {
            replaceFromPos = 0;
        }

        Comparator<PkgPatternProposal> comparator = new Comparator<PkgPatternProposal>() {
            @Override
            public int compare(PkgPatternProposal o1, PkgPatternProposal o2) {
                int result = o1.getPackageFragment()
                    .getElementName()
                    .compareTo(o2.getPackageFragment()
                        .getElementName());
                if (result == 0) {
                    result = Boolean.valueOf(o1.isWildcard())
                        .compareTo(Boolean.valueOf(o2.isWildcard()));
                }
                return result;
            }
        };
        final TreeSet<PkgPatternProposal> result = new TreeSet<PkgPatternProposal>(comparator);

        final IJavaSearchScope scope = SearchEngine.createJavaSearchScope(new IJavaElement[] {
            searchContext.getJavaProject()
        });
        final SearchPattern pattern = SearchPattern.createPattern("*" + prefix + "*", IJavaSearchConstants.PACKAGE, IJavaSearchConstants.DECLARATIONS, SearchPattern.R_PATTERN_MATCH);
        final SearchRequestor requestor = new SearchRequestor() {
            @Override
            public void acceptSearchMatch(SearchMatch match) throws CoreException {
                IPackageFragment pkg = (IPackageFragment) match.getElement();
                // Reject the default package and any package starting with
                // "java." since these cannot be imported
                if (pkg.isDefaultPackage() || pkg.getElementName()
                    .startsWith("java."))
                    return;

                result.add(new PkgPatternProposal(pkg, false, replaceFromPos));
                result.add(new PkgPatternProposal(pkg, true, replaceFromPos));
            }
        };
        IRunnableWithProgress runnable = new IRunnableWithProgress() {
            @Override
            public void run(IProgressMonitor monitor) throws InvocationTargetException, InterruptedException {
                try {
                    new SearchEngine().search(pattern, new SearchParticipant[] {
                        SearchEngine.getDefaultSearchParticipant()
                    }, scope, requestor, monitor);
                } catch (CoreException e) {
                    throw new InvocationTargetException(e);
                }
            }
        };

        try {
            IRunnableContext runContext = searchContext.getRunContext();
            if (runContext != null) {
                runContext.run(false, false, runnable);
            } else {
                runnable.run(new NullProgressMonitor());
            }
            return result;
        } catch (InvocationTargetException e) {
            logger.logError("Error searching for packages.", Exceptions.unrollCause(e, InvocationTargetException.class));
            return Collections.emptyList();
        } catch (InterruptedException e) {
            Thread.currentThread()
                .interrupt();
            return Collections.emptyList();
        }
    }

    @Override
    protected boolean match(String contents, int position, IContentProposal proposal) {
        final String prefix = contents.substring(0, position);
        return ((PkgPatternProposal) proposal).getPackageFragment()
            .getElementName()
            .toLowerCase()
            .indexOf(prefix.toLowerCase()) > -1;
    }

}
