/**
 * 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.
 */
/*
 * Created on Mar 11, 2004
 * 
 * @author Fabio Zadrozny
 */
package org.python.pydev.ui;

import java.util.Map;

import org.eclipse.core.resources.IProject;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.jface.dialogs.InputDialog;
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.swt.widgets.Control;
import org.eclipse.swt.widgets.DirectoryDialog;
import org.eclipse.swt.widgets.FileDialog;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.TabFolder;
import org.eclipse.swt.widgets.TabItem;
import org.eclipse.ui.dialogs.PropertyPage;
import org.eclipse.ui.dialogs.ResourceSelectionDialog;
import org.python.pydev.core.IPythonPathNature;
import org.python.pydev.core.log.Log;
import org.python.pydev.plugin.PydevPlugin;
import org.python.pydev.plugin.nature.PythonNature;
import org.python.pydev.shared_core.string.StringUtils;
import org.python.pydev.shared_ui.UIConstants;
import org.python.pydev.ui.dialogs.ProjectFolderSelectionDialog;
import org.python.pydev.ui.editors.TreeWithAddRemove;

/**
 * This page is specified to appear in the plugin.xml
 */
public class PyProjectProperties extends PropertyPage {

    /**
     * This is the project we are editing
     */
    private IProject project;

    /**
     * Tree with source folders
     */
    private TreeWithAddRemove treeSourceFolders;

    /**
     * Tree with external folders
     */
    private TreeWithAddRemove treeExternalLibs;

    /**
     * Variables are edited here 
     */
    private TabVariables tabVariables;

    /**
     * Yes: things are tab-separated
     */
    private TabFolder tabFolder;

    /**
     * Creates contents given its parent.
     */
    @Override
    protected Control createContents(Composite p) {
        project = (IProject) getElement().getAdapter(IProject.class);

        Composite topComp = new Composite(p, SWT.NONE);
        GridLayout innerLayout = new GridLayout();
        innerLayout.numColumns = 1;
        innerLayout.marginHeight = 0;
        innerLayout.marginWidth = 0;
        topComp.setLayout(innerLayout);
        GridData gd = new GridData(GridData.FILL_BOTH);
        topComp.setLayoutData(gd);
        Label label = new Label(topComp, SWT.None);
        label.setText("The final PYTHONPATH used for a launch is composed of the paths\n"
                + "defined here, joined with the paths defined by the selected interpreter.");

        tabFolder = new TabFolder(topComp, SWT.None);
        gd = new GridData();
        gd.horizontalAlignment = SWT.FILL;
        gd.verticalAlignment = SWT.FILL;
        gd.grabExcessVerticalSpace = true;
        gd.grabExcessHorizontalSpace = true;
        gd.horizontalSpan = 1;
        tabFolder.setLayoutData(gd);

        if (project != null) {
            try {
                IPythonPathNature nature = PythonNature.getPythonPathNature(project);

                createTabProjectSourceFolders(nature.getProjectSourcePath(false));
                createTabExternalSourceFolders(nature.getProjectExternalSourcePath(false));
                tabVariables = new TabVariables(tabFolder, nature.getVariableSubstitution(false));

                createRestoreButton(topComp);
            } catch (Exception e) {
                Log.log(e);
            }

        }
        return topComp;
    }

    private void createRestoreButton(Composite topComp) {
        Button button = new Button(topComp, SWT.NONE);
        button.setText("Force restore internal info");
        button.addSelectionListener(new SelectionListener() {

            @Override
            public void widgetSelected(SelectionEvent e) {
                doIt(true);
            }

            @Override
            public void widgetDefaultSelected(SelectionEvent e) {
            }

        });
    }

    private void createTabExternalSourceFolders(String externalSourcePath) {
        TabItem tabItem = new TabItem(tabFolder, SWT.None);
        tabItem.setText("External Libraries");
        Composite topComp = new Composite(tabFolder, SWT.None);
        tabItem.setImage(PydevPlugin.getImageCache().get(UIConstants.LIB_SYSTEM));
        topComp.setLayout(new GridLayout(1, false));

        GridData gd;
        GridData data;
        Label l2;
        l2 = new Label(topComp, SWT.None);
        l2.setText("External libraries (source folders/zips/jars/eggs) outside of the workspace.\n\n"
                + "When using variables, the final paths resolved must be filesystem absolute.\n\n"
                + "Changes in external libraries are not monitored, so, the 'Force restore internal info'\ns"
                + "hould be used if an external library changes. ");
        gd = new GridData();
        gd.grabExcessHorizontalSpace = true;
        gd.grabExcessVerticalSpace = false;
        l2.setLayoutData(gd);

        treeExternalLibs = new TreeWithAddRemove(topComp, 0, PythonNature.getStrAsStrItems(externalSourcePath)) {

            @Override
            protected String getImageConstant() {
                return UIConstants.LIB_SYSTEM;
            }

            @Override
            protected String getButtonLabel(int i) {
                switch (i) {
                    case 0:
                        return "Add source folder";

                    case 1:
                        return "Add zip/jar/egg";

                    case 2:
                        return "Add based on variable";

                    default:
                        throw new AssertionError("Unexpected: " + i);

                }
            }

            @Override
            protected void handleAddButtonSelected(int nButton) {
                if (nButton == 0) {
                    addItemWithDialog(new DirectoryDialog(getShell()));

                } else if (nButton == 1) {
                    addItemWithDialog(new FileDialog(getShell(), SWT.MULTI));

                } else if (nButton == 2) {
                    addItemWithDialog(new InputDialog(getShell(), "Add path to resolve with variable",
                            "Add path to resolve with variable in the format: ${VARIABLE}", "", null));

                } else {
                    throw new AssertionError("Unexpected");
                }
            }
        };
        data = new GridData(GridData.FILL_BOTH);
        data.grabExcessHorizontalSpace = true;
        data.grabExcessVerticalSpace = true;
        treeExternalLibs.setLayoutData(data);

        tabItem.setControl(topComp);
    }

    private void createTabProjectSourceFolders(String sourcePath) {
        TabItem tabItem = new TabItem(tabFolder, SWT.None);
        tabItem.setText("Source Folders");
        tabItem.setImage(PydevPlugin.getImageCache().get(UIConstants.SOURCE_FOLDER_ICON));
        Composite topComp = new Composite(tabFolder, SWT.None);
        topComp.setLayout(new GridLayout(1, false));

        GridData gd;
        GridData data;
        Label l2 = new Label(topComp, SWT.None);
        l2.setText("Project Source Folders (and zips/jars/eggs).\n\n"
                + "When using variables, the final paths resolved must be workspace-relative.");
        gd = new GridData();
        gd.grabExcessHorizontalSpace = true;
        gd.grabExcessVerticalSpace = false;
        l2.setLayoutData(gd);

        treeSourceFolders = new TreeWithAddRemove(topComp, 0, PythonNature.getStrAsStrItems(sourcePath)) {

            @Override
            protected String getButtonLabel(int i) {
                switch (i) {
                    case 0:
                        return "Add source folder";

                    case 1:
                        return "Add zip/jar/egg";

                    case 2:
                        return "Add based on variable";

                    default:
                        throw new AssertionError("Unexpected: " + i);

                }
            }

            @Override
            protected void handleAddButtonSelected(int nButton) {
                if (nButton == 0) {
                    addItemWithDialog(new ProjectFolderSelectionDialog(getShell(), project, true,
                            "Choose source folders to add to PYTHONPATH"), project);

                } else if (nButton == 1) {
                    addItemWithDialog(new ResourceSelectionDialog(getShell(), project,
                            "Choose zip/jar/egg to add to PYTHONPATH"), project);

                } else if (nButton == 2) {
                    addItemWithDialog(new InputDialog(getShell(), "Add path to resolve with variable",
                            "Add path to resolve with variable in the format: ${VARIABLE}", "", null));

                } else {
                    throw new AssertionError("Unexpected");
                }
            }

            @Override
            protected String getImageConstant() {
                return UIConstants.SOURCE_FOLDER_ICON;
            }

        };
        data = new GridData(GridData.FILL_BOTH);
        data.grabExcessHorizontalSpace = true;
        data.grabExcessVerticalSpace = true;
        treeSourceFolders.setLayoutData(data);

        tabItem.setControl(topComp);
    }

    /**
     * Apply only saves the new value. does not do code completion update.
     * 
     * @see org.eclipse.jface.preference.PreferencePage#performApply()
     */
    @Override
    protected void performApply() {
        doIt(false);
    }

    /**
     * Saves values into the project and updates the code completion. 
     */
    @Override
    public boolean performOk() {
        return doIt(false);
    }

    /**
     * Save the pythonpath - only updates model if asked to.
     * @return
     */
    private boolean doIt(boolean force) {
        if (project != null) {
            try {
                boolean changed = false;
                IPythonPathNature pythonPathNature = PythonNature.getPythonPathNature(project);

                String sourcePath = pythonPathNature.getProjectSourcePath(false);
                String externalSourcePath = pythonPathNature.getProjectExternalSourcePath(false);
                Map<String, String> variableSubstitution = pythonPathNature.getVariableSubstitution(false);

                String newSourcePath = StringUtils.leftAndRightTrim(treeSourceFolders.getTreeItemsAsStr(), '|');
                String newExternalSourcePath = StringUtils.leftAndRightTrim(treeExternalLibs.getTreeItemsAsStr(), '|');
                Map<String, String> newVariableSubstitution = tabVariables.getTreeItemsAsMap();

                if (checkIfShouldBeSet(sourcePath, newSourcePath)) {
                    pythonPathNature.setProjectSourcePath(newSourcePath);
                    changed = true;
                }

                if (checkIfShouldBeSet(externalSourcePath, newExternalSourcePath)) {
                    pythonPathNature.setProjectExternalSourcePath(newExternalSourcePath);
                    changed = true;
                }

                if (checkIfShouldBeSet(variableSubstitution, newVariableSubstitution)) {
                    pythonPathNature.setVariableSubstitution(newVariableSubstitution);
                    changed = true;
                }

                PythonNature pythonNature = PythonNature.getPythonNature(project);
                if (pythonNature != null && (changed || force || pythonNature.getAstManager() == null)) {
                    pythonNature.rebuildPath();
                }

            } catch (Exception e) {
                Log.log(IStatus.ERROR, "Unexpected error setting project properties", e);
            }
        }
        return true;
    }

    @SuppressWarnings({ "rawtypes" })
    private boolean checkIfShouldBeSet(Object oldVal, Object newVal) {
        if (oldVal == null) {
            if (newVal == null) {
                return false;//both null
            }
            if (newVal instanceof String) {

                String string = (String) newVal;
                if (string.trim().length() == 0) {
                    return false; //both are empty
                }

            } else if (newVal instanceof Map) {
                Map map = (Map) newVal;
                if (map.size() == 0) {
                    return false; //both are empty
                }
            } else {
                throw new AssertionError("Unexpected: " + newVal);
            }

            return true;
        }

        if (!oldVal.equals(newVal)) {
            return true;
        }
        return false;
    }
}
