package org.bndtools.utils.swt;

import java.beans.PropertyChangeListener;
import java.beans.PropertyChangeSupport;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

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.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Text;

public class FilterPanelPart {

    private static final String PROP_FILTER = "filter";
    private static final long SEARCH_DELAY = 1000;

    private final ScheduledExecutorService scheduler;

    private String filter;

    private Composite panel;
    private Text txtFilter;

    private final PropertyChangeSupport propSupport = new PropertyChangeSupport(this);
    private final Lock scheduledFilterLock = new ReentrantLock();
    private final Runnable updateFilterTask = new Runnable() {
        @Override
        public void run() {
            Display display = panel.getDisplay();
            Runnable update = new Runnable() {
                @Override
                public void run() {
                    String newFilter = txtFilter.getText();
                    setFilter(newFilter);
                }
            };
            if (display.getThread() == Thread.currentThread())
                update.run();
            else
                display.asyncExec(update);
        }
    };
    private ScheduledFuture<?> scheduledFilterUpdate = null;

    public FilterPanelPart(ScheduledExecutorService scheduler) {
        this.scheduler = scheduler;
    }

    public Control createControl(Composite parent) {
        return createControl(parent, 0, 0);
    }

    public Control createControl(Composite parent, int marginWidth, int marginHeight) {
        // CREATE CONTROLS
        panel = new Composite(parent, SWT.NONE);
        panel.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false));
        txtFilter = new Text(panel, SWT.SEARCH | SWT.ICON_SEARCH | SWT.ICON_CANCEL);
        txtFilter.setMessage("Enter search string");

        // INITIAL PROPERTIES
        if (filter != null)
            txtFilter.setText(filter);

        // LAYOUT
        GridLayout layout = new GridLayout(2, false);
        layout.marginHeight = marginHeight;
        layout.marginWidth = marginWidth;
        panel.setLayout(layout);
        txtFilter.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false));

        // LISTENERS
        txtFilter.addSelectionListener(new SelectionAdapter() {
            @Override
            public void widgetDefaultSelected(SelectionEvent ev) {
                try {
                    scheduledFilterLock.lock();
                    if (scheduledFilterUpdate != null)
                        scheduledFilterUpdate.cancel(true);
                } finally {
                    scheduledFilterLock.unlock();
                }

                String newFilter = (ev.detail == SWT.CANCEL) ? "" : txtFilter.getText();
                setFilter(newFilter);
            }
        });
        txtFilter.addModifyListener(new ModifyListener() {
            @Override
            public void modifyText(ModifyEvent ev) {
                try {
                    scheduledFilterLock.lock();
                    if (scheduledFilterUpdate != null)
                        scheduledFilterUpdate.cancel(true);
                    scheduledFilterUpdate = scheduler.schedule(updateFilterTask, SEARCH_DELAY, TimeUnit.MILLISECONDS);
                } finally {
                    scheduledFilterLock.unlock();
                }
            }
        });
        return panel;
    }

    public Text getFilterControl() {
        return txtFilter;
    }

    public String getFilter() {
        return filter;
    }

    public void setFilter(String filter) {
        String old = this.filter;
        this.filter = filter;
        propSupport.firePropertyChange(PROP_FILTER, old, filter);
    }

    public void setFocus() {
        if (txtFilter != null && !txtFilter.isDisposed())
            txtFilter.setFocus();
    }

    public void addPropertyChangeListener(PropertyChangeListener listener) {
        propSupport.addPropertyChangeListener(PROP_FILTER, listener);
    }

    public void removePropertyChangeListener(PropertyChangeListener listener) {
        propSupport.removePropertyChangeListener(PROP_FILTER, listener);
    }

}
