package edu.sc.seis.gee.task;

import java.awt.BorderLayout;
import java.io.IOException;
import java.net.MalformedURLException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import java.util.StringTokenizer;
import java.util.regex.Pattern;

import javax.swing.JComponent;
import javax.swing.JPanel;
import javax.swing.ListSelectionModel;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;

import org.apache.log4j.Category;

import edu.iris.Fissures.IfEvent.EventAccessOperations;
import edu.iris.Fissures.IfEvent.NoPreferredOrigin;
import edu.iris.Fissures.IfSeismogramDC.RequestFilter;
import edu.iris.Fissures.network.ChannelIdUtil;
import edu.iris.Fissures.network.ChannelImpl;
import edu.iris.Fissures.seismogramDC.LocalSeismogramImpl;
import edu.sc.seis.fissuresUtil.cache.AbstractJob;
import edu.sc.seis.fissuresUtil.cache.FilterNetworkDC;
import edu.sc.seis.fissuresUtil.cache.JobTracker;
import edu.sc.seis.fissuresUtil.chooser.ChannelChooser;
import edu.sc.seis.fissuresUtil.chooser.ChannelChooserSeisSource;
import edu.sc.seis.fissuresUtil.chooser.ChannelChooserSource;
import edu.sc.seis.fissuresUtil.exceptionHandler.GlobalExceptionHandler;
import edu.sc.seis.fissuresUtil.namingService.FissuresNamingService;
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.ConfigurationException;
import edu.sc.seis.seisFile.fdsnws.FDSNStationQueryParams;
import edu.sc.seis.sod.source.network.FdsnStation;
import edu.sc.seis.sod.source.seismogram.SeismogramSource;

/**
 * ChannelChooserTask.java Created: Thu Feb 14 12:49:00 2002
 * 
 * @author <a href="mailto:">Srinivasa Telukutla </a>
 * @version $Id: ChannelChooserTask.java 15327 2005-11-28 21:35:28Z groves $
 */
public class ChannelChooserTask implements GUITask {

    /**
     * Gets the "More Options" GUI component.
     */
    public JComponent getMoreOptionsGUI() {
        return null;
    }

    /**
     * True if this GUI has a "more options" funtionality. More options appear
     * in a separate panel below the main gui, and can be shown/hidden with a
     * "Show More Options" and "Hide More Options" button that is provided
     * automatically is this return true.
     */
    public boolean hasMoreOptions() {
        return false;
    }

    public void invoke() {}

    /**
     * Gets the GUI for this Task. Used for interacting with the user before and
     * after invoking this Task's action.
     */
    public JComponent getGUI() throws Exception {
        if(!guiInitialized) {
            panel = new JPanel();
            guiInitialized = true;
            panel.setLayout(new BorderLayout());
            panel.add(getChannelChooser());
        }
        return panel;
    }

    public void configure(java.util.Map map) throws ConfigurationException {
        this.params = map;
        if(map.containsKey("showSites")
                && ((String)map.get("showSites")).equalsIgnoreCase(TRUE)) {
            showSites = true;
        } // end of if (map.containsKey("showSites"))
        if(map.containsKey("showNetworks")
                && ((String)map.get("showNetworks")).equalsIgnoreCase(FALSE)) {
            showNetworks = false;
        } // end of if (map.containsKey("showSites"))
        if(map.containsKey("showCodes")
                && ((String)map.get("showCodes")).equalsIgnoreCase(TRUE)) {
            showCodes = true;
        } // end of if (map.containsKey("showSites"))
        if(map.containsKey("eventSelector")) {
            TaskAction taskAction = CommonAccess.getCommonAccess()
                    .getTaskAction((String)map.get("eventSelector"));
            final ShowEventsTask showEventsTask = (ShowEventsTask)taskAction.getTask();
            final ListSelectionModel selectionModel = showEventsTask.getEventSelectionModel();
            selectionModel.addListSelectionListener(new ListSelectionListener() {

                public void valueChanged(ListSelectionEvent e) {
                    EventAccessOperations[] selectedEvents = showEventsTask.getSelectedEvents();
                    if(selectedEvents.length > 0) {
                        try {
                            // FIXME: make this work for more than one event
                            getChannelChooser().setAvailbleDataOrigin(selectedEvents[0].get_preferred_origin());
                        } catch(NoPreferredOrigin ex) {
                            // oh well
                        } catch(NoNetworkException ex) {
                            // probably can't happen as the event is over the
                            // net, but...
                            GlobalExceptionHandler.handle(ex);
                        }
                    }
                }
            });
        }
        if(map.containsKey("filterStations")) {
            String filterURL = (String)map.get("filterStations");
            try {
                patterns = FilterNetworkDC.readPattern(filterURL);
            } catch(MalformedURLException e) {
                throw new ConfigurationException("The url for the file with a list of stations to filter, "
                                                         + filterURL
                                                         + ", is malformed",
                                                 e);
            } catch(IOException e) {
                GlobalExceptionHandler.handle("Trouble reading list of patterns",
                                              e);
            }
        }
        if(map.containsKey("orientation")) {
            String orient = (String)map.get("orientation");
            if(orient.equals("INDIVIDUAL_CHANNELS")) {
                orientation = ChannelChooser.INDIVIDUAL_CHANNELS;
            } else if(orient.equals("THREE_COMPONENT")) {
                orientation = ChannelChooser.THREE_COMPONENT;
            } else if(orient.equals("VERTICAL_ONLY")) {
                orientation = ChannelChooser.VERTICAL_ONLY;
            } else if(orient.equals("HORIZONTAL_ONLY")) {
                orientation = ChannelChooser.HORIZONTAL_ONLY;
            } else {
                throw new ConfigurationException("That orientation string isn't supported.  As of now, INDIVIDUAL_CHANNELS is the only one.  Add more in ChannelChooserTask if you need it.");
            }
        }
        if(map.containsKey("bands")) {
            List<String> tmpbands = new ArrayList<String>();
            StringTokenizer tokenizer = new StringTokenizer((String)map.get("bands"),
                                                            ",");
            while(tokenizer.hasMoreTokens()) {
                tmpbands.add(tokenizer.nextToken());
            }
            bands = tmpbands.toArray(new String[0]);
            autoSelectBand = (String[])tmpbands.toArray(new String[0]);
        }
        if (map.containsKey("loadSeismogramTask")) {
            TaskAction taskAction = CommonAccess.getCommonAccess()
                    .getTaskAction((String)map.get("loadSeismogramTask"));
            loadSeis = (LoadSeismogram)taskAction.getTask();
        }
        // start initializing channel chooser in background
        initializeChannelChooser();
    }

    private Pattern[] patterns = new Pattern[0];

    public void destroy() {}

    protected List<ChannelChooserSource> getConfiguredNetworkSources()
            throws ConfigurationException {
        Set keySet = params.keySet();
        Iterator iter = keySet.iterator();
        ArrayList<ChannelChooserSource> list = new ArrayList<ChannelChooserSource>();
        while(iter.hasNext()) {
            String key = (String)iter.next();
            if (key.startsWith(SERVER_HOST)) {
                String host = (String)params.get(key);
                if (host == null || host.length() == 0) {
                    throw new ConfigurationException("Bad configuration for "
                            + key);
                }
                FDSNStationQueryParams queryParams = new FDSNStationQueryParams();
                queryParams.setHost(host);
                for (String band : bands) {
                    queryParams.appendToChannel(band+"H?");
                }
               // queryParams.setFdsnwsPath("fdsnwsbeta");
                FdsnStation fdsnStation = new FdsnStation(key, -1, queryParams);
                list.add(new FDSNChannelChooserSource(fdsnStation));
            }
        }
        return list;
    }

    public String[] getConfiguredNetworks() {
        Set keySet = params.keySet();
        Iterator iter = keySet.iterator();
        ArrayList arrayList = new ArrayList();
        while(iter.hasNext()) {
            String key = (String)iter.next();
            if(key.startsWith("network")) {
                String value = (String)params.get(key);
                arrayList.add(value);
            }
        }
        String[] rtnValues = new String[arrayList.size()];
        rtnValues = (String[])arrayList.toArray(rtnValues);
        return rtnValues;
    }

    /**
     * This acts a a cubby hole, allowing the channel chooser initializer thread
     * to safely create the channel chooser in a separate thread, and then give
     * to the caller with a notify.
     */
    public ChannelChooser getChannelChooser() throws NoNetworkException {
        if(!NetworkGateKeeper.accessAllowed()) {
            throw new NoNetworkException();
        }
        if(channelChooserCubbyHole == null) {
            try {
                initializeChannelChooser();
            } catch(ConfigurationException e) {
                GlobalExceptionHandler.handle("Problem initializing the channel chooser",
                                              e);
            }
        }
        // this get() will block until the channel chooser is initialized
        return channelChooserCubbyHole.get();
    }

    protected void initializeChannelChooser() throws ConfigurationException {
        if(channelChooserCubbyHole == null) {
            channelChooserCubbyHole = new ChannelChooserCubbyHole();
            ChannelChooserInitializer cci = new ChannelChooserInitializer();
            JobTracker.getTracker().add(cci);
            if(processDisplayAction != null) {
                processDisplayAction.checkDisplayLocation();
            }
            cci.run();
        }
    }

    private class ChannelChooserCubbyHole {

        private ChannelChooser contents;

        private boolean available = false;

        public synchronized ChannelChooser get() {
            if(available == false) {
                logger.debug("Waiting to get channel chooser from CubbyHole");
            }
            while(available == false) {
                try {
                    wait();
                } catch(InterruptedException e) {}
            }
            notifyAll();
            return contents;
        }

        public synchronized void put(ChannelChooser value) {
            while(available == true) {
                try {
                    wait();
                } catch(InterruptedException e) {}
            }
            contents = value;
            available = true;
            notifyAll();
        }
    }

    private String[] autoSelectBand = {"B", "H"};
    
    private static final String[] DEFAULT_BAND = {"B", "H", "L", "S", "E"};
    
    String[] bands = DEFAULT_BAND;

    private int orientation = ChannelChooser.BEST_CHANNELS;

    private class ChannelChooserInitializer extends AbstractJob {

        public ChannelChooserInitializer() {
            super("Channel Chooser");
        }

        public void runJob() {
            if(!NetworkGateKeeper.accessAllowed()) {
                setFinished();
                return;
            }
            List<ChannelChooserSource> netDCs = new ArrayList<ChannelChooserSource>();
            try {
                setStatus("Getting Networks");
                netDCs = getConfiguredNetworkSources();
            } catch(ConfigurationException e) {
                GlobalExceptionHandler.handle(e);
            }
            setStatus("Creating chooser");
            ChannelChooser myChannelChooser = new ChannelChooser(netDCs,
                                                                 showSites,
                                                                 showNetworks,
                                                                 getConfiguredNetworks(),
                                                                 bands,
                                                                 autoSelectBand,
                                                                 ChannelChooser.DEFAULT_SELECTABLE_ORIENTATIONS,
                                                                 orientation);
            if(showCodes) {
                myChannelChooser.setShowCodes(showCodes);
            }
            myChannelChooser.setSeismogramDC(new ChannelChooserSeisSource() {
                
                @Override
                public List<LocalSeismogramImpl> request(List<RequestFilter> request) {
                    try {
                        SeismogramSource source = loadSeis.getDataCenterRouter().getSeismogramSource(null, null, request.toArray(new RequestFilter[0]), null);
                        return source.retrieveData(request);
                    } catch(ConfigurationException e) {
                        throw new RuntimeException(e);
                    } catch(Exception e) {
                        throw new RuntimeException(e);
                    }
                }
                
                @Override
                public List<RequestFilter> availableData(List<RequestFilter> request) {
                    try {
                        SeismogramSource source = loadSeis.getDataCenterRouter().getSeismogramSource(null, null, request.toArray(new RequestFilter[0]), null);
                        return source.availableData(request);
                    } catch(ConfigurationException e) {
                        throw new RuntimeException(e);
                    } catch(Exception e) {
                        throw new RuntimeException(e);
                    }
                }
            });
            myChannelChooser.selectAllNetworks();
            channelChooserCubbyHole.put(myChannelChooser);
            setFinished();
        }
    }

    java.util.Map params = new HashMap();

    JPanel panel;

    boolean showSites = false;

    boolean showNetworks = true;

    boolean showCodes = false;

    protected boolean guiInitialized = false;

    private ChannelChooserCubbyHole channelChooserCubbyHole = null;
    
    private LoadSeismogram loadSeis = null;

    static Category logger = Category.getInstance(ChannelChooserTask.class.getName());

    private TaskAction processDisplayAction;
    
    public static final String SERVER_HOST = "host";
    
}// ChannelChooserTask
