package bndtools.editor.workspace;

import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;

import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.WorkspaceJob;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IConfigurationElement;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Platform;
import org.eclipse.core.runtime.Status;
import org.eclipse.jface.viewers.ArrayContentProvider;
import org.eclipse.jface.viewers.ISelectionChangedListener;
import org.eclipse.jface.viewers.ISelectionProvider;
import org.eclipse.jface.viewers.IStructuredSelection;
import org.eclipse.jface.viewers.SelectionChangedEvent;
import org.eclipse.jface.viewers.TableViewer;
import org.eclipse.jface.window.Window;
import org.eclipse.jface.wizard.WizardDialog;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.KeyAdapter;
import org.eclipse.swt.events.KeyEvent;
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.Table;
import org.eclipse.swt.widgets.ToolBar;
import org.eclipse.swt.widgets.ToolItem;
import org.eclipse.ui.ISharedImages;
import org.eclipse.ui.PlatformUI;
import org.eclipse.ui.forms.IManagedForm;
import org.eclipse.ui.forms.SectionPart;
import org.eclipse.ui.forms.editor.IFormPage;
import org.eclipse.ui.forms.widgets.FormToolkit;
import org.eclipse.ui.forms.widgets.Section;
import org.eclipse.ui.ide.ResourceUtil;
import org.eclipse.ui.plugin.AbstractUIPlugin;

import aQute.bnd.build.model.BndEditModel;
import aQute.bnd.build.model.clauses.HeaderClause;
import aQute.bnd.header.Attrs;
import aQute.bnd.osgi.Constants;
import bndtools.Plugin;

public class PluginsPart extends SectionPart implements PropertyChangeListener {

    private final Image editImg = AbstractUIPlugin.imageDescriptorFromPlugin(Plugin.PLUGIN_ID, "/icons/pencil.png")
        .createImage();
    private final Image refreshImg = AbstractUIPlugin.imageDescriptorFromPlugin(Plugin.PLUGIN_ID, "icons/arrow_refresh.png")
        .createImage();

    private final Map<String, IConfigurationElement> configElements = new HashMap<String, IConfigurationElement>();

    private List<HeaderClause> data;

    private Table table;
    private TableViewer viewer;

    private ToolItem editItemTool;
    private ToolItem removeItemTool;

    private BndEditModel model;

    public PluginsPart(Composite parent, FormToolkit toolkit, int style) {
        super(parent, toolkit, style);

        IConfigurationElement[] configElems = Platform.getExtensionRegistry()
            .getConfigurationElementsFor(Plugin.PLUGIN_ID, "bndPlugins");
        for (IConfigurationElement configElem : configElems) {
            String className = configElem.getAttribute("class");
            configElements.put(className, configElem);
        }

        createSection(getSection(), toolkit);
    }

    final void createSection(Section section, FormToolkit toolkit) {
        section.setText("Plugins");
        section.setDescription("Bnd plugins are used to specify repositories and extended behaviours.");

        createToolBar(section);

        Composite composite = toolkit.createComposite(section, SWT.NONE);
        table = toolkit.createTable(composite, SWT.FULL_SELECTION | SWT.SINGLE | SWT.BORDER);

        viewer = new TableViewer(table);
        viewer.setContentProvider(ArrayContentProvider.getInstance());
        viewer.setLabelProvider(new PluginClauseLabelProvider(configElements));

        Button btnReload = toolkit.createButton(composite, "Reload", SWT.NONE);
        btnReload.setImage(refreshImg);

        // Listeners
        viewer.addSelectionChangedListener(new ISelectionChangedListener() {
            @Override
            public void selectionChanged(SelectionChangedEvent event) {
                boolean enable = !viewer.getSelection()
                    .isEmpty();
                removeItemTool.setEnabled(enable);
                editItemTool.setEnabled(enable);
            }
        });
        table.addKeyListener(new KeyAdapter() {
            @Override
            public void keyReleased(KeyEvent e) {
                if (e.character == SWT.DEL) {
                    doRemove();
                } else if (e.character == '+') {
                    doAdd();
                }
            }
        });
        btnReload.addSelectionListener(new SelectionAdapter() {
            @Override
            public void widgetSelected(SelectionEvent e) {
                doReload();
            }
        });

        composite.setLayout(new GridLayout(1, false));
        table.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
        btnReload.setLayoutData(new GridData(SWT.RIGHT, SWT.CENTER, false, false));

        section.setClient(composite);

    }

    void doReload() {
        IFormPage page = (IFormPage) getManagedForm().getContainer();
        final IFile file = ResourceUtil.getFile(page.getEditorInput());
        if (file != null && file.exists()) {
            WorkspaceJob job = new WorkspaceJob("Reload Plugins") {
                @Override
                public IStatus runInWorkspace(IProgressMonitor monitor) throws CoreException {
                    file.touch(monitor);
                    return Status.OK_STATUS;
                }
            };
            job.setUser(true);
            job.schedule();
        }
    }

    void createToolBar(Section section) {
        ToolBar toolbar = new ToolBar(section, SWT.FLAT);
        section.setTextClient(toolbar);

        ToolItem addItem = new ToolItem(toolbar, SWT.PUSH);
        addItem.setImage(PlatformUI.getWorkbench()
            .getSharedImages()
            .getImage(ISharedImages.IMG_OBJ_ADD));
        addItem.setToolTipText("Add Plugin");
        addItem.addSelectionListener(new SelectionAdapter() {
            @Override
            public void widgetSelected(SelectionEvent e) {
                doAdd();
            }
        });

        editItemTool = new ToolItem(toolbar, SWT.PUSH);
        editItemTool.setImage(editImg);
        editItemTool.setToolTipText("Edit");
        editItemTool.setEnabled(false);
        editItemTool.addSelectionListener(new SelectionAdapter() {
            @Override
            public void widgetSelected(SelectionEvent e) {
                doEdit();
            }
        });

        removeItemTool = new ToolItem(toolbar, SWT.PUSH);
        removeItemTool.setImage(PlatformUI.getWorkbench()
            .getSharedImages()
            .getImage(ISharedImages.IMG_TOOL_DELETE));
        removeItemTool.setDisabledImage(PlatformUI.getWorkbench()
            .getSharedImages()
            .getImage(ISharedImages.IMG_TOOL_DELETE_DISABLED));
        removeItemTool.setToolTipText("Remove");
        removeItemTool.setEnabled(false);
        removeItemTool.addSelectionListener(new SelectionAdapter() {
            @Override
            public void widgetSelected(SelectionEvent e) {
                doRemove();
            }
        });
    }

    @Override
    public void initialize(IManagedForm form) {
        super.initialize(form);

        model = (BndEditModel) form.getInput();
        model.addPropertyChangeListener(Constants.PLUGIN, this);
    }

    @Override
    public void dispose() {
        super.dispose();
        if (model != null)
            model.removePropertyChangeListener(Constants.PLUGIN, this);
        editImg.dispose();
        refreshImg.dispose();
    }

    @Override
    public void refresh() {
        List<HeaderClause> modelData = model.getPlugins();
        if (modelData != null)
            this.data = new ArrayList<HeaderClause>(modelData);
        else
            this.data = new LinkedList<HeaderClause>();
        viewer.setInput(this.data);
        super.refresh();
    }

    @Override
    public void commit(boolean onSave) {
        super.commit(onSave);
        model.setPlugins(data);
    }

    @Override
    public void propertyChange(PropertyChangeEvent evt) {
        IFormPage page = (IFormPage) getManagedForm().getContainer();
        if (page.isActive()) {
            refresh();
        } else {
            markStale();
        }
    }

    void doAdd() {
        PluginSelectionWizard wizard = new PluginSelectionWizard();
        WizardDialog dialog = new WizardDialog(getManagedForm().getForm()
            .getShell(), wizard);
        if (dialog.open() == Window.OK) {
            HeaderClause newPlugin = wizard.getHeader();

            data.add(newPlugin);
            viewer.add(newPlugin);
            markDirty();
        }
    }

    void doEdit() {
        HeaderClause header = (HeaderClause) ((IStructuredSelection) viewer.getSelection()).getFirstElement();
        if (header != null) {
            Attrs copyOfProperties = new Attrs(header.getAttribs());

            IConfigurationElement configElem = configElements.get(header.getName());
            PluginEditWizard wizard = new PluginEditWizard(configElem, copyOfProperties);
            WizardDialog dialog = new WizardDialog(getManagedForm().getForm()
                .getShell(), wizard);

            if (dialog.open() == Window.OK && wizard.isChanged()) {
                header.getAttribs()
                    .clear();
                header.getAttribs()
                    .putAll(copyOfProperties);

                viewer.update(header, null);
                markDirty();
            }
        }
    }

    void doRemove() {
        IStructuredSelection sel = (IStructuredSelection) viewer.getSelection();

        viewer.remove(sel.toArray());
        data.removeAll(sel.toList());

        if (!sel.isEmpty())
            markDirty();
    }

    public ISelectionProvider getSelectionProvider() {
        return viewer;
    }

}
