/*******************************************************************************
 * Copyright (c) 2010 Neil Bartlett.
 * 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:
 *     Neil Bartlett - initial API and implementation
 *******************************************************************************/
package bndtools.editor.imports;

import java.beans.PropertyChangeEvent;
import java.util.Arrays;
import java.util.Collection;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;

import org.bndtools.utils.swt.SWTUtil;
import org.eclipse.jface.action.Action;
import org.eclipse.jface.dialogs.IMessageProvider;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.ModifyEvent;
import org.eclipse.swt.events.ModifyListener;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
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.Text;
import org.eclipse.ui.forms.IMessageManager;
import org.eclipse.ui.forms.widgets.FormToolkit;
import org.eclipse.ui.forms.widgets.Section;

import aQute.bnd.build.model.BndEditModel;
import aQute.bnd.build.model.clauses.ImportPattern;
import aQute.bnd.header.Attrs;
import aQute.bnd.osgi.Constants;
import bndtools.editor.pkgpatterns.PkgPatternsListPart;
import bndtools.utils.ModificationLock;

public class ImportPatternsListPart extends PkgPatternsListPart<ImportPattern> {

    private class FixMissingStarsAction extends Action {
        public FixMissingStarsAction(String message) {
            super(message);
        }

        @Override
        public void run() {
            // Remove existing "*" patterns that are not in the last place
            List<ImportPattern> toRemove = new LinkedList<ImportPattern>();
            for (Iterator<ImportPattern> iter = getClauses().iterator(); iter.hasNext();) {
                ImportPattern pattern = iter.next();
                if (pattern.getName()
                    .equals("*") && iter.hasNext()) {
                    toRemove.add(pattern);
                }
            }
            if (!toRemove.isEmpty()) {
                doRemoveClauses(toRemove);
            }

            // Add a "*" at the end, if not already present
            List<ImportPattern> patterns = getClauses();
            if (patterns.size() != 0 && !patterns.get(patterns.size() - 1)
                .getName()
                .equals("*")) {
                ImportPattern starPattern = new ImportPattern("*", new Attrs());
                ImportPatternsListPart.super.doAddClauses(Arrays.asList(starPattern), -1, false);
            }
        }
    }

    private final ModificationLock modifyLock = new ModificationLock();
    private Text txtPattern;
    private Text txtVersion;
    private Button btnOptional;
    private Composite pnlDetails;

    public ImportPatternsListPart(Composite parent, FormToolkit toolkit, int style) {
        super(parent, toolkit, style, Constants.IMPORT_PACKAGE, "Customise Imports", new ImportPatternLabelProvider());
        addPropertyChangeListener(PROP_SELECTION, this);
    }

    @Override
    protected void createSection(Section section, FormToolkit toolkit) {
        super.createSection(section, toolkit);

        Composite parentComposite = (Composite) getSection().getClient();
        pnlDetails = toolkit.createComposite(parentComposite);

        toolkit.createLabel(pnlDetails, "Pattern:");
        txtPattern = toolkit.createText(pnlDetails, "");

        toolkit.createLabel(pnlDetails, "Version:");
        txtVersion = toolkit.createText(pnlDetails, "", SWT.BORDER);
        btnOptional = toolkit.createButton(pnlDetails, "Optional", SWT.CHECK);

        pnlDetails.setLayout(new GridLayout(5, false));
        txtPattern.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false));
        txtVersion.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false));

        pnlDetails.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));

        SWTUtil.recurseEnable(false, pnlDetails);

        txtPattern.addModifyListener(new ModifyListener() {
            @Override
            public void modifyText(ModifyEvent e) {
                if (!modifyLock.isUnderModification()) {
                    List<ImportPattern> selectedClauses = getSelection();
                    if (selectedClauses.size() == 1) {
                        selectedClauses.get(0)
                            .setName(txtPattern.getText());
                        updateLabels(selectedClauses);
                        validate();
                        markDirty();
                    }
                }
            }
        });
        txtVersion.addModifyListener(new ModifyListener() {
            @Override
            public void modifyText(ModifyEvent e) {
                if (!modifyLock.isUnderModification()) {
                    String text = txtVersion.getText();
                    if (text.length() == 0)
                        text = null;

                    List<ImportPattern> selectedClauses = getSelection();
                    for (ImportPattern clause : selectedClauses) {
                        clause.getAttribs()
                            .put(Constants.VERSION_ATTRIBUTE, text);
                    }
                    updateLabels(selectedClauses);
                    validate();
                    markDirty();
                }
            }
        });
        btnOptional.addSelectionListener(new SelectionAdapter() {
            @Override
            public void widgetSelected(SelectionEvent e) {
                if (!modifyLock.isUnderModification()) {
                    boolean optional = btnOptional.getSelection();
                    List<ImportPattern> patterns = getSelection();
                    for (ImportPattern pattern : patterns) {
                        pattern.setOptional(optional);
                    }
                    updateLabels(patterns);
                    validate();
                    markDirty();
                }
            }
        });
    }

    @Override
    protected void doAddClauses(Collection<? extends ImportPattern> clauses, int index, boolean select) {
        boolean appendStar = getClauses().isEmpty();

        super.doAddClauses(clauses, index, select);

        if (appendStar) {
            ImportPattern starPattern = new ImportPattern("*", new Attrs()); //$NON-NLS-1$
            super.doAddClauses(Arrays.asList(starPattern), -1, false);
        }
    }

    @Override
    public void validate() {
        IMessageManager msgs = getManagedForm().getMessageManager();
        msgs.setDecorationPosition(SWT.TOP | SWT.RIGHT);

        String noStarWarning = null;
        String actionMessage = null;
        List<ImportPattern> patterns = getClauses();
        if (!patterns.isEmpty()) {
            for (Iterator<ImportPattern> iter = patterns.iterator(); iter.hasNext();) {
                ImportPattern pattern = iter.next();
                if (pattern.getName()
                    .equals("*") && iter.hasNext()) {
                    noStarWarning = "The catch-all pattern \"*\" should be in the last position.";
                    actionMessage = "Move \"*\" pattern to the last position.";
                    break;
                }
            }

            if (noStarWarning == null) {
                ImportPattern last = patterns.get(patterns.size() - 1);
                if (!last.getName()
                    .equals("*")) {
                    noStarWarning = "The catch-all pattern \"*\" should be present and in the last position.";
                    actionMessage = "Add missing \"*\" pattern.";
                }
            }
        }
        if (noStarWarning != null) {
            msgs.addMessage("_warning_no_star", noStarWarning, new FixMissingStarsAction(actionMessage), IMessageProvider.WARNING);
        } else {
            msgs.removeMessage("_warning_no_star");
        }
    }

    @Override
    protected ImportPattern newHeaderClause(String text) {
        return new ImportPattern(text, new Attrs());
    }

    @Override
    protected List<ImportPattern> loadFromModel(BndEditModel model) {
        return model.getImportPatterns();
    }

    @Override
    protected void saveToModel(BndEditModel model, List<? extends ImportPattern> clauses) {
        model.setImportPatterns(clauses);
    }

    @Override
    public void propertyChange(PropertyChangeEvent evt) {
        if (PROP_SELECTION.equals(evt.getPropertyName())) {
            final List<ImportPattern> selectedClauses = getSelection();
            modifyLock.modifyOperation(new Runnable() {
                @Override
                public void run() {
                    if (selectedClauses.isEmpty()) {
                        SWTUtil.recurseEnable(false, pnlDetails);
                        txtPattern.setText("");
                        txtVersion.setText("");
                        btnOptional.setSelection(false);
                    } else if (selectedClauses.size() == 1) {
                        SWTUtil.recurseEnable(true, pnlDetails);
                        ImportPattern pattern = selectedClauses.get(0);
                        txtPattern.setText(pattern.getName() != null ? pattern.getName() : "");
                        txtVersion.setText(pattern.getVersionRange() != null ? pattern.getVersionRange() : "");
                        btnOptional.setSelection(pattern.isOptional());
                    } else {
                        SWTUtil.recurseEnable(false, pnlDetails);
                        btnOptional.setEnabled(true);
                        pnlDetails.setEnabled(true);

                        boolean differs = false;
                        boolean first = selectedClauses.get(0)
                            .isOptional();
                        for (ImportPattern pattern : selectedClauses) {
                            if (first != pattern.isOptional()) {
                                differs = true;
                                break;
                            }
                        }
                        if (differs) {
                            btnOptional.setGrayed(true);
                        } else {
                            btnOptional.setGrayed(false);
                            btnOptional.setSelection(first);
                        }
                    }

                }
            });
        } else {
            super.propertyChange(evt);
        }
    }
}
