/******************************************************************************
* Copyright (C) 2013  Fabio Zadrozny
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
*     Fabio Zadrozny <fabiofz@gmail.com> - initial API and implementation
******************************************************************************/
package org.python.pydev.shared_ui.quick_outline;

import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Status;
import org.eclipse.jface.viewers.ILabelProvider;
import org.eclipse.jface.viewers.ITreeContentProvider;
import org.eclipse.jface.viewers.TreeViewer;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.KeyEvent;
import org.eclipse.swt.events.KeyListener;
import org.eclipse.swt.events.ShellEvent;
import org.eclipse.swt.events.ShellListener;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.ui.progress.UIJob;
import org.python.pydev.shared_core.structure.DataAndImageTreeNode;
import org.python.pydev.shared_ui.SharedUiPlugin;
import org.python.pydev.shared_ui.dialogs.DialogMemento;
import org.python.pydev.shared_ui.dialogs.TreeSelectionDialog;

public abstract class BaseQuickOutlineSelectionDialog extends TreeSelectionDialog {

    /**
     * Structure without parents.
     */
    protected DataAndImageTreeNode<Object> root;

    /**
     * Structure with the parent methods.
     */
    protected DataAndImageTreeNode<Object> rootWithParents;

    /**
     * The first line selected (starts at 1)
     */
    protected int startLineIndex = -1;

    protected DataAndImageTreeNode<Object> initialSelection;

    /**
     * Helper to save/restore geometry.
     */
    private final DialogMemento memento;

    /**
     * Listener to handle the 2nd ctrl+O
     */
    private KeyListener ctrlOlistener;

    /**
     * Should we show the parents or not?
     */
    private boolean showParentHierarchy;

    /**
     * Label indicating what are we showing.
     */
    private Label labelCtrlO;

    protected final UIJob uiJobSetRootWithParentsInput = new UIJob("Set input") {

        @Override
        public IStatus runInUIThread(IProgressMonitor monitor) {
            if (!monitor.isCanceled()) {
                getTreeViewer().setInput(rootWithParents);
            } else {
                //Will be recalculated if asked again!
                rootWithParents = null;
            }

            return Status.OK_STATUS;
        }
    };

    private final boolean canShowParentHierarchy;

    protected BaseQuickOutlineSelectionDialog(Shell shell, String pluginId, ILabelProvider labelProvider,
            ITreeContentProvider contentProvider, boolean canShowParentHierarchy) {
        super(shell, labelProvider, contentProvider);
        setShellStyle(getShellStyle() & ~SWT.APPLICATION_MODAL); //Not modal because then the user may cancel the progress.
        if (SharedUiPlugin.getDefault() != null) {
            memento = new DialogMemento(getShell(), pluginId + ".actions.QuickShowOutline");
        } else {
            memento = null;
        }
        this.canShowParentHierarchy = canShowParentHierarchy;

        setMessage("Filter (press enter to go to selected element)");
        setTitle("Quick Outline");
        setAllowMultiple(false);
        this.showParentHierarchy = false;
    }

    protected void toggleShowParentHierarchy() {
        showParentHierarchy = !showParentHierarchy;
        updateShowParentHierarchyMessage();

        TreeViewer treeViewer = this.getTreeViewer();
        if (showParentHierarchy) {
            //Create the DataAndImageTreeNode structure if it's still not created...
            calculateHierarchyWithParents();
        } else {
            calculateHierarchy();
            treeViewer.setInput(root);
        }
    }

    protected abstract void calculateHierarchy();

    protected abstract void calculateHierarchyWithParents();

    @Override
    protected void configureShell(final Shell shell) {
        super.configureShell(shell);
        //Whenever the shell is deactivated, we want to go on and close it (i.e.: work as a popup dialog)
        shell.addShellListener(new ShellListener() {

            public void shellIconified(ShellEvent e) {
            }

            public void shellDeiconified(ShellEvent e) {
            }

            public void shellDeactivated(ShellEvent e) {
                shell.close();
            }

            public void shellClosed(ShellEvent e) {
            }

            public void shellActivated(ShellEvent e) {
            }
        });

    }

    public Control createDialogArea(Composite parent) {
        if (memento != null) {
            memento.readSettings();
        }
        Control ret = super.createDialogArea(parent);
        if (canShowParentHierarchy) {
            ctrlOlistener = new KeyListener() {

                public void keyReleased(KeyEvent e) {
                }

                public void keyPressed(KeyEvent e) {
                    if ((e.keyCode == 'o' || e.keyCode == 'O') && e.stateMask == SWT.CTRL) {
                        toggleShowParentHierarchy();
                    }
                }
            };
            this.text.addKeyListener(ctrlOlistener);
        }

        this.text.addKeyListener(new KeyListener() {

            public void keyReleased(KeyEvent e) {
            }

            public void keyPressed(KeyEvent e) {
                if (e.keyCode == SWT.CR || e.keyCode == SWT.LF || e.keyCode == SWT.KEYPAD_CR) {
                    okPressed();
                }
            }
        });

        if (canShowParentHierarchy) {
            this.getTreeViewer().getTree().addKeyListener(ctrlOlistener);
        }
        return ret;
    }

    /* (non-Javadoc)
     * @see org.eclipse.ui.dialogs.SelectionStatusDialog#createButtonBar(org.eclipse.swt.widgets.Composite)
     */
    @Override
    protected Control createButtonBar(Composite parent) {
        if (canShowParentHierarchy) {
            labelCtrlO = new Label(parent, SWT.NONE);
            this.labelCtrlO.addKeyListener(ctrlOlistener);
            updateShowParentHierarchyMessage();
        }
        return labelCtrlO;
    }

    protected void updateShowParentHierarchyMessage() {
        if (canShowParentHierarchy) {
            if (showParentHierarchy) {
                labelCtrlO.setText("Press Ctrl+O to hide parent hierarchy.");
            } else {
                labelCtrlO.setText("Press Ctrl+O to show parent hierarchy.");
            }
        }
    }

    public boolean close() {
        if (memento != null) {
            memento.writeSettings(getShell());
        }
        return super.close();
    }

    protected Point getInitialSize() {
        if (memento != null) {
            return memento.getInitialSize(super.getInitialSize(), getShell());
        }
        return new Point(640, 480);
    }

    protected Point getInitialLocation(Point initialSize) {
        if (memento != null) {
            return memento.getInitialLocation(initialSize, super.getInitialLocation(initialSize), getShell());
        }
        return new Point(250, 250);
    }

    @Override
    protected int getDefaultMargins() {
        return 0;
    }

    @Override
    protected int getDefaultSpacing() {
        return 0;
    }
}
