package bndtools.views.repository;

import org.eclipse.swt.SWT;
import org.eclipse.swt.events.ModifyEvent;
import org.eclipse.swt.events.ModifyListener;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Text;
import org.eclipse.ui.IMemento;
import org.osgi.framework.Constants;
import org.osgi.framework.Filter;
import org.osgi.framework.FrameworkUtil;
import org.osgi.framework.InvalidSyntaxException;
import org.osgi.namespace.service.ServiceNamespace;
import org.osgi.resource.Namespace;

import aQute.bnd.osgi.resource.CapReqBuilder;

public class ArbitraryNamespaceSearchPanel extends SearchPanel {

    private String namespace;
    private String filterStr = "";

    private Control focusControl;
    private Label lblFilterHint;

    @Override
    public Control createControl(Composite parent) {
        Composite container = new Composite(parent, SWT.NONE);
        GridLayout layout = new GridLayout(2, false);
        container.setLayout(layout);

        Label lblInstruction = new Label(container, SWT.WRAP | SWT.LEFT);
        lblInstruction.setText("Enter a capability namespace and filter expression in OSGi standard format. Refer to OSGi Core specification, section 3.2.7 \"Filter Syntax\".");
        lblInstruction.setLayoutData(new GridData(SWT.FILL, SWT.TOP, true, false, 2, 1));

        new Label(container, SWT.NONE).setText("Namespace:");
        final Text txtNamespace = new Text(container, SWT.BORDER);
        if (namespace != null)
            txtNamespace.setText(namespace);
        txtNamespace.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false));
        txtNamespace.addModifyListener(new ModifyListener() {
            @Override
            public void modifyText(ModifyEvent e) {
                namespace = txtNamespace.getText()
                    .trim();
                validate();
            }
        });

        new Label(container, SWT.NONE).setText("Filter Expression:");
        @SuppressWarnings("unused")
        Label lblSpacer2 = new Label(container, SWT.NONE); // spacer

        final Text txtFilter = new Text(container, SWT.MULTI | SWT.BORDER);
        txtFilter.setMessage("enter OSGi-style filter");
        if (filterStr != null)
            txtFilter.setText(filterStr);
        GridData gdArbitrarySearchFilter = new GridData(SWT.FILL, SWT.FILL, true, true, 2, 1);
        gdArbitrarySearchFilter.heightHint = 50;
        txtFilter.setLayoutData(gdArbitrarySearchFilter);
        txtFilter.addModifyListener(new ModifyListener() {
            @Override
            public void modifyText(ModifyEvent e) {
                filterStr = txtFilter.getText()
                    .trim();
                validate();
            }
        });

        lblFilterHint = new Label(container, SWT.NONE);
        lblFilterHint.setText("Example: (&&(name=value)(version>=1.0))");
        lblFilterHint.setLayoutData(new GridData(SWT.FILL, SWT.NONE, true, false, 2, 1));

        validate();

        focusControl = txtNamespace;
        return container;
    }

    private void validate() {
        try {
            if (namespace == null || namespace.length() == 0) {
                setError(null);
                setRequirement(null);
                return;
            }

            for (int i = 0; i < namespace.length(); i++) {
                char c = namespace.charAt(i);
                if ('.' == c) {
                    if (i == 0 || i == namespace.length() - 1)
                        throw new IllegalArgumentException("Namespace cannot have leading or trailing '.' character");
                    else if ('.' == namespace.charAt(i - 1))
                        throw new IllegalArgumentException("Namespace cannot have repeated '.' characters");
                } else if (!Character.isLetterOrDigit(c) && c != '-' && c != '_')
                    throw new IllegalArgumentException(String.format("Invalid character in namespace: '%c'", c));
            }
            updateFilterExpressionHint(namespace);

            CapReqBuilder builder = new CapReqBuilder(namespace);
            if (filterStr != null && filterStr.trim()
                .length() > 0) {
                try {
                    Filter filter = FrameworkUtil.createFilter(filterStr.trim());
                    builder.addDirective(Namespace.REQUIREMENT_FILTER_DIRECTIVE, filter.toString());
                } catch (InvalidSyntaxException e) {
                    throw new IllegalArgumentException("Invalid filter string: " + e.getMessage());
                }
            }
            setRequirement(builder.buildSyntheticRequirement());
            setError(null);
        } catch (Exception e) {
            setError(e.getMessage());
            setRequirement(null);
        }
    }

    private void updateFilterExpressionHint(String namespace) {
        String hint;
        if (ServiceNamespace.SERVICE_NAMESPACE.equals(namespace))
            hint = String.format("(%s=fully-qualified-classname)", Constants.OBJECTCLASS);
        else
            // double ampersand because it's a mnemonic in SWT... FFS!
            hint = String.format("(&&(%s=value)(version>=1.0))", namespace);

        lblFilterHint.setText("Example: " + hint);
    }

    @Override
    public void setFocus() {
        focusControl.setFocus();
    }

    @Override
    public void saveState(IMemento memento) {
        memento.putString("namespace", namespace);
        memento.putString("filter", filterStr);
    }

    @Override
    public void restoreState(IMemento memento) {
        namespace = memento.getString("namespace");
        filterStr = memento.getString("filter");
    }

}
