package edu.sc.seis.gee.task;

import java.awt.FlowLayout;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.sql.SQLException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.TimeZone;

import javax.swing.JButton;
import javax.swing.JComponent;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPanel;

import org.apache.log4j.Category;

import edu.iris.Fissures.AuditInfo;
import edu.iris.Fissures.FissuresException;
import edu.iris.Fissures.Time;
import edu.iris.Fissures.IfNetwork.Channel;
import edu.iris.Fissures.IfNetwork.ChannelId;
import edu.iris.Fissures.IfSeismogramDC.DataCenterCallBack;
import edu.iris.Fissures.IfSeismogramDC.DataCenterOperations;
import edu.iris.Fissures.IfSeismogramDC.LocalSeismogram;
import edu.iris.Fissures.IfSeismogramDC.RequestFilter;
import edu.iris.Fissures.model.MicroSecondDate;
import edu.iris.Fissures.model.TimeInterval;
import edu.iris.Fissures.model.UnitImpl;
import edu.iris.Fissures.network.ChannelImpl;
import edu.iris.Fissures.network.NetworkIdUtil;
import edu.iris.Fissures.seismogramDC.LocalSeismogramImpl;
import edu.sc.seis.fissuresUtil.cache.InstrumentationLoader;
import edu.sc.seis.fissuresUtil.chooser.ChannelChooser;
import edu.sc.seis.fissuresUtil.chooser.ChannelChooserSource;
import edu.sc.seis.fissuresUtil.chooser.ClockUtil;
import edu.sc.seis.fissuresUtil.chooser.CoarseAvailableData;
import edu.sc.seis.fissuresUtil.chooser.DateChooser;
import edu.sc.seis.fissuresUtil.chooser.DateChooserOptions;
import edu.sc.seis.fissuresUtil.chooser.IntervalChooser;
import edu.sc.seis.fissuresUtil.chooser.IntervalChooserOptions;
import edu.sc.seis.fissuresUtil.database.DBDataCenter;
import edu.sc.seis.fissuresUtil.dataset.Organizer;
import edu.sc.seis.fissuresUtil.exceptionHandler.GlobalExceptionHandler;
import edu.sc.seis.fissuresUtil.xml.DataSetSeismogram;
import edu.sc.seis.fissuresUtil.xml.FDSNWSDataSetSeismogram;
import edu.sc.seis.gee.BuildVersion;
import edu.sc.seis.gee.CommonAccess;
import edu.sc.seis.gee.NetworkGateKeeper;
import edu.sc.seis.gee.NoNetworkException;
import edu.sc.seis.gee.configurator.ConfigurationException;
import edu.sc.seis.seisFile.fdsnws.FDSNDataSelectQueryParams;
import edu.sc.seis.sod.source.seismogram.ChoiceSource;
import edu.sc.seis.sod.source.seismogram.ChoiceSourceItem;
import edu.sc.seis.sod.source.seismogram.FdsnDataSelect;
import edu.sc.seis.sod.source.seismogram.SeismogramSource;
import edu.sc.seis.sod.source.seismogram.SeismogramSourceLocator;

/**
 * LoadSeismogram.java Created: Wed Jan 2 08:14:03 2002
 * 
 * @author <a href="mailto:">Srinivasa Telukutla </a>
 * @version
 */
public class LoadSeismogram implements GUITask {

    public LoadSeismogram() {
        dateFormat.setTimeZone(TimeZone.getTimeZone("GMT"));
        instLoader = new InstrumentationLoader();
        instLoader.start();
    }

    public void invoke() throws Exception {}

    public JComponent getMoreOptionsGUI() {
        return null;
    }

    public boolean hasMoreOptions() {
        return false;
    }

    public JComponent getGUI() {
        if (!guiInitialized) {
            guiInitialized = true;
            createGUI();
        } // end of if (! guiInitialized)
        return panel;
    }

    public void destroy() {}

    /***************************************************************************
     * This function is used to display the GUI used for the task LoadSeismogram
     * of the DataExplorer. The GUI contains a DateChooser(StartDate),
     * IntervalChooser, and PushButtons ok,Close and ChooseChannels.
     **************************************************************************/
    public void createGUI() {
        panel = new JPanel();
        IntervalChooserOptions[] options = new IntervalChooserOptions[6];
        options[0] = IntervalChooserOptions.SECOND;
        options[1] = IntervalChooserOptions.MINUTE;
        options[2] = IntervalChooserOptions.HOUR;
        options[3] = IntervalChooserOptions.DAY;
        options[4] = IntervalChooserOptions.MONTH;
        options[5] = IntervalChooserOptions.YEAR;
        final IntervalChooser intervalChooser = new IntervalChooser(options);
        intervalChooser.setSelectedValue(10);
        DateChooserOptions[] dateformat = new DateChooserOptions[6];
        dateformat[0] = DateChooserOptions.YEAR;
        dateformat[1] = DateChooserOptions.MONTH;
        dateformat[2] = DateChooserOptions.DAY;
        dateformat[3] = DateChooserOptions.HOUR;
        dateformat[4] = DateChooserOptions.MINUTES;
        dateformat[5] = DateChooserOptions.SECONDS;
        final DateChooser startDate = new DateChooser(dateformat);
        MicroSecondDate end = ClockUtil.now();
        MicroSecondDate begin = end.subtract(new TimeInterval(10, UnitImpl.MINUTE));
        startDate.setDate(begin);
        JPanel endPanel = new JPanel();
        final JLabel endDateLabel = new JLabel("End Date");
        JLabel intervalLabel = new JLabel("Interval");
        JLabel startDateLabel = new JLabel("Start Date");
        JButton okButton = new JButton("OK");
        final GridBagLayout bagLayout = new GridBagLayout();
        final GridBagConstraints constraints = new GridBagConstraints();
        endPanel.setLayout(new FlowLayout());
        endPanel.add(okButton);
        panel.setLayout(bagLayout);
        constraints.weightx = 1.0;
        constraints.weighty = 1.0;
        constraints.gridx = 0;
        constraints.gridy = 0;
        constraints.fill = GridBagConstraints.HORIZONTAL;
        // /adding start Date Label to the panel
        bagLayout.setConstraints(startDateLabel, constraints);
        panel.add(startDateLabel);
        constraints.weightx = 0;
        constraints.gridy = constraints.gridy + 1;
        bagLayout.setConstraints(startDate, constraints);
        panel.add(startDate);
        constraints.gridheight = 1;
        constraints.gridy = 2;
        constraints.gridx = 0;
        bagLayout.setConstraints(intervalLabel, constraints);
        panel.add(intervalLabel);
        constraints.gridx = 0;
        constraints.gridheight = 1;
        constraints.weightx = 1;
        constraints.gridy = constraints.gridy + 1;
        bagLayout.setConstraints(intervalChooser, constraints);
        panel.add(intervalChooser);
        constraints.gridx = 0;
        constraints.weighty = 1;
        constraints.gridy = constraints.gridy + 1;
        bagLayout.setConstraints(endDateLabel, constraints);
        panel.add(endDateLabel);
        constraints.gridy = constraints.gridy + 1;;
        bagLayout.setConstraints(endPanel, constraints);
        panel.add(endPanel);
        okButton.addActionListener(new ActionListener() {

            public void actionPerformed(ActionEvent e) {
                try {
                    java.util.Date intervalDate = intervalChooser.addTo(startDate.getDate());
                    endDateLabel.setText("End Date:" + dateFormat.format(intervalDate));
                    MicroSecondDate when = new MicroSecondDate(startDate.getDate());
                    Time startTime = when.getFissuresTime();
                    Time endTime = new MicroSecondDate(intervalDate).getFissuresTime();
                    Channel[] channels = channelChooserTask.getChannelChooser().getSelectedChannels(when);
                    ChannelId[] chanIds = new ChannelId[channels.length];
                    for (int i = 0; i < channels.length; i++) {
                        logger.debug(channels[i].get_id().station_code + "="
                                + channels[i].getSite().getStation().getName());
                        chanIds[i] = channels[i].get_id();
                    } // end of for ()
                      // make sure channel info is there before seis
                    populateLocalDataSet(channels);
                    loadAndStore(startTime, endTime, chanIds);
                } catch(Exception fe) {
                    GlobalExceptionHandler.handle("Exception while Getting the Seismograms", fe);
                }
            }
        });
    }

    /**
     * Loads seismograms for the selected channels and times from the
     * seismogramDC server and stores them in a dataset using the current
     * DataSetOrganizer.
     */
    protected DataSetSeismogram[] loadAndStore(Time startTime, Time endTime, ChannelId[] channelId)
            throws ConfigurationException, NoNetworkException {
        DataSetSeismogram[] seis = new DataSetSeismogram[0];
        boolean tryAgain = true;
        while (tryAgain) {
            try {
                seis = retrieveSeismograms(startTime, endTime, channelId);
                populateLocalDataSet(seis);
                tryAgain = false;
            } catch(org.omg.CORBA.SystemException corbaE) {
                int n = JOptionPane.showConfirmDialog(panel,
                                                      "A problem has occured in connecting to the server."
                                                              + " Would you like to try again?",
                                                      "Network Error",
                                                      JOptionPane.OK_CANCEL_OPTION,
                                                      JOptionPane.ERROR_MESSAGE);
                if (n == JOptionPane.CANCEL_OPTION) {
                    tryAgain = false;
                    seis = new DataSetSeismogram[0];
                } else {
                    tryAgain = true;
                    dataCenter = null;
                } // end of else
            } // end of try-catch
        } // end of while (localSeismograms == null;)
          // populateLocalDataSet(seis);//populate the localdataset with the
          // seismograms retrieved.
        messageStr = new String("Retrieved " + seis.length + " Seismograms");
        JOptionPane.showMessageDialog(null, messageStr, "Seismogram Information", JOptionPane.INFORMATION_MESSAGE);
        return seis;
    }

    /**
     * This function retrieves Seismogram for the given startTime, endTime and
     * ChannelId
     */
    public DataSetSeismogram retrieveSeismogram(RequestFilter request) throws ConfigurationException,
            NoNetworkException {
        RequestFilter[] inRequest = new RequestFilter[] {request};
        FDSNWSDataSetSeismogram seis = new FDSNWSDataSetSeismogram(request);
        seis.setUserAgent("GEE/" + BuildVersion.getVersion());
        ChannelChooser channelChooser = channelChooserTask.getChannelChooser();
        // only try to load instrumentation if network is already known
        if (channelChooser.isSourceKnown(inRequest[0].channel_id.network_id)) {
            instLoader.getInstrumentation(seis, channelChooser);
        } else {
            logger.warn("networkAccess for " + NetworkIdUtil.toString(inRequest[0].channel_id.network_id)
                    + " is not loaded, skipping instrumentation");
        }
        return seis;
    }

    public DataSetSeismogram retrieveSeismogram(Time startTime, Time endTime, ChannelId channelId)
            throws ConfigurationException, NoNetworkException {
        return retrieveSeismogram(new RequestFilter(channelId, startTime, endTime));
    }

    public DataSetSeismogram[] retrieveSeismograms(Time startTime, Time endTime, ChannelId[] channelId)
            throws ConfigurationException, NoNetworkException {
        LinkedList seismos = new LinkedList();
        for (int i = 0; i < channelId.length; i++) {
            DataSetSeismogram seis = retrieveSeismogram(new RequestFilter(channelId[i], startTime, endTime));
            if (seis != null) {
                seismos.add(seis);
            } // end of if ()
        } // end of for (int i=0; i<channelId.length; i++)
        DataSetSeismogram[] seismoArray = new DataSetSeismogram[0];
        return (DataSetSeismogram[])seismos.toArray(seismoArray);
    }

    /**
     * Adds seismograms vie the organizer. No channels are added.
     */
    public void populateLocalDataSet(DataSetSeismogram[] seismograms) {
        Organizer organizer = CommonAccess.getCommonAccess().getOrganizer();
        for (int i = 0; i < seismograms.length; i++) {
            if (seismograms[i] != null) {
                AuditInfo[] audit_info = {new AuditInfo(System.getProperty("user.name"), "loaded seismogram "
                        + seismograms[i].getName())};
                organizer.addSeismogram(seismograms[i], audit_info);
            } else {
                logger.warn("Received a null seismogram");
            } // end of else
        } // end of for (int i=0; i<localSeismograms.length; i++)
    }

    public void populateLocalDataSet(Channel[] channels) {
        Organizer organizer = CommonAccess.getCommonAccess().getOrganizer();
        AuditInfo[] audit = new AuditInfo[1];
        audit[0] = new AuditInfo(System.getProperty("user.name"), "loaded channel.");
        for (int counter = 0; counter < channels.length; counter++) {
            organizer.addChannel(channels[counter], audit);
        }
    }

    public void configure(java.util.Map params) throws ConfigurationException {
        configParams = params;
        String chanChooserId = (String)params.get("channelChooserTask");
        TaskAction chanChooserAction = CommonAccess.getCommonAccess().getTaskAction(chanChooserId);
        channelChooserTask = (ChannelChooserTask)chanChooserAction.getTask();
    }

    void initializeDCRouter() throws ConfigurationException {
        if (!NetworkGateKeeper.accessAllowed()) {
            logger.info("No internet");
            return;
        }
        CommonAccess commonAccess = CommonAccess.getCommonAccess();
        CoarseAvailableData coarseAvailableData = null;
        //
        // bad, we only do IRIS as seismogram source???
        //
        String host = FDSNDataSelectQueryParams.IRIS_HOST;
        try {
        List<ChannelChooserSource> netdcList = channelChooserTask.getChannelChooser().getNetworkDCs();
        for (ChannelChooserSource netSource : netdcList) {
            if (netSource instanceof FDSNChannelChooserSource) {
                FDSNChannelChooserSource fdsnStation = (FDSNChannelChooserSource)netSource;
                if (fdsnStation.getFdsnStation().getDefaultQueryParams().getHost().equals(host)) {
                    coarseAvailableData = fdsnStation.getFdsnStation().getAvailableData();
                }
            }
        }
        } catch (NoNetworkException e) {
            logger.info("No network, so no available data");
        }
        dataCenterRouter = new ChoiceSource(new ArrayList<ChoiceSourceItem>(),
                                            new FdsnDataSelect(host, 80, coarseAvailableData));
        // new HardCodeDataCenterRouter(fissuresNamingService);
        try {
            dataCenter = DBDataCenter.getDataCenter(commonAccess.getCacheDirectory(),
                                                    "GEE_database",
                                                    new DataCenterOperations() {

                                                        @Override
                                                        public RequestFilter[] available_data(RequestFilter[] request) {
                                                            try {
                                                                SeismogramSource s = dataCenterRouter.getSeismogramSource(null,
                                                                                                                          (ChannelImpl)channelChooserTask.getChannelChooser()
                                                                                                                                  .getChannel(request[0].channel_id),
                                                                                                                          request,
                                                                                                                          null);
                                                                return s.availableData(Arrays.asList(request))
                                                                        .toArray(new RequestFilter[0]);
                                                            } catch(NoNetworkException e) {
                                                                throw new RuntimeException(e);
                                                            } catch(Exception e) {
                                                                throw new RuntimeException(e);
                                                            }
                                                        }

                                                        @Override
                                                        public LocalSeismogram[] retrieve_seismograms(RequestFilter[] request)
                                                                throws FissuresException {
                                                            try {
                                                                SeismogramSource s = dataCenterRouter.getSeismogramSource(null,
                                                                                                                          (ChannelImpl)channelChooserTask.getChannelChooser()
                                                                                                                                  .getChannel(request[0].channel_id),
                                                                                                                          request,
                                                                                                                          null);
                                                                return s.retrieveData(Arrays.asList(request))
                                                                        .toArray(new LocalSeismogramImpl[0]);
                                                            } catch(NoNetworkException e) {
                                                                throw new RuntimeException(e);
                                                            } catch(Exception e) {
                                                                throw new RuntimeException(e);
                                                            }
                                                        }

                                                        @Override
                                                        public String request_seismograms(RequestFilter[] a_filterseq,
                                                                                          DataCenterCallBack a_client,
                                                                                          boolean long_lived,
                                                                                          Time expiration_time)
                                                                throws FissuresException {
                                                            throw new RuntimeException("No implement");
                                                        }

                                                        @Override
                                                        public String queue_seismograms(RequestFilter[] a_filterseq)
                                                                throws FissuresException {
                                                            throw new RuntimeException("No implement");
                                                        }

                                                        @Override
                                                        public LocalSeismogram[] retrieve_queue(String a_request)
                                                                throws FissuresException {
                                                            throw new RuntimeException("No implement");
                                                        }

                                                        @Override
                                                        public void cancel_request(String a_request)
                                                                throws FissuresException {
                                                            throw new RuntimeException("No implement");
                                                        }

                                                        @Override
                                                        public String request_status(String a_request)
                                                                throws FissuresException {
                                                            throw new RuntimeException("No implement");
                                                        }
                                                    });
            // dataCenterRouter);
        } catch(SQLException e) {
            throw new ConfigurationException("Database problem.", e);
        } // end of try-catch
    }

    public SeismogramSourceLocator getDataCenterRouter() throws ConfigurationException {
        if (dataCenterRouter == null) {
            initializeDCRouter();
        } // end of if (dataCenter == null)
        return dataCenterRouter;
    }

    java.text.SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy MMM dd HH:mm:ss z");

    protected boolean guiInitialized = false;

    protected JPanel panel = null;

    protected SeismogramSourceLocator dataCenterRouter;

    protected DataCenterOperations dataCenter;

    protected HashMap nameToDataCenterMap = new HashMap();

    private Map configParams;

    private ChannelChooserTask channelChooserTask;

    private String messageStr = new String();

    private InstrumentationLoader instLoader;

    static Category logger = Category.getInstance(LoadSeismogram.class.getName());
}// LoadSeismogram
