/*
 * Copyright (C) 2010 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.refactorings.renamepackage;

import com.android.ide.eclipse.adt.AdtPlugin;
import com.android.ide.eclipse.adt.internal.project.AndroidManifestHelper;
import com.android.sdklib.xml.ManifestData;

import org.eclipse.core.resources.IProject;
import org.eclipse.core.runtime.IAdaptable;
import org.eclipse.core.runtime.Status;
import org.eclipse.jdt.core.dom.AST;
import org.eclipse.jdt.core.dom.Name;
import org.eclipse.jdt.ui.refactoring.RefactoringSaveHelper;
import org.eclipse.jface.action.IAction;
import org.eclipse.jface.dialogs.IInputValidator;
import org.eclipse.jface.dialogs.InputDialog;
import org.eclipse.jface.viewers.ISelection;
import org.eclipse.jface.viewers.IStructuredSelection;
import org.eclipse.jface.window.Window;
import org.eclipse.ltk.core.refactoring.Refactoring;
import org.eclipse.ltk.ui.refactoring.RefactoringWizardOpenOperation;
import org.eclipse.ui.IObjectActionDelegate;
import org.eclipse.ui.IWorkbenchPart;
import org.eclipse.ui.IWorkbenchWindow;
import org.eclipse.ui.IWorkbenchWindowActionDelegate;

import java.util.Iterator;

/**
 * Refactoring steps:
 * <ol>
 * <li>Update the "package" attribute of the &lt;manifest&gt; tag with the new
 * name.</li>
 * <li>Replace all values for the "android:name" attribute in the
 * &lt;application&gt; and "component class" (&lt;activity&gt;, &lt;service&gt;,
 * &lt;receiver&gt;, and &lt;provider&gt;) tags with the non-shorthand version
 * of the class name</li>
 * <li>Replace package resource imports (*.R) in .java files</li>
 * <li>Update package name in the namespace declarations (e.g. "xmlns:app")
 * used for custom styleable attributes in layout resource files</li>
 * </ol>
 * Caveat: Sometimes it is necessary to perform a project-wide
 * "Organize Imports" afterwards. (CTRL+SHIFT+O when a project has active
 * selection)
 */
public class RenamePackageAction implements IObjectActionDelegate {

    private ISelection mSelection;
    @SuppressWarnings("unused") private IWorkbenchPart mTargetPart; // TODO cleanup

    /**
     * @see IObjectActionDelegate#setActivePart(IAction, IWorkbenchPart)
     */
    public void setActivePart(IAction action, IWorkbenchPart targetPart) {
        mTargetPart = targetPart;
    }

    public void selectionChanged(IAction action, ISelection selection) {
        mSelection = selection;
    }

    /**
     * @see IWorkbenchWindowActionDelegate#init
     */
    public void init(IWorkbenchWindow window) {
        // pass
    }

    public void run(IAction action) {

        // Prompt for refactoring on the selected project
        if (mSelection instanceof IStructuredSelection) {
            for (Iterator<?> it = ((IStructuredSelection) mSelection).iterator(); it.hasNext();) {
                Object element = it.next();
                IProject project = null;
                if (element instanceof IProject) {
                    project = (IProject) element;
                } else if (element instanceof IAdaptable) {
                    project = (IProject) ((IAdaptable) element).getAdapter(IProject.class);
                }
                if (project != null) {
                    // It is advisable that the user saves before proceeding,
                    // revealing any compilation errors. The following lines
                    // enforce a save as a convenience.
                    RefactoringSaveHelper save_helper = new RefactoringSaveHelper(
                            RefactoringSaveHelper.SAVE_ALL_ALWAYS_ASK);
                    if (save_helper.saveEditors(AdtPlugin.getDisplay().getActiveShell())) {
                        promptNewName(project);
                    }
                }
            }
        }
    }

    /*
     * Validate the new package name and start the refactoring wizard
     */
    private void promptNewName(final IProject project) {

        ManifestData manifestData = AndroidManifestHelper.parseForData(project);
        if (manifestData == null) {
            return;
        }

        final String oldPackageNameString = manifestData.getPackage();

        final AST astValidator = AST.newAST(AST.JLS3);
        Name oldPackageName = astValidator.newName(oldPackageNameString);

        IInputValidator validator = new IInputValidator() {

            public String isValid(String newText) {
                try {
                    astValidator.newName(newText);
                } catch (IllegalArgumentException e) {
                    return "Illegal package name.";
                }

                if (newText.equals(oldPackageNameString))
                    return "No change.";
                else
                    return null;
            }
        };

        InputDialog dialog = new InputDialog(AdtPlugin.getDisplay().getActiveShell(),
                "Rename Application Package", "Enter new package name:", oldPackageNameString,
                validator);

        if (dialog.open() == Window.OK) {
            Name newPackageName = astValidator.newName(dialog.getValue());
            initiateAndroidPackageRefactoring(project, oldPackageName, newPackageName);
        }
    }


    private void initiateAndroidPackageRefactoring(
            final IProject project,
            Name oldPackageName,
            Name newPackageName) {

        Refactoring package_name_refactoring =
            new ApplicationPackageNameRefactoring(project, oldPackageName, newPackageName);

        ApplicationPackageNameRefactoringWizard wizard =
            new ApplicationPackageNameRefactoringWizard(package_name_refactoring);
        RefactoringWizardOpenOperation op = new RefactoringWizardOpenOperation(wizard);
        try {
            op.run(AdtPlugin.getDisplay().getActiveShell(), package_name_refactoring.getName());
        } catch (InterruptedException e) {
            Status s = new Status(Status.ERROR, AdtPlugin.PLUGIN_ID, e.getMessage(), e);
            AdtPlugin.getDefault().getLog().log(s);
        }
    }
}
