/*
 * Copyright (C) 2011 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.wizards.newproject;

import com.android.ide.eclipse.adt.internal.project.AndroidManifestHelper;
import com.android.ide.eclipse.adt.internal.project.ProjectChooserHelper;
import com.android.ide.eclipse.adt.internal.sdk.Sdk;
import com.android.sdklib.IAndroidTarget;
import com.android.sdklib.xml.ManifestData;

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.dialogs.IMessageProvider;
import org.eclipse.jface.viewers.ILabelProvider;
import org.eclipse.jface.wizard.WizardPage;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.events.SelectionListener;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.ui.dialogs.FilteredList;

/**
 * Page shown when creating a test project which lets you choose between testing
 * yourself and testing a different project
 */
class TestTargetPage extends WizardPage implements SelectionListener {
    private final NewProjectWizardState mValues;
    /** Flag used when setting button/text state manually to ignore listener updates */
    private boolean mIgnore;
    private String mLastExistingPackageName;

    private Button mCurrentRadioButton;
    private Button mExistingRadioButton;
    private FilteredList mProjectList;

    /**
     * Create the wizard.
     */
    TestTargetPage(NewProjectWizardState values) {
        super("testTargetPage"); //$NON-NLS-1$
        setTitle("Select TestTarget");
        setDescription("Choose a project to test");
        mValues = values;
    }

    /**
     * Create contents of the wizard.
     */
    public void createControl(Composite parent) {
        Composite container = new Composite(parent, SWT.NULL);

        setControl(container);
        container.setLayout(new GridLayout(2, false));

        mCurrentRadioButton = new Button(container, SWT.RADIO);
        mCurrentRadioButton.setLayoutData(new GridData(SWT.LEFT, SWT.CENTER, false, false, 2, 1));
        mCurrentRadioButton.setText("This project");
        mCurrentRadioButton.addSelectionListener(this);

        mExistingRadioButton = new Button(container, SWT.RADIO);
        mExistingRadioButton.setLayoutData(new GridData(SWT.LEFT, SWT.CENTER, false, false, 2, 1));
        mExistingRadioButton.setText("An existing Android project:");
        mExistingRadioButton.addSelectionListener(this);

        ILabelProvider labelProvider = new JavaElementLabelProvider(
                JavaElementLabelProvider.SHOW_DEFAULT);
        mProjectList = new FilteredList(container,
                SWT.BORDER | SWT.V_SCROLL | SWT.H_SCROLL | SWT.SINGLE, labelProvider,
                true /*ignoreCase*/, false /*allowDuplicates*/, true /* matchEmptyString*/);
        mProjectList.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true, 2, 1));
        mProjectList.addSelectionListener(this);
    }

    private void initializeList() {
        ProjectChooserHelper helper = new ProjectChooserHelper(getShell(), null /*filter*/);
        IWorkspaceRoot workspaceRoot = ResourcesPlugin.getWorkspace().getRoot();
        IJavaModel javaModel = JavaCore.create(workspaceRoot);
        IJavaProject[] androidProjects = helper.getAndroidProjects(javaModel);
        mProjectList.setElements(androidProjects);
        if (mValues.testedProject != null) {
            for (IJavaProject project : androidProjects) {
                if (project.getProject() == mValues.testedProject) {
                    mProjectList.setSelection(new Object[] { project });
                    break;
                }
            }
        } else {
            // No initial selection: force the user to choose
            mProjectList.setSelection(new int[0]);
        }
    }

    @Override
    public void setVisible(boolean visible) {
        super.setVisible(visible);

        if (visible) {
            try {
                mIgnore = true;
                mCurrentRadioButton.setSelection(mValues.testingSelf);
                mExistingRadioButton.setSelection(!mValues.testingSelf);
                mProjectList.setEnabled(!mValues.testingSelf);

                if (mProjectList.isEmpty()) {
                    initializeList();
                }
                if (!mValues.testingSelf) {
                    mProjectList.setFocus();
                    IProject project = getSelectedProject();
                    if (project != null) {
                        // The FilteredList seems to -insist- on selecting the first item
                        // in the list, even when the selection is explicitly set to an empty
                        // array. This means the user is looking at a selection, so we need
                        // to also go ahead and select this item in the model such that the
                        // two agree, even if we would have preferred to have no initial
                        // selection.
                        mValues.testedProject = project;
                    }
                }
            } finally {
                mIgnore = false;
            }
        }

        validatePage();
    }

    public void widgetSelected(SelectionEvent e) {
        if (mIgnore) {
            return;
        }

        Object source = e.getSource();
        if (source == mExistingRadioButton) {
            mProjectList.setEnabled(true);
            mValues.testingSelf = false;
            setExistingProject(getSelectedProject());
            mProjectList.setFocus();
        } else if (source == mCurrentRadioButton) {
            mProjectList.setEnabled(false);
            mValues.testingSelf = true;
            mValues.testedProject = null;
        } else {
            // The event must be from the project list, which unfortunately doesn't
            // pass itself as the selection event, it passes a reference to some internal
            // table widget that it uses, so we check for this case last
            IProject project = getSelectedProject();
            if (project != mValues.testedProject) {
                setExistingProject(project);
            }
        }

        validatePage();
    }

    private IProject getSelectedProject() {
        Object[] selection = mProjectList.getSelection();
        IProject project = selection != null && selection.length == 1
            ? ((IJavaProject) selection[0]).getProject() : null;
        return project;
    }

    public void widgetDefaultSelected(SelectionEvent e) {
    }

    private void setExistingProject(IProject project) {
        mValues.testedProject = project;

        // Try to update the application, package, sdk target and minSdkVersion accordingly
        if (project != null &&
                (!mValues.applicationNameModifiedByUser ||
                 !mValues.packageNameModifiedByUser     ||
                 !mValues.targetModifiedByUser          ||
                 !mValues.minSdkModifiedByUser)) {
            ManifestData manifestData = AndroidManifestHelper.parseForData(project);
            if (manifestData != null) {
                String appName = String.format("%1$sTest", project.getName());
                String packageName = manifestData.getPackage();
                String minSdkVersion = manifestData.getMinSdkVersionString();
                IAndroidTarget sdkTarget = null;
                if (Sdk.getCurrent() != null) {
                    sdkTarget = Sdk.getCurrent().getTarget(project);
                }

                if (packageName == null) {
                    packageName = "";  //$NON-NLS-1$
                }
                mLastExistingPackageName = packageName;

                if (!mValues.projectNameModifiedByUser) {
                    mValues.projectName = appName;
                }

                if (!mValues.applicationNameModifiedByUser) {
                    mValues.applicationName = appName;
                }

                if (!mValues.packageNameModifiedByUser) {
                    packageName += ".test";  //$NON-NLS-1$
                    mValues.packageName = packageName;
                }

                if (!mValues.targetModifiedByUser && sdkTarget != null) {
                    mValues.target = sdkTarget;
                }

                if (!mValues.minSdkModifiedByUser) {
                    if (minSdkVersion != null || sdkTarget != null) {
                        mValues.minSdk = minSdkVersion;
                    }
                    if (sdkTarget == null) {
                        mValues.updateSdkTargetToMatchMinSdkVersion();
                    }
                }
            }
        }

        updateTestTargetPackageField(mLastExistingPackageName);
    }

    /**
     * Updates the test target package name
     *
     * When using the "self-test" option, the packageName argument is ignored and the
     * current value from the project package is used.
     *
     * Otherwise the packageName is used if it is not null.
     */
    private void updateTestTargetPackageField(String packageName) {
        if (mValues.testingSelf) {
            mValues.testTargetPackageName = mValues.packageName;
        } else if (packageName != null) {
            mValues.testTargetPackageName = packageName;
        }
    }

    private void validatePage() {
        String error = null;

        if (!mValues.testingSelf) {
            if (mValues.testedProject == null) {
                error = "Please select an existing Android project as a test target.";
            } else if (mValues.projectName.equals(mValues.testedProject.getName())) {
                error = "The main project name and the test project name must be different.";
            }
        }

        // -- update UI & enable finish if there's no error
        setPageComplete(error == null);
        if (error != null) {
            setMessage(error, IMessageProvider.ERROR);
        } else {
            setErrorMessage(null);
            setMessage(null);
        }
    }
}
