/*
 * 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.wizards.export;

import com.android.ide.eclipse.adt.AdtPlugin;
import com.android.ide.eclipse.adt.AndroidConstants;
import com.android.ide.eclipse.adt.internal.project.AndroidManifestParser;
import com.android.ide.eclipse.adt.internal.project.BaseProjectHelper;
import com.android.ide.eclipse.adt.internal.project.ProjectChooserHelper;
import com.android.ide.eclipse.adt.internal.project.ProjectHelper;
import com.android.ide.eclipse.adt.internal.wizards.export.ExportWizard.ExportWizardPage;

import org.eclipse.core.resources.IFolder;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.jdt.core.IJavaProject;
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.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Text;

import java.io.File;

/**
 * First Export Wizard Page. Display warning/errors. 
 */
final class ProjectCheckPage extends ExportWizardPage {
    private final static String IMG_ERROR = "error.png"; //$NON-NLS-1$
    private final static String IMG_WARNING = "warning.png"; //$NON-NLS-1$

    private final ExportWizard mWizard;
    private Display mDisplay;
    private Image mError;
    private Image mWarning;
    private boolean mHasMessage = false;
    private Composite mTopComposite;
    private Composite mErrorComposite;
    private Text mProjectText;
    private ProjectChooserHelper mProjectChooserHelper;
    private boolean mFirstOnShow = true;

    protected ProjectCheckPage(ExportWizard wizard, String pageName) {
        super(pageName);
        mWizard = wizard;

        setTitle("Project Checks");
        setDescription("Performs a set of checks to make sure the application can be exported.");
    }

    public void createControl(Composite parent) {
        mProjectChooserHelper = new ProjectChooserHelper(parent.getShell());
        mDisplay = parent.getDisplay();

        GridLayout gl = null;
        GridData gd = null;

        mTopComposite = new Composite(parent, SWT.NONE);
        mTopComposite.setLayoutData(new GridData(GridData.FILL_BOTH));
        mTopComposite.setLayout(new GridLayout(1, false));
        
        // composite for the project selection.
        Composite projectComposite = new Composite(mTopComposite, SWT.NONE);
        projectComposite.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
        projectComposite.setLayout(gl = new GridLayout(3, false));
        gl.marginHeight = gl.marginWidth = 0;

        Label label = new Label(projectComposite, SWT.NONE);
        label.setLayoutData(gd = new GridData(GridData.FILL_HORIZONTAL));
        gd.horizontalSpan = 3;
        label.setText("Select the project to export:");

        new Label(projectComposite, SWT.NONE).setText("Project:");
        mProjectText = new Text(projectComposite, SWT.BORDER);
        mProjectText.setLayoutData(gd = new GridData(GridData.FILL_HORIZONTAL));
        mProjectText.addModifyListener(new ModifyListener() {
            public void modifyText(ModifyEvent e) {
                handleProjectNameChange();
            }
        });

        Button browseButton = new Button(projectComposite, SWT.PUSH);
        browseButton.setText("Browse...");
        browseButton.addSelectionListener(new SelectionAdapter() {
            @Override
            public void widgetSelected(SelectionEvent e) {
                IJavaProject javaProject = mProjectChooserHelper.chooseJavaProject(
                        mProjectText.getText().trim());

                if (javaProject != null) {
                    IProject project = javaProject.getProject();

                    // set the new name in the text field. The modify listener will take
                    // care of updating the status and the ExportWizard object.
                    mProjectText.setText(project.getName());
                }
            }
        });

        setControl(mTopComposite);
    }

    @Override
    void onShow() {
        if (mFirstOnShow) {
            // get the project and init the ui
            IProject project = mWizard.getProject();
            if (project != null) {
                mProjectText.setText(project.getName());
            }
            
            mFirstOnShow = false;
        }
    }
    
    private void buildErrorUi(IProject project) {
        // Show description the first time
        setErrorMessage(null);
        setMessage(null);
        setPageComplete(true);
        mHasMessage = false;

        // composite parent for the warning/error
        GridLayout gl = null;
        mErrorComposite = new Composite(mTopComposite, SWT.NONE);
        mErrorComposite.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
        gl = new GridLayout(2, false);
        gl.marginHeight = gl.marginWidth = 0;
        gl.verticalSpacing *= 3; // more spacing than normal.
        mErrorComposite.setLayout(gl);

        if (project == null) {
            setErrorMessage("Select project to export.");
            mHasMessage = true;
        } else {
            try {
                if (project.hasNature(AndroidConstants.NATURE) == false) {
                    addError(mErrorComposite, "Project is not an Android project.");
                } else {
                    // check for errors
                    if (ProjectHelper.hasError(project, true))  {
                        addError(mErrorComposite, "Project has compilation error(s)");
                    }
                    
                    // check the project output
                    IFolder outputIFolder = BaseProjectHelper.getOutputFolder(project);
                    if (outputIFolder != null) {
                        String outputOsPath =  outputIFolder.getLocation().toOSString();
                        String apkFilePath =  outputOsPath + File.separator + project.getName() +
                                AndroidConstants.DOT_ANDROID_PACKAGE;
                        
                        File f = new File(apkFilePath);
                        if (f.isFile() == false) {
                            addError(mErrorComposite,
                                    String.format("%1$s/%2$s/%1$s%3$s does not exists!",
                                            project.getName(),
                                            outputIFolder.getName(),
                                            AndroidConstants.DOT_ANDROID_PACKAGE));
                        }
                    } else {
                        addError(mErrorComposite,
                                "Unable to get the output folder of the project!");
                    }


                    // project is an android project, we check the debuggable attribute.
                    AndroidManifestParser manifestParser = AndroidManifestParser.parse(
                            BaseProjectHelper.getJavaProject(project), null /* errorListener */,
                            true /* gatherData */, false /* markErrors */);

                    Boolean debuggable = manifestParser.getDebuggable();
                    
                    if (debuggable != null && debuggable == Boolean.TRUE) {
                        addWarning(mErrorComposite,
                                "The manifest 'debuggable' attribute is set to true.\nYou should set it to false for applications that you release to the public."); 
                    }
                    
                    // check for mapview stuff
                }
            } catch (CoreException e) {
                // unable to access nature
                addError(mErrorComposite, "Unable to get project nature");
            }
        }
        
        if (mHasMessage == false) {
            Label label = new Label(mErrorComposite, SWT.NONE);
            GridData gd = new GridData(GridData.FILL_HORIZONTAL);
            gd.horizontalSpan = 2;
            label.setLayoutData(gd);
            label.setText("No errors found. Click Next.");
        }
        
        mTopComposite.layout();
    }
    
    /**
     * Adds an error label to a {@link Composite} object.
     * @param parent the Composite parent.
     * @param message the error message.
     */
    private void addError(Composite parent, String message) {
        if (mError == null) {
            mError = AdtPlugin.getImageLoader().loadImage(IMG_ERROR, mDisplay);
        }
        
        new Label(parent, SWT.NONE).setImage(mError);
        Label label = new Label(parent, SWT.NONE);
        label.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
        label.setText(message);
        
        setErrorMessage("Application cannot be exported due to the error(s) below.");
        setPageComplete(false);
        mHasMessage = true;
    }
    
    /**
     * Adds a warning label to a {@link Composite} object.
     * @param parent the Composite parent.
     * @param message the warning message.
     */
    private void addWarning(Composite parent, String message) {
        if (mWarning == null) {
            mWarning = AdtPlugin.getImageLoader().loadImage(IMG_WARNING, mDisplay);
        }
        
        new Label(parent, SWT.NONE).setImage(mWarning);
        Label label = new Label(parent, SWT.NONE);
        label.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
        label.setText(message);
        
        mHasMessage = true;
    }
    
    /**
     * Checks the parameters for correctness, and update the error message and buttons.
     */
    private void handleProjectNameChange() {
        setPageComplete(false);
        
        if (mErrorComposite != null) {
            mErrorComposite.dispose();
            mErrorComposite = null;
        }
        
        // update the wizard with the new project
        mWizard.setProject(null);

        //test the project name first!
        String text = mProjectText.getText().trim();
        if (text.length() == 0) {
            setErrorMessage("Select project to export.");
        } else if (text.matches("[a-zA-Z0-9_ \\.-]+") == false) {
            setErrorMessage("Project name contains unsupported characters!");
        } else {
            IJavaProject[] projects = mProjectChooserHelper.getAndroidProjects(null);
            IProject found = null;
            for (IJavaProject javaProject : projects) {
                if (javaProject.getProject().getName().equals(text)) {
                    found = javaProject.getProject();
                    break;
                }
                
            }
            
            if (found != null) {
                setErrorMessage(null);
                
                // update the wizard with the new project
                mWizard.setProject(found);

                // now rebuild the error ui.
                buildErrorUi(found);
            } else {
                setErrorMessage(String.format("There is no android project named '%1$s'",
                        text));
            }
        }
    }
}
