/*
 * Copyright (C) 2008 The Android Open Source Project
 *
 * Licensed under the Eclipse Public License, Version 1.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.eclipse.org/org/documents/epl-v10.php
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.android.ide.eclipse.adt.internal.project;

import com.android.ide.eclipse.adt.internal.project.BaseProjectHelper.IProjectFilter;
import com.android.ide.eclipse.adt.internal.sdk.ProjectState;
import com.android.ide.eclipse.adt.internal.sdk.Sdk;

import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IWorkspaceRoot;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.jdt.core.IJavaModel;
import org.eclipse.jdt.core.IJavaProject;
import org.eclipse.jdt.core.JavaCore;
import org.eclipse.jdt.ui.JavaElementLabelProvider;
import org.eclipse.jface.viewers.ILabelProvider;
import org.eclipse.jface.window.Window;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.events.SelectionListener;
import org.eclipse.swt.widgets.Combo;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.ui.dialogs.ElementListSelectionDialog;

/**
 * Helper class to deal with displaying a project choosing dialog that lists only the
 * projects with the Android nature.
 */
public class ProjectChooserHelper {

    private final Shell mParentShell;
    private final IProjectChooserFilter mFilter;

    /**
     * List of current android projects. Since the dialog is modal, we'll just get
     * the list once on-demand.
     */
    private IJavaProject[] mAndroidProjects;

    /**
     * Interface to filter out some project displayed by {@link ProjectChooserHelper}.
     *
     * @see IProjectFilter
     */
    public interface IProjectChooserFilter extends IProjectFilter {
        /**
         * Whether the Project Chooser can compute the project list once and cache the result.
         * </p>If false the project list is recomputed every time the dialog is opened.
         */
        boolean useCache();
    }

    /**
     * An implementation of {@link IProjectChooserFilter} that only displays non-library projects.
     */
    public final static class NonLibraryProjectOnlyFilter implements IProjectChooserFilter {
        public boolean accept(IProject project) {
            ProjectState state = Sdk.getProjectState(project);
            if (state != null) {
                return state.isLibrary() == false;
            }

            return false;
        }

        public boolean useCache() {
            return true;
        }
    }

    /**
     * An implementation of {@link IProjectChooserFilter} that only displays library projects.
     */
    public final static class LibraryProjectOnlyFilter implements IProjectChooserFilter {
        public boolean accept(IProject project) {
            ProjectState state = Sdk.getProjectState(project);
            if (state != null ) {
                return state.isLibrary();
            }

            return false;
        }

        public boolean useCache() {
            return true;
        }
    }

    /**
     * Creates a new project chooser.
     * @param parentShell the parent {@link Shell} for the dialog.
     * @param filter a filter to only accept certain projects. Can be null.
     */
    public ProjectChooserHelper(Shell parentShell, IProjectChooserFilter filter) {
        mParentShell = parentShell;
        mFilter = filter;
    }

    /**
     * Displays a project chooser dialog which lists all available projects with the Android nature.
     * <p/>
     * The list of project is built from Android flagged projects currently opened in the workspace.
     *
     * @param projectName If non null and not empty, represents the name of an Android project
     *                    that will be selected by default.
     * @param message Message for the dialog box. Can be null in which case a default message
     *                is displayed.
     * @return the project chosen by the user in the dialog, or null if the dialog was canceled.
     */
    public IJavaProject chooseJavaProject(String projectName, String message) {
        ILabelProvider labelProvider = new JavaElementLabelProvider(
                JavaElementLabelProvider.SHOW_DEFAULT);
        ElementListSelectionDialog dialog = new ElementListSelectionDialog(
                mParentShell, labelProvider);
        dialog.setTitle("Project Selection");
        if (message == null) {
            message = "Please select a project";
        }
        dialog.setMessage(message);

        IWorkspaceRoot workspaceRoot = ResourcesPlugin.getWorkspace().getRoot();
        IJavaModel javaModel = JavaCore.create(workspaceRoot);

        // set the elements in the dialog. These are opened android projects.
        dialog.setElements(getAndroidProjects(javaModel));

        // look for the project matching the given project name
        IJavaProject javaProject = null;
        if (projectName != null && projectName.length() > 0) {
            javaProject = javaModel.getJavaProject(projectName);
        }

        // if we found it, we set the initial selection in the dialog to this one.
        if (javaProject != null) {
            dialog.setInitialSelections(new Object[] { javaProject });
        }

        // open the dialog and return the object selected if OK was clicked, or null otherwise
        if (dialog.open() == Window.OK) {
            return (IJavaProject) dialog.getFirstResult();
        }
        return null;
    }

    /**
     * Returns the list of Android projects.
     * <p/>
     * Because this list can be time consuming, this class caches the list of project.
     * It is recommended to call this method instead of
     * {@link BaseProjectHelper#getAndroidProjects()}.
     *
     * @param javaModel the java model. Can be null.
     */
    public IJavaProject[] getAndroidProjects(IJavaModel javaModel) {
        // recompute only if we don't have the projects already or the filter is dynamic
        // and prevent usage of a cache.
        if (mAndroidProjects == null || (mFilter != null && mFilter.useCache() == false)) {
            if (javaModel == null) {
                mAndroidProjects = BaseProjectHelper.getAndroidProjects(mFilter);
            } else {
                mAndroidProjects = BaseProjectHelper.getAndroidProjects(javaModel, mFilter);
            }
        }

        return mAndroidProjects;
    }

    /**
     * Helper method to get the Android project with the given name
     *
     * @param projectName the name of the project to find
     * @return the {@link IProject} for the Android project. <code>null</code> if not found.
     */
    public IProject getAndroidProject(String projectName) {
        IProject iproject = null;
        IJavaProject[] javaProjects = getAndroidProjects(null);
        if (javaProjects != null) {
            for (IJavaProject javaProject : javaProjects) {
                if (javaProject.getElementName().equals(projectName)) {
                    iproject = javaProject.getProject();
                    break;
                }
            }
        }
        return iproject;
    }

    /**
     * A selector combo for showing the currently selected project and for
     * changing the selection
     */
    public static class ProjectCombo extends Combo implements SelectionListener {
        /** Currently chosen project, or null when no project has been initialized or selected */
        private IProject mProject;
        private IJavaProject[] mAvailableProjects;

        /**
         * Creates a new project selector combo
         *
         * @param helper associated {@link ProjectChooserHelper} for looking up
         *            projects
         * @param parent parent composite to add the combo to
         * @param initialProject the initial project to select, or null (which
         *            will show a "Please Choose Project..." label instead.)
         */
        public ProjectCombo(ProjectChooserHelper helper, Composite parent,
                IProject initialProject) {
            super(parent, SWT.BORDER | SWT.FLAT | SWT.READ_ONLY);
            mProject = initialProject;

            mAvailableProjects = helper.getAndroidProjects(null);
            String[] items = new String[mAvailableProjects.length + 1];
            items[0] = "--- Choose Project ---";

            ILabelProvider labelProvider = new JavaElementLabelProvider(
                    JavaElementLabelProvider.SHOW_DEFAULT);
            int selectionIndex = 0;
            for (int i = 0, n = mAvailableProjects.length; i < n; i++) {
                IProject project = mAvailableProjects[i].getProject();
                items[i + 1] = labelProvider.getText(project);
                if (project == initialProject) {
                    selectionIndex = i + 1;
                }
            }
            setItems(items);
            select(selectionIndex);

            addSelectionListener(this);
        }

        /**
         * Returns the project selected by this chooser (or the initial project
         * passed to the constructor if the user did not change it)
         *
         * @return the selected project
         */
        public IProject getSelectedProject() {
            return mProject;
        }

        /**
         * Sets the project selected by this chooser
         *
         * @param project the selected project
         */
        public void setSelectedProject(IProject project) {
            mProject = project;

            int selectionIndex = 0;
            for (int i = 0, n = mAvailableProjects.length; i < n; i++) {
                if (project == mAvailableProjects[i].getProject()) {
                    selectionIndex = i + 1; // +1: Slot 0 is reserved for "Choose Project"
                    select(selectionIndex);
                    break;
                }
            }
        }

        /**
         * Click handler for the button: Open the {@link ProjectChooserHelper}
         * dialog for selecting a new project.
         */
        public void widgetSelected(SelectionEvent e) {
            int selectionIndex = getSelectionIndex();
            if (selectionIndex > 0 && mAvailableProjects != null
                    && selectionIndex <= mAvailableProjects.length) {
                // selection index 0 is "Choose Project", all other projects are offset
                // by 1 from the selection index
                mProject = mAvailableProjects[selectionIndex - 1].getProject();
            } else {
                mProject = null;
            }
        }

        public void widgetDefaultSelected(SelectionEvent e) {
        }

        @Override
        protected void checkSubclass() {
            // Disable the check that prevents subclassing of SWT components
        }
    }
}
