package edu.sc.seis.gee.task;

import java.awt.Font;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Insets;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.net.URISyntaxException;
import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
import java.util.Iterator;
import java.util.Map;
import java.util.TimeZone;
import javax.swing.Box;
import javax.swing.BoxLayout;
import javax.swing.JButton;
import javax.swing.JComboBox;
import javax.swing.JComponent;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JSpinner;
import javax.swing.SpinnerNumberModel;
import javax.swing.border.TitledBorder;
import javax.swing.text.DateFormatter;
import net.sf.nachocalendar.CalendarFactory;
import net.sf.nachocalendar.components.CalendarUtils;
import net.sf.nachocalendar.components.DateField;
import org.apache.log4j.Logger;
import org.hibernate.event.EventSource;
import org.xml.sax.SAXException;
import org.xml.sax.XMLReader;
import edu.iris.Fissures.Area;
import edu.iris.Fissures.BoxArea;
import edu.iris.Fissures.GlobalArea;
import edu.iris.Fissures.PointDistanceArea;
import edu.iris.Fissures.Quantity;
import edu.iris.Fissures.Time;
import edu.iris.Fissures.TimeRange;
import edu.iris.Fissures.IfEvent.EventAccess;
import edu.iris.Fissures.IfEvent.EventAccessOperations;
import edu.iris.Fissures.IfEvent.EventFinder;
import edu.iris.Fissures.IfEvent.EventFinderOperations;
import edu.iris.Fissures.IfEvent.EventSeqIterHolder;
import edu.iris.Fissures.model.GlobalAreaImpl;
import edu.iris.Fissures.model.MicroSecondDate;
import edu.iris.Fissures.model.QuantityImpl;
import edu.iris.Fissures.model.TimeInterval;
import edu.iris.Fissures.model.UnitImpl;
import edu.sc.seis.fissuresUtil.cache.AbstractJob;
import edu.sc.seis.fissuresUtil.cache.BulletproofVestFactory;
import edu.sc.seis.fissuresUtil.cache.CacheEvent;
import edu.sc.seis.fissuresUtil.cache.JobTracker;
import edu.sc.seis.fissuresUtil.cache.ProxyEventAccessOperations;
import edu.sc.seis.fissuresUtil.cache.ProxyEventDC;
import edu.sc.seis.fissuresUtil.cache.WorkerThreadPool;
import edu.sc.seis.fissuresUtil.chooser.ClockUtil;
import edu.sc.seis.fissuresUtil.exceptionHandler.GlobalExceptionHandler;
import edu.sc.seis.fissuresUtil.namingService.FissuresNamingService;
import edu.sc.seis.gee.BuildVersion;
import edu.sc.seis.gee.CommonAccess;
import edu.sc.seis.gee.NetworkGateKeeper;
import edu.sc.seis.gee.NoCorbaException;
import edu.sc.seis.gee.NoNetworkException;
import edu.sc.seis.gee.configurator.AbstractHandler;
import edu.sc.seis.gee.configurator.ConfigurationException;
import edu.sc.seis.seisFile.fdsnws.FDSNEventQuerier;
import edu.sc.seis.seisFile.fdsnws.FDSNEventQueryParams;
import edu.sc.seis.sod.source.event.FdsnEvent;

/**
 * QueryEventsTask.java Created: Wed Jan 16 08:25:02 2002
 * 
 * @author <a href="mailto:">Srinivasa Telukutla </a>
 * @version
 */
public class QueryEventsTask implements GUITask, SAXTask {

    public JComponent getMoreOptionsGUI() {
        return null;
    }

    public boolean hasMoreOptions() {
        return false;
    }

    public JComponent getGUI() {
        disabled = false;
        if(!guiInitialized && panel == null) {
            guiInitialized = true;
            panel = createGUI();
        }
        return panel;
    }

    public synchronized void invoke() {
        disabled = false;
    }

    public void destroy() {}

    public void configure(java.util.Map params) throws ConfigurationException {
        CommonAccess ca = CommonAccess.getCommonAccess();
        configParams = params;
        serverDNS = (String)configParams.get("serverDNS");
        serverName = (String)configParams.get("serverName");
        if(serverDNS == null || serverDNS.length() == 0 || serverName == null
                || serverName.length() == 0) {
            throw new ConfigurationException("Invlaid server specification: dns="
                    + serverDNS + " name=" + serverName);
        }
        String[] eventDestinationIds = TaskUtils.getParamStartsWith("eventDestinationId_",
                                                                    configParams);
        eventDestinations = new EventDestination[eventDestinationIds.length];
        TaskAction eventDestAction;
        for(int i = 0; i < eventDestinationIds.length; i++) {
            eventDestAction = ca.getTaskAction(eventDestinationIds[i]);
            if(eventDestAction != null) {
                eventDestinations[i] = (EventDestination)eventDestAction.getTask();
            } else {
                logger.warn("Can't load event destination task for id="
                        + eventDestinationIds[i]);
            }
        }
        if(params.containsKey("minDepth")) {
            initialMinDepth = Integer.parseInt((String)params.get("minDepth"));
        }
        if(params.containsKey("maxDepth")) {
            initialMaxDepth = Integer.parseInt((String)params.get("maxDepth"));
        }
        if(params.containsKey("minMag")) {
            initialMinMag = Float.parseFloat((String)params.get("minMag"));
        }
        if(params.containsKey("maxMag")) {
            initialMaxMag = Float.parseFloat((String)params.get("maxMag"));
        }
        if(params.containsKey("minLat")) {
            initialMinLat = Integer.parseInt((String)params.get("minLat"));
        }
        if(params.containsKey("maxLat")) {
            initialMaxLat = Integer.parseInt((String)params.get("maxLat"));
        }
        if(params.containsKey("minLon")) {
            initialMinLon = Integer.parseInt((String)params.get("minLon"));
        }
        if(params.containsKey("maxLon")) {
            initialMaxLon = Integer.parseInt((String)params.get("maxLon"));
        }
        if(params.containsKey("map")) {
            TaskAction mapTaskAction = ca.getTaskAction((String)params.get("map"));
            OpenMapTask mapTask = (OpenMapTask)mapTaskAction.getTask();
            boxArea = new BoxAreaDisplay(initialMinLat,
                                         initialMinLon,
                                         initialMaxLat,
                                         initialMaxLon,
                                         mapTask);
        } else {
            boxArea = new BoxAreaDisplay(initialMinLat,
                                         initialMinLon,
                                         initialMaxLat,
                                         initialMaxLon);
        }
        timeEditor = new TimeEditor();
        depthEditor = new DepthEditor();
        magEditor = new MagnitudeEditor();
        catEditor = new CatalogEditor();
        query = new EventQuery();
    }

    private static DateFormat configTimeParser = new SimpleDateFormat("yyyy-MM-dd");

    private int initialMinDepth = 0, initialMaxDepth = 1000;// in kilometers

    private float initialMinMag = 0.0f, initialMaxMag = 10.0f;

    private Date initialEndTime = ClockUtil.now();

    private int initialMinLat = -90, initialMaxLat = 90, initialMinLon = -180,
            initialMaxLon = 180;

    private Date initialStartTime = ClockUtil.now()
            .subtract(new TimeInterval(1, UnitImpl.WEEK));

    private class DateParser extends AbstractHandler {

        private DateParser(XMLReader parser, AbstractHandler parent, String name) {
            super(parser, parent);
            this.name = name;
        }

        public void endElement(String namespaceURI,
                               String localName,
                               String qName) throws SAXException {
            if(localName.equals("date")) {
                try {
                    d = new MicroSecondDate(configTimeParser.parse(lastString));
                } catch(ParseException e) {
                    throw new SAXException("Unable to parse "
                                                   + lastString
                                                   + " as an event query time.  It must be in the form yyyy-mm-dd",
                                           e);
                }
            } else if(localName.equals("unit")) {
                try {
                    timeUnit = UnitImpl.getUnitFromString(lastString);
                } catch(NoSuchFieldException e) {
                    throw new SAXException("Unable to find a unit named "
                                                   + lastString
                                                   + ". It must be the name of a field in UnitImpl.",
                                           e);
                }
            } else if(localName.equals("value")) {
                value = Double.parseDouble(lastString);
            } else {
                super.endElement(namespaceURI, localName, qName);
            }
        }

        public void characters(char[] ch, int start, int length) {
            lastString = new String(ch, start, length);
        }

        public void finished() {
            d = d.subtract(new TimeInterval(value, timeUnit));
            if(name.equals("startTime")) {
                initialStartTime = d;
            } else if(name.equals("endTime")) {
                initialEndTime = d;
            }
        }

        public MicroSecondDate getDate() {
            return d;
        }

        private double value = 0;

        private String name;

        private UnitImpl timeUnit = UnitImpl.DAY;

        private MicroSecondDate d = ClockUtil.now();

        private String lastString;
    }

    private class DepthEditor extends JPanel {

        public DepthEditor() {
            setBorder(new TitledBorder("Depth"));
            GridBagLayout layout = new GridBagLayout();
            GridBagConstraints constraints = new GridBagConstraints();
            setLayout(layout);
            JLabel from = new JLabel("From");
            constraints.gridx = 0;
            constraints.insets = HORIZONTAL_SPACER;
            layout.setConstraints(from, constraints);
            add(from);
            constraints.gridx++;
            constraints.insets = ZERO;
            layout.setConstraints(min, constraints);
            add(min);
            JLabel to = new JLabel("to");
            constraints.gridx++;
            constraints.insets = HORIZONTAL_SPACER;
            layout.setConstraints(to, constraints);
            add(to);
            constraints.gridx++;
            constraints.insets = ZERO;
            layout.setConstraints(max, constraints);
            add(max);
            JLabel in = new JLabel("in");
            constraints.gridx++;
            constraints.insets = HORIZONTAL_SPACER;
            layout.setConstraints(in, constraints);
            add(in);
            populateUnitField(unit);
            unit.setEditable(false);
            constraints.gridx++;
            constraints.insets = ZERO;
            layout.setConstraints(unit, constraints);
            add(unit);
        }

        public QuantityImpl getMin() {
            return getQuantity(((Integer)min.getValue()).intValue());
        }

        public QuantityImpl getMax() {
            return getQuantity(((Integer)max.getValue()).intValue());
        }

        private QuantityImpl getQuantity(int val) {
            return new QuantityImpl(val, getUnit());
        }

        private UnitImpl getUnit() {
            String selectedUnit = (String)unit.getSelectedItem();
            if(selectedUnit.equals(KILOMETER)) {
                return UnitImpl.KILOMETER;
            } else {
                return UnitImpl.METER;
            }
        }

        private JSpinner min = new JSpinner(new SpinnerNumberModel(initialMinDepth,
                                                                   0,
                                                                   1000,
                                                                   100));

        private JSpinner max = new JSpinner(new SpinnerNumberModel(initialMaxDepth,
                                                                   0,
                                                                   1000,
                                                                   100));

        private JComboBox unit = new JComboBox();
    }

    private class TimeEditor extends JPanel {

        public TimeEditor() {
            setBorder(new TitledBorder("Time"));
            GridBagLayout layout = new GridBagLayout();
            GridBagConstraints constraints = new GridBagConstraints();
            setLayout(layout);
            DateFormat dFormat = new SimpleDateFormat("MMM dd, yyyy");
            dFormat.setTimeZone(TimeZone.getTimeZone("GMT"));
            DateFormatter dateFormatter = new DateFormatter(dFormat);
            startDF = CalendarFactory.createDateField(dateFormatter);
            startDF.setValue(initialStartTime);
            endDF = CalendarFactory.createDateField(dateFormatter);
            endDF.setValue(initialEndTime);
            constraints.gridx = 0;
            JLabel startDateLabel = new JLabel("From ");
            constraints.insets = HORIZONTAL_SPACER;
            layout.setConstraints(startDateLabel, constraints);
            add(startDateLabel);
            constraints.gridx++;
            constraints.insets = ZERO;
            constraints.ipadx = 10;
            layout.setConstraints(startDF, constraints);
            add(startDF);
            JLabel endDateLabel = new JLabel("to");
            constraints.gridx++;
            constraints.insets = HORIZONTAL_SPACER;
            constraints.ipadx = 0;
            layout.setConstraints(endDateLabel, constraints);
            add(endDateLabel);
            constraints.gridx++;
            constraints.insets = ZERO;
            constraints.ipadx = 10;
            layout.setConstraints(endDF, constraints);
            add(endDF);
        }

        public Date getStart() {
            Date start = getDate(startDF, true);
            return start;
        }

        public Date getEnd() {
            Date end = getDate(endDF, false);
            return end;
        }

        public Date getDate(DateField control, boolean isStart) {
            try {
                return setTimeOnDate(CalendarUtils.convertToDate(control.getValue()),
                                     isStart);
            } catch(ParseException e) {
                GlobalExceptionHandler.handle("problem setting end date. returning now as the date",
                                              e);
            }
            return ClockUtil.now();
        }

        public void setStart(Date d) {
            setDate(startDF, d, true);
        }

        public void setEnd(Date d) {
            setDate(endDF, d, false);
        }

        private void setDate(DateField control, Date d, boolean isStart) {
            control.setValue(setTimeOnDate(d, isStart));
        }

        private Date setTimeOnDate(Date d, boolean isStart) {
            cal.setTime(d);
            int hours = 0, minutes = 0, seconds = 0, millis = 0;
            if(!isStart) {
                hours = 23;
                minutes = 59;
                seconds = 59;
                millis = 999;
            }
            cal.set(Calendar.HOUR_OF_DAY, hours);
            cal.set(Calendar.MINUTE, minutes);
            cal.set(Calendar.SECOND, seconds);
            cal.set(Calendar.MILLISECOND, millis);
            MicroSecondDate adjDate = new MicroSecondDate(cal.getTime());
            return adjDate;
        }

        private DateField startDF, endDF;

        private Calendar cal = Calendar.getInstance(TimeZone.getTimeZone("GMT"));
    }

    private class MagnitudeEditor extends JPanel {

        public MagnitudeEditor() {
            setBorder(new TitledBorder("Magnitude"));
            typeDialog.selectAll();
            GridBagLayout layout = new GridBagLayout();
            GridBagConstraints constraints = new GridBagConstraints();
            setLayout(layout);
            JLabel from = new JLabel("From");
            constraints.gridx = 0;
            constraints.insets = HORIZONTAL_SPACER;
            layout.setConstraints(from, constraints);
            add(from);
            constraints.gridx++;
            constraints.insets = ZERO;
            layout.setConstraints(min, constraints);
            add(min);
            JLabel to = new JLabel("to");
            constraints.gridx++;
            constraints.insets = HORIZONTAL_SPACER;
            layout.setConstraints(to, constraints);
            add(to);
            constraints.gridx++;
            constraints.insets = ZERO;
            layout.setConstraints(max, constraints);
            add(max);
            JLabel in = new JLabel("of");
            constraints.gridx++;
            constraints.insets = HORIZONTAL_SPACER;
            layout.setConstraints(in, constraints);
            add(in);
            JButton typeField = new JButton("Type");
            typeField.addActionListener(new ActionListener() {

                public void actionPerformed(ActionEvent ae) {
                    typeDialog.show();
                }
            });
            constraints.gridx++;
            constraints.insets = ZERO;
            layout.setConstraints(typeField, constraints);
            add(typeField);
        }

        public String[] getTypes() {
            return typeDialog.getSelectedValues();
        }

        public float getMin() {
            return ((Double)min.getValue()).floatValue();
        }

        public float getMax() {
            return ((Double)max.getValue()).floatValue();
        }

        private CheckListBox typeDialog = new CheckListBox(types,
                                                           "Magnitude Types");

        private JSpinner min = new JSpinner(new SpinnerNumberModel(initialMinMag,
                                                                   0.0,
                                                                   10.0,
                                                                   1.0));

        private JSpinner max = new JSpinner(new SpinnerNumberModel(initialMaxMag,
                                                                   0.0,
                                                                   10.0,
                                                                   1.0));
    }

    private class CatalogEditor extends JPanel {

        public CatalogEditor() {
            setLayout(new BoxLayout(this, BoxLayout.X_AXIS));
            add(Box.createHorizontalGlue());
            String[] catalogTypes = new String[0];
            Iterator it = configParams.keySet().iterator();
            while(it.hasNext()) {
                String key = (String)it.next();
                Object next = configParams.get(key);
                if(next instanceof String && key.startsWith("selectedCatalog_")) {
                    String[] tmpStr = new String[catalogTypes.length + 1];
                    System.arraycopy(catalogTypes,
                                     0,
                                     tmpStr,
                                     0,
                                     catalogTypes.length);
                    catalogTypes = tmpStr;
                    catalogTypes[catalogTypes.length - 1] = (String)next;
                }
            }
            catalogs = new CheckListBox(catalogTypes, "Catalog");
            catalogs.selectAll();
            Thread t = new Thread(new CatalogLoader(catalogs),
                                  "CatalogContributorLoader");
            t.start();
            JButton catalogField = new JButton("Catalog");
            catalogField.addActionListener(new ActionListener() {

                public void actionPerformed(ActionEvent ae) {
                    catalogs.show();
                }
            });
            add(catalogField);
            add(Box.createHorizontalGlue());
        }

        public String[] getCatalogs() {
            return catalogs.getSelectedValues();
        }

        private CheckListBox catalogs;
    }

    private TimeEditor timeEditor;

    private BoxAreaDisplay boxArea;

    private DepthEditor depthEditor;

    private MagnitudeEditor magEditor;

    private CatalogEditor catEditor;

    protected JComponent createGUI() {
        Box b = Box.createVerticalBox();
        b.add(timeEditor);
        b.add(boxArea);
        b.add(depthEditor);
        b.add(magEditor);
        b.add(Box.createVerticalStrut(10));
        b.add(catEditor);
        b.add(Box.createVerticalStrut(10));
        final JButton getEventsButton = new JButton("Get Earthquakes");
        getEventsButton.setFont(getEventsButton.getFont().deriveFont(Font.BOLD));
        Box getEventsBox = Box.createHorizontalBox();
        getEventsBox.add(Box.createHorizontalGlue());
        getEventsBox.add(getEventsButton);
        getEventsBox.add(Box.createHorizontalGlue());
        b.add(getEventsBox);
        b.add(Box.createVerticalStrut(10));
        final JLabel statusLabel = new JLabel("Status:");
        Box statusBox = Box.createHorizontalBox();
        statusBox.add(statusLabel);
        statusBox.add(Box.createHorizontalGlue());
        b.add(statusBox);
        getEventsButton.addActionListener(new ActionListener() {

            public void actionPerformed(ActionEvent e) {
                getEventsButton.setEnabled(false);
                getEventsButton.setText("Getting Earthquakes");
                statusLabel.setText("Status: ");
                try {
                    WorkerThreadPool.getDefaultPool()
                            .invokeLater(new Runnable() {

                                public void run() {
                                    try {
                                        EventAccessOperations[] events = queryEvents();
                                        statusLabel.setText("Status: found "
                                                + events.length + " events.");
                                    } catch(NoNetworkException ee) {
                                        ee.showMessage();
                                    } catch(Throwable ee) {
                                        GlobalExceptionHandler.handle("Unable to query for events",
                                                                      ee);
                                    } finally {
                                        getEventsButton.setEnabled(true);
                                        getEventsButton.setText("Get Earthquakes");
                                    }
                                }
                            });
                } catch(Throwable ee) {
                    GlobalExceptionHandler.handle(ee);
                }
            }
        });
        return b;
    }

    public ProxyEventAccessOperations[] queryEvents()
            throws ConfigurationException, NoNetworkException {
        Date startingDate = timeEditor.getStart();
        Date endingDate = timeEditor.getEnd();
        if(startingDate.after(endingDate)) {
            timeConflictError();
        }
        Area area = boxArea.getArea();
        Time startTime = new MicroSecondDate(startingDate).getFissuresTime();
        Time endTime = new MicroSecondDate(endingDate).getFissuresTime();
        TimeRange timeRange = new TimeRange(startTime, endTime);
        QuantityImpl minDepth = depthEditor.getMin();
        QuantityImpl maxDepth = depthEditor.getMax();
        String[] magTypes = new String[0];
        System.err.println("Bad, mag types not from list");
        //String[] magTypes = magEditor.getTypes();
        float minMag = magEditor.getMin();
        float maxMag = magEditor.getMax();
        String[] catalogs = catEditor.getCatalogs();
        String[] contributors = new String[0];
        return queryEvents(area,
                           minDepth,
                           maxDepth,
                           timeRange,
                           magTypes,
                           minMag,
                           maxMag,
                           catalogs,
                           contributors);
    }

    public ProxyEventAccessOperations[] queryEvents(Area area,
                                                    Quantity minDepth,
                                                    Quantity maxDepth,
                                                    TimeRange timeRange,
                                                    String[] magTypes,
                                                    float minMagnitude,
                                                    float maxMagnitude,
                                                    String[] catalogs,
                                                    String[] contributors)
            throws ConfigurationException, NoNetworkException {
        return query.query(area,
                           minDepth,
                           maxDepth,
                           timeRange,
                           magTypes,
                           minMagnitude,
                           maxMagnitude,
                           catalogs,
                           contributors);
    }

    private class EventQuery extends AbstractJob {

        public EventQuery() {
            super("Event Query to " + serverName);
            JobTracker.getTracker().add(this);
            setFinished(true);
        }

        public ProxyEventAccessOperations[] query(Area area,
                                                  Quantity minDepth,
                                                  Quantity maxDepth,
                                                  TimeRange timeRange,
                                                  String[] magTypes,
                                                  float minMagnitude,
                                                  float maxMagnitude,
                                                  String[] catalogs,
                                                  String[] contributors)
                throws ConfigurationException, NoNetworkException {
            try {
                if(NetworkGateKeeper.accessAllowed() && !disabled) {
                    setFinished(false);
                    EventSeqIterHolder iter = new EventSeqIterHolder();
                    setStatus("Getting events");
                    if(disabled) {
                        return new ProxyEventAccessOperations[0];
                    }
                    MicroSecondDate start = new MicroSecondDate(timeRange.start_time);
                    MicroSecondDate end = new MicroSecondDate(timeRange.end_time);
                    MicroSecondDate useStartTime = start;
                    MicroSecondDate useEndTime = useStartTime.add(QUERY_INCREMENT);
                    if(useEndTime.after(end)) {
                        useEndTime = end;
                    }
                    ProxyEventAccessOperations[] eventAccess = new ProxyEventAccessOperations[0];

                    FDSNEventQueryParams qp = new FDSNEventQueryParams();
                    if (area instanceof GlobalArea) {
                        // nothing
                    } else if (area instanceof BoxArea) {
                        BoxArea box = (BoxArea)area;
                        qp.area(box.min_latitude, box.max_latitude,
                                box.min_longitude, box.max_longitude);
                    } else if (area instanceof PointDistanceArea) {
                        PointDistanceArea pd = (PointDistanceArea)area;
                        qp.donut(pd.latitude, pd.longitude, 
                                 (float)((QuantityImpl)pd.min_distance).getValue(UnitImpl.DEGREE),
                                 (float)((QuantityImpl)pd.max_distance).getValue(UnitImpl.DEGREE));
                    }
                    qp.setMinDepth((float)((QuantityImpl)minDepth).getValue(UnitImpl.KILOMETER));
                    qp.setMaxDepth((float)((QuantityImpl)maxDepth).getValue(UnitImpl.KILOMETER));
                    qp.setStartTime(new MicroSecondDate(timeRange.start_time));
                    qp.setEndTime(new MicroSecondDate(timeRange.end_time));
                    qp.setMinMagnitude(minMagnitude);
                    qp.setMaxMagnitude(maxMagnitude);
                    /*
                     * trouble with DMC and catalogs, and mag types, so just don't seed
                    if (magTypes != null && magTypes.length != 0) {
                        qp.setMagnitudeType(magTypes[0]);
                    }
                    if (catalogs != null && catalogs.length != 0) {
                        qp.setCatalog(catalogs[0]);
                    }
                    if (contributors != null && contributors.length != 0) {
                        qp.setContributor(contributors[0]);
                    }
                    */
                    try {
                        logger.info("Events: "+qp.formURI());
                    } catch(URISyntaxException e) {
                        // TODO Auto-generated catch block
                        e.printStackTrace();
                    }
                    FdsnEvent fdsnEvent = new FdsnEvent(qp);
                    fdsnEvent.setLag(new TimeInterval(0, UnitImpl.SECOND));
                    fdsnEvent.setUserAgent("GEE/"+BuildVersion.getVersion());
                    edu.sc.seis.sod.source.event.EventSource source = fdsnEvent;
                    boolean firstLoop = true;
                    while( source.getWaitBeforeNext().getValue() == 0 && source.hasNext()) {
                        
                        CacheEvent[] events = source.next();
                        if(firstLoop) {
                            updateEventDestinations(events);
                            firstLoop = false;
                        } else {
                            appendEventDestinations(events);
                        }
                        ProxyEventAccessOperations[] tmp = new ProxyEventAccessOperations[eventAccess.length
                                + events.length];
                        System.arraycopy(eventAccess,
                                         0,
                                         tmp,
                                         0,
                                         eventAccess.length);
                        System.arraycopy(events,
                                         0,
                                         tmp,
                                         eventAccess.length,
                                         events.length);
                        eventAccess = tmp;
                    }
                    setFinished();
                    return eventAccess;
                }
                setFinished();
                if(disabled) {
                    return new ProxyEventAccessOperations[0];
                }
                throw new NoNetworkException();
            } finally {
                // just in case an exception was thrown, make sure we finish
                setFinished();
            }
        }

        public String toString() {
            return "Query on " + serverDNS + "/" + serverName;
        }

        public void runJob() {}
    }

    public EventAccessOperations[] queryEvents(Quantity minDepth,
                                               Quantity maxDepth,
                                               TimeRange timeRange,
                                               float minMagnitude,
                                               float maxMagnitude)
            throws ConfigurationException, NoNetworkException {
        String[] magTypes = new String[0];
        String[] catalogs = new String[0];
        String[] contributors = new String[0];
        return queryEvents(new GlobalAreaImpl(),
                           minDepth,
                           maxDepth,
                           timeRange,
                           magTypes,
                           minMagnitude,
                           maxMagnitude,
                           catalogs,
                           contributors);
    }

    public void updateEventDestinations(EventAccessOperations[] eventAccess) {
        for(int i = 0; i < eventDestinations.length; i++) {
            eventDestinations[i].updateEvents(eventAccess);
        }
    }

    protected void appendEventDestinations(EventAccessOperations[] eventAccess) {
        if(eventAccess.length != 0) {
            for(int i = 0; i < eventDestinations.length; i++) {
                eventDestinations[i].appendEvents(eventAccess);
            }
        }
    }

    private void populateUnitField(JComboBox comboBox) {
        comboBox.addItem(KILOMETER);
        comboBox.addItem(METER);
    }

    private boolean disabled = false;

    private JComponent panel = null;

    private Map configParams;

    private EventDestination[] eventDestinations;

    private ProxyEventDC eventdc = null;

    public static final TimeInterval QUERY_INCREMENT = new TimeInterval(4,
                                                                        UnitImpl.WEEK);

    String serverDNS;

    String serverName;

    EventQuery query;

    protected boolean guiInitialized = false;

    class CatalogLoader implements Runnable {

        CatalogLoader(CheckListBox catalogDialog) {
            this.catalogDialog = catalogDialog;
        }

        CheckListBox catalogDialog;

        public void run() {
            try {
                if(disabled) {
                    return;
                }
                System.err.println("Bad, catalogs are not dynamic");
                String[] catalogTypes = { "NEIC PDE" };
                for(int i = 0; i < catalogTypes.length; i++) {
                    catalogDialog.addItem(catalogTypes[i]);
                }
            } catch(Exception e) {
                GlobalExceptionHandler.handle("Problem loading catalogs and contributors from Event service",
                                              e);
            }
        }
    }

    private static Logger logger = Logger.getLogger(QueryEventsTask.class);

    private static final Insets HORIZONTAL_SPACER = new Insets(0, 5, 0, 5);

    private static final Insets ZERO = new Insets(0, 0, 0, 0);

    private static final String KILOMETER = "kilometers", METER = "meters";

    private static final String[] types = new String[] {"LG",
                                                        "M",
                                                        "MB",
                                                        "MBL",
                                                        "MD",
                                                        "ML",
                                                        "MS",
                                                        "MsZ",
                                                        "MW"};

    public Date getStartTime() {
        return timeEditor.getStart();
    }

    public Date getEndTime() {
        return timeEditor.getEnd();
    }

    public void timeConflictError() {
        JOptionPane.showMessageDialog(null,
                                      "The time range you selected is invalid! \nPlease make sure that the start time \nis before the end time.",
                                      "GEE",
                                      JOptionPane.WARNING_MESSAGE);
    }

    public void setStartTime(Date d) {
        timeEditor.setStart(d);
    }

    public void setEndTime(Date d) {
        timeEditor.setEnd(d);
    }

    public AbstractHandler getConfigureHandler(String name,
                                               XMLReader parser,
                                               AbstractHandler parent) {
        return new DateParser(parser, parent, name);
    }
}// QueryEventsTask
