/*
 * To change this license header, choose License Headers in Project Properties.
 * To change this template file, choose Tools | Templates
 * and open the template in the editor.
 */

package org.autoplot.pdsppi;

import java.awt.Window;
import java.io.IOException;
import java.net.URISyntaxException;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.logging.ConsoleHandler;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.swing.DefaultComboBoxModel;
import javax.swing.DefaultListModel;
import javax.swing.ImageIcon;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTree;
import javax.swing.ListModel;
import javax.swing.SwingUtilities;
import javax.swing.tree.TreeModel;
import javax.swing.tree.TreePath;
import org.das2.util.LoggerManager;
import org.das2.util.filesystem.FSTreeModel;
import org.das2.util.filesystem.FileSystem;
import org.das2.util.monitor.NullProgressMonitor;
import org.das2.util.monitor.ProgressMonitor;
import org.autoplot.datasource.DataSourceEditorPanel;
import org.autoplot.datasource.URISplit;

/**
 * Editor panel for getting data from PDS/PPI node.
 * @author jbf
 */
public class PDSPPIDataSourceEditorPanel extends javax.swing.JPanel implements DataSourceEditorPanel {

    private static final Logger logger= LoggerManager.getLogger("apdss.pdsppi");
    
    /**
     * Creates new form PPARAMPPIDataSourceEditorPanel
     */
    public PDSPPIDataSourceEditorPanel() {
        initComponents();
        if ( FileSystem.settings().isOffline() ) {
            JOptionPane.showMessageDialog(this,"internet is not available");
            this.setEnabled(false);
            return;
        }
        updateSpacecraftSoon();
    }

    /**
     * This method is called from within the constructor to initialize the form.
     * WARNING: Do NOT modify this code. The content of this method is always
     * regenerated by the Form Editor.
     */
    @SuppressWarnings("unchecked")
    // <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
    private void initComponents() {

        productTextField = new javax.swing.JTextField();
        jLabel1 = new javax.swing.JLabel();
        inventoryScComboBox = new javax.swing.JComboBox();
        datasetComboBox = new javax.swing.JComboBox();
        pickProductButton = new javax.swing.JButton();
        jScrollPane1 = new javax.swing.JScrollPane();
        paramList = new javax.swing.JList();
        jLabel2 = new javax.swing.JLabel();
        jLabel3 = new javax.swing.JLabel();
        jLabel4 = new javax.swing.JLabel();

        productTextField.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                productTextFieldActionPerformed(evt);
            }
        });

        jLabel1.setText("Spacecraft:");

        inventoryScComboBox.setModel(new javax.swing.DefaultComboBoxModel(new String[] { "voyager", "galileo", "cassini" }));
        inventoryScComboBox.addItemListener(new java.awt.event.ItemListener() {
            public void itemStateChanged(java.awt.event.ItemEvent evt) {
                inventoryScComboBoxItemStateChanged(evt);
            }
        });

        datasetComboBox.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                datasetComboBoxActionPerformed(evt);
            }
        });

        pickProductButton.setText("Pick...");
        pickProductButton.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                pickProductButtonActionPerformed(evt);
            }
        });

        jScrollPane1.setViewportView(paramList);

        jLabel2.setText("Dataset:");

        jLabel3.setText("Product:");

        jLabel4.setText("Parameters:");

        javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this);
        this.setLayout(layout);
        layout.setHorizontalGroup(
            layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(layout.createSequentialGroup()
                .addContainerGap()
                .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                    .addComponent(jScrollPane1, javax.swing.GroupLayout.Alignment.TRAILING)
                    .addGroup(layout.createSequentialGroup()
                        .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                            .addComponent(jLabel2)
                            .addComponent(jLabel3))
                        .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                        .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                            .addGroup(layout.createSequentialGroup()
                                .addComponent(productTextField)
                                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                                .addComponent(pickProductButton))
                            .addComponent(datasetComboBox, 0, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)))
                    .addGroup(layout.createSequentialGroup()
                        .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                            .addComponent(jLabel4)
                            .addGroup(layout.createSequentialGroup()
                                .addComponent(jLabel1)
                                .addGap(18, 18, 18)
                                .addComponent(inventoryScComboBox, javax.swing.GroupLayout.PREFERRED_SIZE, 192, javax.swing.GroupLayout.PREFERRED_SIZE)))
                        .addGap(0, 197, Short.MAX_VALUE)))
                .addContainerGap())
        );
        layout.setVerticalGroup(
            layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(layout.createSequentialGroup()
                .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
                    .addComponent(jLabel1)
                    .addComponent(inventoryScComboBox, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
                    .addComponent(datasetComboBox, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
                    .addComponent(jLabel2))
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
                    .addComponent(productTextField, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
                    .addComponent(pickProductButton)
                    .addComponent(jLabel3))
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                .addComponent(jLabel4)
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                .addComponent(jScrollPane1, javax.swing.GroupLayout.DEFAULT_SIZE, 182, Short.MAX_VALUE)
                .addContainerGap())
        );
    }// </editor-fold>//GEN-END:initComponents

    /**
     * apparently the id needs to have underscores where there are slashes...  e.g.
     * PPI/CO-E/J/S/SW-CAPS-5-DDR-ELE-MOMENTS-V1.0 -> PPI/CO-E_J_S_SW-CAPS-5-DDR-ELE-MOMENTS-V1.0
     * @param root like PPI/CO-E/J/S/SW-CAPS-5-DDR-ELE-MOMENTS-V1.0/
     * @return result like PPI/CO-E_J_S_SW-CAPS-5-DDR-ELE-MOMENTS-V1.0
     */
    private String removeExtraSlashes( String root ) {
        int i= root.indexOf("/"); // 4 for PPI/
        i++;
        return root.substring(0,i) + root.substring(i).replaceAll("/","_");
    }
    
    /**
     * return the current dataset.
     * @return 
     */
    private String getCurrentRoot() {
        return removeExtraSlashes( datasetComboBox.getSelectedItem().toString() );
    }
    
    /** 
     * there's a little problem that we can't set this on the event thread, because it 
     * involves listing files.
     * @param tree
     * @param m
     * @param s 
     */
    private void setSelectedProduct( JTree tree, TreeModel m, String s ) { 
        String[] ss= s.split("/",-2);
        Object parent= m.getRoot();
        TreePath p= new TreePath(parent);
        for ( String s1 : ss ) {
            s1= s1+"/";
            int idx= -1;
            for ( int i=0; i<m.getChildCount(parent); i++ ) {
                String test= m.getChild( parent, i).toString();
                if ( test.equals(s1) ) {
                    idx= i;
                    parent= m.getChild(parent,idx);
                    break;
                }
            }
            if ( idx!=-1 ) {
                parent= m.getChild(parent,idx);
                p= p.pathByAddingChild( parent );
            } else {
            }
        }
        tree.setSelectionPath(p);
    }
    
    private void pickProductButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_pickProductButtonActionPerformed
        try {
            String l_id;
            String root= getCurrentRoot();
            FileSystem fs= new PDSPPIFileSystem( root );
            //String s= idTextField.getText( );
            javax.swing.JTree tree= new javax.swing.JTree( new FSTreeModel(fs) );   
            //setSelectedProduct( tree, tree.getModel(), s );
            ImageIcon icon= new ImageIcon( PDSPPIDataSourceEditorPanel.class.getResource("/resources/ppi_home_16_crop.gif") );
            if ( JOptionPane.OK_OPTION==JOptionPane.showConfirmDialog( datasetComboBox, 
                    new JScrollPane(tree), 
                    "Pick Dataset ID",
                    JOptionPane.OK_CANCEL_OPTION, 
                    JOptionPane.QUESTION_MESSAGE,
                    icon ) ) {
                TreePath p= tree.getSelectionPath();
                if ( p!=null ) {
                    Object[] o= p.getPath();
                    StringBuilder dsb= new StringBuilder();
                    for ( int i=1; i<o.length; i++ ) {
                        dsb.append(o[i].toString());
                    }
                    String ds= dsb.toString();

                    if ( PDSPPIDB.isPlottable(ds) ) {
                        int dotpos= ds.lastIndexOf("."); // pop off the extension
                        ds= ds.substring(0,dotpos);
                    }
                    productTextField.setText( ds );
                    l_id= removeExtraSlashes( this.datasetComboBox.getModel().getSelectedItem().toString() ) + "/" + productTextField.getText();
                    updateParamsSoon(l_id);
                }
            }
        } catch (URISyntaxException ex) {
            Logger.getLogger(PDSPPIDataSourceEditorPanel.class.getName()).log(Level.SEVERE, null, ex);
        }
    }//GEN-LAST:event_pickProductButtonActionPerformed

    /**
     * update the paramComboBox later on the event thread.
     * @param dss 
     */
    private void updateParamComboBoxSoon( final Map<String,String> dss ) {
        Runnable run= new Runnable() {
            @Override
            public void run() {
                String[] ss= new String[ dss.size() ];
                int i= 0;
                for ( Entry<String,String> e: dss.entrySet() ) {
                    ss[i]= e.getKey()+": "+ e.getValue();
                    i=i+1;
                }
                
                if ( ss.length==0 ) {
                    paramList.setModel( getMessageModel("(No plottable parameters)") );
                    return;
                }
                
                String lparam= param;
                if ( lparam!=null ) lparam= lparam.replaceAll("\\+"," ");
                
                String selectedParam= null;
                DefaultListModel lm= new DefaultListModel();
                for ( String s: ss ) {
                    lm.addElement(s);
                    int j= s.indexOf(":");
                    if ( j==-1 ) j= s.length();
                    if ( s.substring(0,j).equals(lparam) ) {
                        selectedParam= s;
                    }
                }
                paramList.setModel( lm );
                
                if ( selectedParam==null && lparam!=null && lparam.matches("col\\d+") ) {
                    paramList.setSelectedIndex( Integer.parseInt( lparam.substring(3) )-1 );
                } else if ( selectedParam!=null ) {
                    paramList.setSelectedValue( selectedParam, true );
                }
            }
        };
        SwingUtilities.invokeLater(run);
    }
    
    ListModel getMessageModel( final String message ) {
        return new DefaultListModel() {

            @Override
            public Object getElementAt(int index) {
                return message;
            }

            @Override
            public int getSize() {
                return 1;
            }
            
        };
    };
    
    /**
     * given the new ID, update the available params.
     * @param id 
     */
    private void updateParamsSoon( final String id ) {
        paramList.setModel( getMessageModel("(Please Wait...)") );
        Runnable run= new Runnable() {
           @Override
           public void run() {
               try {
                  final Map<String,String> dss= PDSPPIDB.getInstance().getParams( id, new NullProgressMonitor() );
                  updateParamComboBoxSoon(dss);
               } catch ( final IllegalArgumentException ex ) {
                  Runnable run= new Runnable() {
                      public void run() {
                          paramList.setModel( getMessageModel("("+ex.getMessage()+")" ) );
                      }
                  };
                  SwingUtilities.invokeLater(run);
               }
           }
        };
        new Thread(run).start();
    }
    
    private void productTextFieldActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_productTextFieldActionPerformed
            String lid= productTextField.getText();
            String root= getCurrentRoot();
            lid= root+"/"+lid;
            updateParamsSoon(lid);
    }//GEN-LAST:event_productTextFieldActionPerformed

    private void datasetComboBoxActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_datasetComboBoxActionPerformed
        productTextField.setText("");
        paramList.setModel( getMessageModel("") );
    }//GEN-LAST:event_datasetComboBoxActionPerformed

    private void inventoryScComboBoxItemStateChanged(java.awt.event.ItemEvent evt) {//GEN-FIRST:event_inventoryScComboBoxItemStateChanged
        this.sc= evt.getItem().toString().replaceAll("\\+"," ");
        updateInventorySoon();
    }//GEN-LAST:event_inventoryScComboBoxItemStateChanged

    private void updateSpacecraftSoon() {
        Runnable run= new Runnable() {
            @Override
            public void run() {
                final String[] scs= PDSPPIDB.getInstance().getSpacecraft();
                logger.fine("updateFacetSoon");
                Runnable run= new Runnable() { public void run() {
                    inventoryScComboBox.setModel( new DefaultComboBoxModel(scs) );
                    
                    if ( sc==null ) {
                        logger.finer("picking arbitrary spacecraft for GUI");
                        sc=scs[0].replaceAll("\\+"," ");
                    } //TODO: pref for last spacecraft
                    
                    inventoryScComboBox.setSelectedItem( sc );
                    updateInventorySoon();
                } };
                SwingUtilities.invokeLater(run);
            }   
        };
        new Thread(run).start();
    }
    
    /**
     * test if the two are the same, where t is at least as long as s, and
     * slashes in s are ignored.
     * PPI/CO-E/J/S/SW... is the same as PPI/CO-E_J_S_SW...
     * @param s e.g. PPI/CO-E/J/S/SW
     * @param t e.g. PPI/CO-E_J_S_SW/foo/
     * @return true if they are referreinf to the same dataset, e.g. true in the example case.
     */
    private static boolean isSameId( String s, String t ) {
        if ( s.length()>t.length() ) {
            return false;
        }
        for ( int i=0; i<s.length(); i++ ) {
            if ( s.charAt(i)!='/' && s.charAt(i)!=t.charAt(i) ) {
                return false;
            }
        }
        return true;
    }
    
    private void doCheckIdSelectedItem() {
        if ( inventoryScComboBox.getSelectedItem().equals(sc) ) { 
            if ( id!=null ) {
                int i= id.indexOf("/",0);
                i= id.indexOf("/",i+1);
                for ( int ii=0; ii<datasetComboBox.getModel().getSize(); ii++ ) {
                    String s= datasetComboBox.getModel().getElementAt(ii).toString(); // DANGER: assumes labels are machine readable
                    if ( isSameId( s, id ) ) {
                        datasetComboBox.setSelectedIndex(ii);
                    }
                }
                datasetComboBox.setSelectedItem(id.substring(0,i) );
                productTextField.setText(id.substring(i+1));
                if ( id.length()>i+1 ) {
                    updateParamsSoon(id);
                }
            }
        }
    }
    
    private void updateInventorySoon() {
        Runnable run= new Runnable() {
            @Override
            public void run() {
                logger.fine("updateInventorySoon");
                try {
                    final String[] dss= PDSPPIDB.getInstance().getIds("sc="+sc,"PPI/");
                    Runnable run= new Runnable() { public void run() {
                        datasetComboBox.setModel( new DefaultComboBoxModel(dss) );
                        doCheckIdSelectedItem();
                    } };
                    SwingUtilities.invokeLater(run);
                } catch ( final IOException ex ) {
                    String msg= "<html><b>PDS/PPI Database is not available</b><br>"+ex.getMessage();
                    JOptionPane.showMessageDialog( PDSPPIDataSourceEditorPanel.this, msg );
                }
            }   
        };
        new Thread(run).start();
    }

    // Variables declaration - do not modify//GEN-BEGIN:variables
    private javax.swing.JComboBox datasetComboBox;
    private javax.swing.JComboBox inventoryScComboBox;
    private javax.swing.JLabel jLabel1;
    private javax.swing.JLabel jLabel2;
    private javax.swing.JLabel jLabel3;
    private javax.swing.JLabel jLabel4;
    private javax.swing.JScrollPane jScrollPane1;
    private javax.swing.JList paramList;
    private javax.swing.JButton pickProductButton;
    private javax.swing.JTextField productTextField;
    // End of variables declaration//GEN-END:variables

    @Override
    public boolean reject(String uri) throws Exception {
        return false;
    }


    
    /**
     * 
     * @param uri
     * @param parent
     * @param mon
     * @return
     * @throws Exception 
     */
    @Override
    public boolean prepare(String uri, Window parent, ProgressMonitor mon) throws Exception {
        return true;
    }

    Map<String,String> params; // hold on to original values for a reference.
    
    @Override
    public void setURI(String uri) {
        URISplit split= URISplit.parse(uri);
        Map<String,String> lparams= URISplit.parseParams(split.params);
        this.sc= lparams.get(SC);
        if ( sc!=null ) {
            sc= sc.replaceAll("\\+"," ");
            this.inventoryScComboBox.setSelectedItem(sc);
        }
        this.id= lparams.get(ID); 
        if ( id!=null && id.startsWith("/") ) {
            logger.warning("id ought not start with slash, just PPI/CO-E...");
            id= id.substring(1);
        }
        if ( id!=null && id.startsWith("pds://") ) {
            logger.warning("id ought not start with pds://, just PPI/CO-E...");
            id= id.substring(6);
        }
        this.param= lparams.get(PARAM);
        updateSpacecraftSoon();
    }
    
    private String sc;
    private String id;
    private String param;
    
    public static final String PARAM = "param";
    public static final String ID = "id";
    public static final String SC = "sc";  // spacecraft facet
    
    @Override
    public void markProblems(List<String> problems) {
        
    }

    @Override
    public JPanel getPanel() {
        return this;
    }

    @Override
    public String getURI() {
        String lid= getCurrentRoot() + "/" + this.productTextField.getText(); //TODO: why must I add PPI??
        lid= lid.replaceAll(" ","+");
        String lparam= (String) this.paramList.getSelectedValue();
        if ( lparam!=null ) {
            int i= lparam.indexOf(": ");
            if ( i>-1 ) {
                lparam= lparam.substring(0,i);   
            }
            lparam= lparam.replaceAll(" ","+");
        }
        String lsc= inventoryScComboBox.getSelectedItem().toString().replaceAll(" ","+");
        if ( lparam==null || lparam.startsWith("(") ) {
            return "vap+pdsppi:" + SC+"="+ lsc + "&" + ID + "="+ lid;
        } else {
            return "vap+pdsppi:" + SC+"="+ lsc + "&" + ID + "="+ lid + "&" + PARAM + "="+ lparam;
        }
    }
    
    public static void main( String[] args ) {
        LoggerManager.getLogger( "apdss.pdsppi" ).setLevel(Level.ALL);
        ConsoleHandler h= new ConsoleHandler();
        h.setLevel(Level.ALL);
        Logger.getLogger("").addHandler(h);
        Logger.getLogger("apdss.pdsppi").fine("run from main");
        
        PDSPPIDataSourceEditorPanel test= new PDSPPIDataSourceEditorPanel();
        //test.setURI("vap+pdsppi:sc=Voyager+2&id=PPI/VG2-J-CRS-5-SUMM-FLUX-V1.0/DATA/BS2E_RATE&param=BS2E RATE2");
        //test.setURI("vap+pdsppi:sc=Cassini&id=PPI/CO-E_J_S_SW-CAPS-5-DDR-ELE-MOMENTS-V1.0/");
        test.setURI("vap+pdsppi:id=PPI/VG1-J-CRS-5-SUMM-FLUX-V1.0/DATA/FPHA_RATE&param=PROTON_FLUX_1&sc=Voyager+1");
        JOptionPane.showMessageDialog( null, test);
        System.err.println( test.getURI() );
                
    }
}