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.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<>();
			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(e -> {
			if (!modifyLock.isUnderModification()) {
				List<ImportPattern> selectedClauses = getSelection();
				if (selectedClauses.size() == 1) {
					selectedClauses.get(0)
						.setName(txtPattern.getText());
					updateLabels(selectedClauses);
					validate();
					markDirty();
				}
			}
		});
		txtVersion.addModifyListener(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(() -> {
				if (selectedClauses.isEmpty()) {
					SWTUtil.recurseEnable(false, pnlDetails);
					txtPattern.setText("");
					txtVersion.setText("");
					btnOptional.setSelection(false);
				} else if (selectedClauses.size() == 1) {
					SWTUtil.recurseEnable(true, pnlDetails);
					ImportPattern pattern1 = selectedClauses.get(0);
					txtPattern.setText(pattern1.getName() != null ? pattern1.getName() : "");
					txtVersion.setText(pattern1.getVersionRange() != null ? pattern1.getVersionRange() : "");
					btnOptional.setSelection(pattern1.isOptional());
				} else {
					SWTUtil.recurseEnable(false, pnlDetails);
					btnOptional.setEnabled(true);
					pnlDetails.setEnabled(true);

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

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