/**
 * 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 May 23, 2005
 *
 * @author Fabio Zadrozny
 */
package org.python.pydev.ui.pythonpathconf;

import java.lang.reflect.InvocationTargetException;
import java.util.Set;

import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.jface.dialogs.ProgressMonitorDialog;
import org.eclipse.jface.operation.IRunnableWithProgress;
import org.eclipse.jface.preference.FieldEditorPreferencePage;
import org.eclipse.jface.viewers.IStructuredContentProvider;
import org.eclipse.jface.viewers.LabelProvider;
import org.eclipse.jface.viewers.Viewer;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.ui.IWorkbench;
import org.eclipse.ui.IWorkbenchPreferencePage;
import org.eclipse.ui.IWorkbenchWindow;
import org.eclipse.ui.PlatformUI;
import org.eclipse.ui.dialogs.ListDialog;
import org.eclipse.ui.dialogs.ListSelectionDialog;
import org.eclipse.ui.dialogs.SelectionDialog;
import org.python.pydev.core.IInterpreterInfo;
import org.python.pydev.core.IInterpreterManager;
import org.python.pydev.core.log.Log;
import org.python.pydev.plugin.PydevPlugin;
import org.python.pydev.shared_ui.UIConstants;
import org.python.pydev.shared_ui.utils.AsynchronousProgressMonitorDialog;
import org.python.pydev.ui.dialogs.PyDialogHelpers;
import org.python.pydev.ui.interpreters.AbstractInterpreterManager;

/**
 * @author Fabio Zadrozny
 */
public abstract class AbstractInterpreterPreferencesPage extends FieldEditorPreferencePage implements
        IWorkbenchPreferencePage {

    protected AbstractInterpreterEditor pathEditor;
    private boolean inApply = false;

    /**
     * Initializer sets the preference store
     */
    public AbstractInterpreterPreferencesPage() {
        super(GRID);
        setPreferenceStore(PydevPlugin.getDefault().getPreferenceStore());
        PyDialogHelpers.enableAskInterpreterStep(false);
    }

    @Override
    public void createControl(Composite parent) {
        super.createControl(parent);
    }

    protected abstract AbstractInterpreterEditor getInterpreterEditor(Composite p);

    /**
     * Creates a dialog that'll choose from a list of interpreter infos.
     */
    public static SelectionDialog createChooseIntepreterInfoDialog(IWorkbenchWindow workbenchWindow,
            IInterpreterInfo[] interpreters, String msg, boolean selectMultiple) {

        IStructuredContentProvider contentProvider = new IStructuredContentProvider() {

            @Override
            public Object[] getElements(Object inputElement) {
                if (inputElement instanceof IInterpreterInfo[]) {
                    return (IInterpreterInfo[]) inputElement;
                }
                return new Object[0];
            }

            @Override
            public void dispose() {
            }

            @Override
            public void inputChanged(Viewer viewer, Object oldInput, Object newInput) {

            }
        };

        LabelProvider labelProvider = new LabelProvider() {
            @Override
            public Image getImage(Object element) {
                return PydevPlugin.getImageCache().get(UIConstants.PY_INTERPRETER_ICON);
            }

            @Override
            public String getText(Object element) {
                if (element != null && element instanceof IInterpreterInfo) {
                    IInterpreterInfo info = (IInterpreterInfo) element;
                    return info.getNameForUI();
                }
                return super.getText(element);
            }
        };

        SelectionDialog selectionDialog;
        if (selectMultiple) {
            selectionDialog = new ListSelectionDialog(workbenchWindow.getShell(), interpreters, contentProvider,
                    labelProvider, msg) {
                @Override
                protected Control createContents(Composite parent) {
                    Control ret = super.createContents(parent);
                    org.python.pydev.plugin.PydevPlugin.setCssId(parent, "py-select-dialog", true);
                    return ret;
                }
            };
        } else {

            ListDialog listDialog = new ListDialog(workbenchWindow.getShell()) {
                @Override
                protected Control createContents(Composite parent) {
                    Control ret = super.createContents(parent);
                    org.python.pydev.plugin.PydevPlugin.setCssId(parent, "py-select-dialog", true);
                    return ret;
                }
            };

            listDialog.setContentProvider(contentProvider);
            listDialog.setLabelProvider(labelProvider);
            listDialog.setInput(interpreters);
            listDialog.setMessage(msg);
            selectionDialog = listDialog;
        }
        return selectionDialog;
    }

    @Override
    public void init(IWorkbench workbench) {
    }

    /**
     * Applies changes (if any)
     * @see org.eclipse.jface.preference.PreferencePage#performApply()
     */
    @Override
    protected void performApply() {
        this.inApply = true;
        try {
            super.performApply(); //calls performOk()
        } finally {
            this.inApply = false;
        }
    }

    /**
     * Restores the default values
     *
     * @see org.eclipse.jface.preference.PreferencePage#performDefaults()
     */
    @Override
    protected void performDefaults() {
        //don't do anything on defaults...
    }

    /**
     * Cancels any change
     *
     * @see org.eclipse.jface.preference.IPreferencePage#performCancel()
     */
    @Override
    public boolean performCancel() {
        //re-enable "configure interpreter" dialogs
        PyDialogHelpers.enableAskInterpreterStep(true);
        return super.performCancel();
    }

    /**
     * Applies changes (if any)
     *
     * @see org.eclipse.jface.preference.IPreferencePage#performOk()
     */
    @Override
    public boolean performOk() {
        //IMPORTANT: we must call the perform before restoring the modules because this
        //info is going to be used when restoring them.
        super.performOk();

        //we need to update the tree so that the environment variables stay correct.
        pathEditor.updateTree();

        IInterpreterManager interpreterManager = getInterpreterManager();
        String newStringToPersist = AbstractInterpreterManager.getStringToPersist(pathEditor.getExesList());
        String oldStringToPersist = AbstractInterpreterManager.getStringToPersist(interpreterManager
                .getInterpreterInfos());
        boolean changed;
        if (!newStringToPersist.equals(oldStringToPersist)) {
            changed = true;
        } else {
            changed = false;
        }

        if (changed || inApply) {
            //If the user just presses 'apply' and nothing changed, he'll be asked to restore information on one of
            //the current interpreters.
            restoreInterpreterInfos(changed);
        }

        if (!inApply) {
            //re-enable "configure interpreter" dialogs, but only upon exiting the wizard
            PyDialogHelpers.enableAskInterpreterStep(true);
        }
        return true;
    }

    /**
     * @return the interpreter manager associated to this page.
     */
    protected abstract IInterpreterManager getInterpreterManager();

    /**
     * Creates the editors - also provides a hook for getting a different interpreter editor
     */
    @Override
    protected void createFieldEditors() {
        Composite p = getFieldEditorParent();
        pathEditor = getInterpreterEditor(p);
        addField(pathEditor);
    }

    /**
     * Restores the modules. Is called when the user changed something in the editor and applies the change.
     *
     * Gathers all the info and calls the hook that really restores things within a thread, so that the user can
     * get information on the progress.
     *
     * Only the information on the default interpreter is stored.
     *
     * @param editorChanged whether the editor was changed (if it wasn't, we'll ask the user what to restore).
     * @return true if the info was restored and false otherwise.
     */
    protected void restoreInterpreterInfos(boolean editorChanged) {
        final Set<String> interpreterNamesToRestore = pathEditor.getInterpreterExeOrJarToRestoreAndClear();
        final IInterpreterInfo[] exesList = pathEditor.getExesList();

        if (!editorChanged && interpreterNamesToRestore.size() == 0) {
            IWorkbenchWindow workbenchWindow = PlatformUI.getWorkbench().getActiveWorkbenchWindow();
            SelectionDialog listDialog = createChooseIntepreterInfoDialog(workbenchWindow, exesList,
                    "Select interpreters to be restored", true);

            int open = listDialog.open();
            if (open != ListDialog.OK) {
                return;
            }
            Object[] result = listDialog.getResult();
            if (result == null || result.length == 0) {
                return;

            }
            for (Object o : result) {
                interpreterNamesToRestore.add(((IInterpreterInfo) o).getExecutableOrJar());
            }

        }

        //this is the default interpreter
        ProgressMonitorDialog monitorDialog = new AsynchronousProgressMonitorDialog(this.getShell());
        monitorDialog.setBlockOnOpen(false);

        try {
            IRunnableWithProgress operation = new IRunnableWithProgress() {

                @Override
                public void run(IProgressMonitor monitor) throws InvocationTargetException, InterruptedException {
                    monitor.beginTask("Restoring PYTHONPATH", IProgressMonitor.UNKNOWN);
                    try {
                        pathEditor.pushExpectedSetInfos();
                        //clear all but the ones that appear
                        getInterpreterManager().setInfos(exesList, interpreterNamesToRestore, monitor);
                    } finally {
                        pathEditor.popExpectedSetInfos();
                        monitor.done();
                    }
                }
            };

            monitorDialog.run(true, true, operation);

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

}
