/**
 * Copyright (c) 2005-2013 by Appcelerator, Inc. All Rights Reserved.
 * Licensed under the terms of the Eclipse Public License (EPL).
 * Please see the license.txt included with this distribution for details.
 * Any modifications to this file must keep this entire header intact.
 */
package org.python.pydev.django.ui.wizards.project;

import java.io.ByteArrayInputStream;
import java.io.File;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.eclipse.core.resources.IContainer;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IProjectDescription;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.runtime.Assert;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.NullProgressMonitor;
import org.eclipse.core.runtime.OperationCanceledException;
import org.eclipse.core.runtime.Path;
import org.eclipse.jface.dialogs.MessageDialog;
import org.eclipse.jface.resource.ImageDescriptor;
import org.eclipse.jface.text.IDocument;
import org.eclipse.jface.wizard.IWizardPage;
import org.python.pydev.core.FileUtilsFileBuffer;
import org.python.pydev.core.ICodeCompletionASTManager;
import org.python.pydev.core.log.Log;
import org.python.pydev.django.DjangoPlugin;
import org.python.pydev.django.launching.DjangoConstants;
import org.python.pydev.django.nature.DjangoNature;
import org.python.pydev.django.ui.wizards.project.DjangoSettingsPage.DjangoSettings;
import org.python.pydev.plugin.PyStructureConfigHelpers;
import org.python.pydev.plugin.PydevPlugin;
import org.python.pydev.plugin.nature.PythonNature;
import org.python.pydev.runners.UniversalRunner;
import org.python.pydev.runners.UniversalRunner.AbstractRunner;
import org.python.pydev.shared_core.callbacks.ICallback;
import org.python.pydev.shared_core.callbacks.ICallback0;
import org.python.pydev.shared_core.io.FileUtils;
import org.python.pydev.shared_core.structure.Tuple;
import org.python.pydev.shared_ui.EditorUtils;
import org.python.pydev.shared_ui.utils.RunInUiThread;
import org.python.pydev.ui.wizards.project.IWizardNewProjectNameAndLocationPage;
import org.python.pydev.ui.wizards.project.PythonProjectWizard;

/**
 * Creation of a Django project
 * 
 * @author Mikko Ohtamaa
 * @author Leo Soto
 * @author Fabio Zadrozny
 */
public class DjangoProjectWizard extends PythonProjectWizard {

    public static final String WIZARD_ID = "org.python.pydev.ui.wizards.project.DjangoProjectWizard";

    protected DjangoSettingsPage settingsPage;

    protected static final String RUN_DJANGO_ADMIN = "from django.core import management;management.execute_from_command_line();";

    public DjangoProjectWizard() {
        super();
        settingsPage = createDjangoSettingsPage(new ICallback0<IWizardNewProjectNameAndLocationPage>() {
            public IWizardNewProjectNameAndLocationPage call() {
                return projectPage;
            }
        });
    }

    @Override
    protected IWizardNewProjectNameAndLocationPage createProjectPage() {
        return new DjangoNewProjectPage("Setting project properties");
    }

    protected DjangoSettingsPage createDjangoSettingsPage(ICallback0<IWizardNewProjectNameAndLocationPage> projectPage) {
        return new DjangoSettingsPage("Django Settings", projectPage);
    }

    /**
     * Add wizard pages to the instance
     *
     * @see org.eclipse.jface.wizard.IWizard#addPages()
     */
    @Override
    public void addPages() {
        super.addPages();
        addPage(settingsPage);
    }

    @Override
    protected void createAndConfigProject(IProject projectHandle, IProjectDescription description, String projectType,
            String projectInterpreter, IProgressMonitor monitor, Object... additionalArgsToConfigProject)
            throws CoreException {

        Assert.isTrue(additionalArgsToConfigProject.length == 1);
        final DjangoSettings djSettings = (DjangoSettings) additionalArgsToConfigProject[0];

        final int sourceFolderConfigurationStyle = projectPage.getSourceFolderConfigurationStyle();
        ICallback<List<IContainer>, IProject> getSourceFolderHandlesCallback = super.getSourceFolderHandlesCallback;
        ICallback<List<IPath>, IProject> getExistingSourceFolderHandlesCallback = super.getExistingSourceFolderHandlesCallback;

        ICallback<Map<String, String>, IProject> getVariableSubstitutionCallback = new ICallback<Map<String, String>, IProject>() {

            public Map<String, String> call(IProject projectHandle) {
                Map<String, String> variableSubstitution = new HashMap<String, String>();
                String manageLocation;
                if (djSettings.djangoVersion.equals(DjangoSettingsPage.DJANGO_14)) {
                    manageLocation = "manage.py";

                } else {
                    //Before 1.4
                    manageLocation = projectHandle.getName() + "/manage.py";
                }

                switch (sourceFolderConfigurationStyle) {
                    case IWizardNewProjectNameAndLocationPage.PYDEV_NEW_PROJECT_CREATE_PROJECT_AS_SRC_FOLDER:
                    case IWizardNewProjectNameAndLocationPage.PYDEV_NEW_PROJECT_NO_PYTHONPATH:
                        break;
                    default:
                        manageLocation = "src/" + manageLocation;
                }

                variableSubstitution.put(DjangoConstants.DJANGO_MANAGE_VARIABLE, manageLocation);
                return variableSubstitution;
            }
        };

        PyStructureConfigHelpers.createPydevProject(description, projectHandle, monitor, projectType,
                projectInterpreter, getSourceFolderHandlesCallback, null, getExistingSourceFolderHandlesCallback,
                getVariableSubstitutionCallback);

        //The django nature is added only so that we can identify whether we should show django actions.
        DjangoNature.addNature(projectHandle, null);

        try {
            if (monitor.isCanceled()) {
                throw new OperationCanceledException();
            }
            PythonNature nature = PythonNature.getPythonNature(projectHandle);
            Assert.isNotNull(nature);
            ICodeCompletionASTManager astManager = nature.getAstManager();
            Object sync = new Object();
            //Wait up to 10 seconds for it to be restored
            for (int i = 0; i < 100 && astManager == null; i++) {
                synchronized (sync) {
                    try {
                        sync.wait(100);
                    } catch (InterruptedException e) {
                    }
                }
                astManager = nature.getAstManager();
            }

            if (astManager == null) {
                throw new RuntimeException(
                        "Error creating Django project. ASTManager not available after 10 seconds.\n"
                                + "Please report this bug at the sourceforge tracker.");
            }
            AbstractRunner runner = UniversalRunner.getRunner(nature);

            IContainer projectContainer;

            switch (sourceFolderConfigurationStyle) {
                case IWizardNewProjectNameAndLocationPage.PYDEV_NEW_PROJECT_CREATE_PROJECT_AS_SRC_FOLDER:
                case IWizardNewProjectNameAndLocationPage.PYDEV_NEW_PROJECT_EXISTING_SOURCES:
                case IWizardNewProjectNameAndLocationPage.PYDEV_NEW_PROJECT_NO_PYTHONPATH:
                    projectContainer = projectHandle;
                    break;
                default:
                    projectContainer = projectHandle.getFolder("src");
            }

            String projectName = projectHandle.getName();
            Tuple<String, String> output = runner.runCodeAndGetOutput(RUN_DJANGO_ADMIN, new String[] { "startproject",
                    projectName }, projectContainer.getLocation().toFile(), new NullProgressMonitor());

            if (output.o2.indexOf("ImportError: no module named django") != -1) {
                RunInUiThread.async(new Runnable() {

                    public void run() {
                        MessageDialog.openError(EditorUtils.getShell(), "Unable to create project.",
                                "Unable to create project because the selected interpreter does not have django.");
                    }
                });
                projectHandle.delete(true, null);
                return;
            }

            IDocument docFromResource = null;
            IFile settingsFile = null;
            if (djSettings.djangoVersion.equals(DjangoSettingsPage.DJANGO_14)) {

                //Ok, Django 1.4 is now as follows:
                //It'll create a structure
                //   /projectName
                //   /projectName/manage.py
                //   /projectName/projectName
                //   /projectName/projectName/__init__.py
                //   /projectName/projectName/settings.py

                //So, what pydev did before (i.e.: creating the projectName inital folder) is repeated in its process.
                //Thus, we have to go on and get rid of it, moving the manage.py and projectName to the projectContainer
                //and removing the root projectName altoghether.
                File copyTo = projectContainer.getLocation().toFile();
                File copyFrom = new File(copyTo, projectName);
                File[] files = copyFrom.listFiles();
                if (files != null) {
                    for (File f : files) {
                        if (f.isFile()) {
                            try {
                                FileUtils.copyFile(f, new File(copyTo, f.getName()));
                                FileUtils.deleteFile(f);
                            } catch (Exception e) {
                                Log.log(e);
                            }
                        } else {
                            try {
                                FileUtils.copyDirectory(f, new File(copyTo, f.getName()), null, null);
                                FileUtils.deleteDirectoryTree(f);
                            } catch (Exception e) {
                                Log.log(e);
                            }
                        }
                    }
                }
            }
            //Before 1.4
            settingsFile = projectContainer.getFile(new Path(projectName + "/settings.py"));

            settingsFile.refreshLocal(IResource.DEPTH_ZERO, null);
            docFromResource = FileUtilsFileBuffer.getDocFromResource(settingsFile);
            if (docFromResource == null) {
                throw new RuntimeException("Error creating Django project.\n" + "settings.py file not created.\n"
                        + "Stdout: " + output.o1 + "\n" + "Stderr: " + output.o2);
            }

            String settings = docFromResource.get();
            if (djSettings.djangoVersion.equals(DjangoSettingsPage.DJANGO_12_OR_13)
                    || djSettings.djangoVersion.equals(DjangoSettingsPage.DJANGO_14)) {
                //1.2, 1.3 or 1.4
                settings = settings.replaceFirst("'ENGINE': 'django.db.backends.'", "'ENGINE': 'django.db.backends."
                        + djSettings.databaseEngine + "'");
                settings = settings.replaceFirst("'NAME': ''", "'NAME': '" + djSettings.databaseName + "'");
                settings = settings.replaceFirst("'HOST': ''", "'HOST': '" + djSettings.databaseHost + "'");
                settings = settings.replaceFirst("'PORT': ''", "'PORT': '" + djSettings.databasePort + "'");
                settings = settings.replaceFirst("'USER': ''", "'USER': '" + djSettings.databaseUser + "'");
                settings = settings.replaceFirst("'PASSWORD': ''", "'PASSWORD': '" + djSettings.databasePassword + "'");
            } else {
                //Before 1.2
                settings = settings.replaceFirst("DATABASE_ENGINE = ''", "DATABASE_ENGINE = '"
                        + djSettings.databaseEngine + "'");
                settings = settings.replaceFirst("DATABASE_NAME = ''", "DATABASE_NAME = '" + djSettings.databaseName
                        + "'");
                settings = settings.replaceFirst("DATABASE_HOST = ''", "DATABASE_HOST = '" + djSettings.databaseHost
                        + "'");
                settings = settings.replaceFirst("DATABASE_PORT = ''", "DATABASE_PORT = '" + djSettings.databasePort
                        + "'");
                settings = settings.replaceFirst("DATABASE_USER = ''", "DATABASE_USER = '" + djSettings.databaseUser
                        + "'");
                settings = settings.replaceFirst("DATABASE_PASSWORD = ''", "DATABASE_PASSWORD = '"
                        + djSettings.databasePassword + "'");
            }

            if (settingsFile != null) {
                settingsFile.setContents(new ByteArrayInputStream(settings.getBytes()), 0, monitor);
            }

        } catch (Exception e) {
            Log.log(e);
        } finally {
            monitor.done();
        }
    }

    /**
     * Creates a new project resource with the entered name.
     *
     * @return the created project resource, or <code>null</code> if the project was not created
     */
    @Override
    protected IProject createNewProject(final Object... additionalArgsToConfigProject) {
        if (additionalArgsToConfigProject != null && additionalArgsToConfigProject.length > 0) {
            throw new RuntimeException("Did not expect to receive arguments here.");
        }
        final DjangoSettings djSettings = settingsPage.getSettings();
        return super.createNewProject(djSettings);
    }

    /**
     * Set Django logo to top bar
     */
    @Override
    protected void initializeDefaultPageImageDescriptor() {
        ImageDescriptor desc = PydevPlugin.imageDescriptorFromPlugin(DjangoPlugin.getPluginID(),
                "icons/django_logo.png");//$NON-NLS-1$
        setDefaultPageImageDescriptor(desc);
    }

    @Override
    public boolean canFinish() {
        IWizardPage currentPage = this.getContainer().getCurrentPage();
        return currentPage == this.settingsPage; //can only finish at the last page!
    }
}
