/******************************************************************************
* Copyright 1996-2014 United States Government as represented by the
* Administrator of the National Aeronautics and Space Administration.
* All Rights Reserved.
******************************************************************************/

import java.awt.*;
import java.awt.event.*;
import java.text.*;
import java.util.*;
import java.lang.reflect.*;
import javax.swing.*;
import javax.swing.border.*;
import javax.swing.event.*;
import gsfc.nssdc.cdf.*;
import gsfc.nssdc.cdf.util.*;

/**
 *  This class create a table showing all variables for a CDF to allow user to make 
 *  selection how the CDF is exported.
 */

public class ExportCDFTable extends JDialog implements CDFConstants {

    static final long serialVersionUID = 1L;

    private int varNum;
    private Entry[] entries;

    private JFrame myFrame;
    private JPanel mp;
    private JPanel bp;
    private JPanel bpX;
    private CDFSpecPanel cdfspec;

    private JTabbedPane jtp;
    private JCheckBox avc1, avc2;
    private CDFExport myCDFExport;
    private ExportVarListTable aVarListTable;
    private ExportVarFilterTable aVarFilterTable;
    private static ExportCDFTable aCDFTable;
    private JScrollPane scrollPane1, scrollPane2; 
    private JPanel buttonPanel;
    private String fileName;
    private static Boolean outputT = new Boolean(true);
    private static Boolean outputF = new Boolean(false);
    private JButton enterButton, cancelButton, resetButton;
    private String[] columnNames1 = null;
    protected String[] columnNames2 = null;
    private Object[][] dataObject = null;
    private Object[][] filterObject = null;
    private Object[][] filterInitialObject = null;
    private Dimension ss = Toolkit.getDefaultToolkit().getScreenSize();
    private static String[] headcol = {"Item/Variable", "Description", "CATDESC",
					"DataType", "Dimension", "Output" };
    private static String[] headfil = {"Item/Variable", "VALIDMIN", "VALIDMAX", 
					"FILLVAL" };
    protected static String headrow1 = "<Record #>", 
			    headrow2 = "<Indices>";
    protected static String na = "N/A"; 

    protected int numColumns1 = 0, 
	  	  numRows = 0,
		  numColumns2 = 0;
    protected Hashtable myHash = new Hashtable();
    protected int istart = 0;
    private boolean fromRESET;

    static boolean defaultModal = true;

    private static final String ENTER = "Continue";
    private static final String RESET = "Reset";
    private static final String CANCEL = "Cancel";

    private ExportCDFTable(JFrame frame, CDFExport cdfexport) {
        this(frame, cdfexport, defaultModal);
    }

    private ExportCDFTable(JFrame frame, CDFExport cdfexport, boolean modal) {

        super(frame, modal);
        myFrame = frame;
        aCDFTable = this;
        myCDFExport = cdfexport;

    }

    public static ExportCDFTable createCDFTable(JFrame frame, CDFExport cdfexport,
                                                String fileName) {
        if (aCDFTable == null) {
	  aCDFTable = new ExportCDFTable(frame, cdfexport);
          // Only way to close is to use the buttons
          aCDFTable.setDefaultCloseOperation(JDialog.DO_NOTHING_ON_CLOSE);
          aCDFTable.addWindowListener(new WindowAdapter() {
            public void windowClosing(WindowEvent we) {
            }
          });
	  aCDFTable.getContentPane().setLayout(new BorderLayout());
          aCDFTable.mp = (JPanel) aCDFTable.getContentPane();
	  aCDFTable.bp = new JPanel(new BorderLayout(), true);
          aCDFTable.mp.setBorder(new EmptyBorder(10,10,10,10));
	  aCDFTable.cdfspec = new CDFSpecPanel();
	} else {
	  aCDFTable.mp.removeAll();
//	  aCDFTable.bp.removeAll();
	}
	aCDFTable.fileName = fileName;
	aCDFTable.buildPanel();

        aCDFTable.setSize(aCDFTable.getPreferredSize());
        aCDFTable.pack();
        Dimension ps = aCDFTable.getSize();
        aCDFTable.setLocation(aCDFTable.ss.width/2 - ps.width/2,
                              aCDFTable.ss.height/2 - ps.height/2);

//	aCDFTable.pack();
	aCDFTable.setVisible(true);
	return aCDFTable;
    }

    private void buildPanel() {

        aCDFTable.setTitle("Variable Selection for CDF: "+fileName);

        cdfspec.set(myCDFExport.sourceCDF);
        mp.add(cdfspec, BorderLayout.NORTH);

	buildDataTable();
        aVarListTable = ExportVarListTable.createVarListTable(dataObject, columnNames1,
                                 			      myCDFExport, aCDFTable);
        //Create the scroll pane and add the table to it.
        if (scrollPane1 == null)
	  scrollPane1 = new JScrollPane(aVarListTable.getTable());
	else
	  scrollPane1.setViewportView(aVarListTable.getTable());

        if (numColumns2 > 0) { // with filtering
          aVarFilterTable = new ExportVarFilterTable(myFrame, filterObject,
                                                     columnNames2, aCDFTable);

          if (jtp == null) {
	    jtp = new JTabbedPane();
	    jtp.addChangeListener(new TabChangeListener());
	    scrollPane2 = new JScrollPane(aVarFilterTable.getTable());
	  } else {
	    jtp.removeTabAt(1);
	    jtp.removeTabAt(0);
	    scrollPane2.setViewportView(aVarFilterTable.getTable());
	  }
          jtp.addTab("Output", null, scrollPane1, "Variable list and description");
          jtp.addTab("Filtering", null, scrollPane2, "Filtering information");
          mp.add(jtp, BorderLayout.CENTER);
        } else
          mp.add(scrollPane1, BorderLayout.CENTER);

        createCBUIs(bp);
        createButtons(bp);
        mp.add(bp, BorderLayout.SOUTH);
/*
	aCDFTable.setSize(aCDFTable.getPreferredSize());
        pack();
	Dimension ps = aCDFTable.getSize();
	setLocation(ss.width/2 - ps.width/2,
			      ss.height/2 - ps.height/2);

	setVisible(true);
*/
     }

     public void reEnter() {	

	if (myCDFExport.getFilterChanged()) { // a new filter table is created
	  myCDFExport.setFilterChanged(false);
          numColumns2 = 0;
          if (myCDFExport.getFiltering() == 1) {
            if (myCDFExport.getValidMin() == 1) numColumns2++;
            if (myCDFExport.getValidMax() == 1) numColumns2++;
            if (myCDFExport.getFillVal() == 1) numColumns2++;
            if (numColumns2 > 0) numColumns2 ++;
          }
	  if (numColumns2 > 0) { // display both variable list and filter tables
	    setupFiltering();
	    aVarFilterTable = new ExportVarFilterTable(myFrame, filterObject,
						       columnNames2, aCDFTable);
            if (jtp == null) {
	      jtp = new JTabbedPane();
	      jtp.addChangeListener(new TabChangeListener());
	      scrollPane2 = new JScrollPane(aVarFilterTable.getTable());
	    } else {
	      jtp.removeTabAt(1);
	      jtp.removeTabAt(0);
	      scrollPane2.setViewportView(aVarFilterTable.getTable());
	    }
            jtp.addTab("Output", null, scrollPane1, "Variable list and description");
            jtp.addTab("Filtering", null, scrollPane2, "Filtering information");
	    mp.removeAll();
	    mp.add(cdfspec, BorderLayout.NORTH);
            mp.add(jtp, BorderLayout.CENTER);
	    mp.add(bp, BorderLayout.SOUTH);
	  } else { // display only the variable filter table
            filterObject = null;
            filterInitialObject = null;
            columnNames2 = null;
            aVarFilterTable = null;
	    mp.removeAll();
	    mp.add(cdfspec, BorderLayout.NORTH);
	    mp.add(scrollPane1, BorderLayout.CENTER);
	    mp.add(bp, BorderLayout.SOUTH);
	  }
	  aCDFTable.pack();
	  aCDFTable.setSize(aCDFTable.getPreferredSize());
	  Dimension ps = aCDFTable.getSize();
	  aCDFTable.setLocation((ss.width - ps.width)/2,
			        (ss.height - ps.height)/2); 
	} else {
	  aCDFTable.pack();
          Dimension ps = aCDFTable.getSize();
          aCDFTable.setLocation((ss.width - ps.width)/2,
                                (ss.height - ps.height)/2);
	}
//	aCDFTable.pack();
	aCDFTable.toFront();
//	aCDFTable.setVisible(true);
	aCDFTable.show();

    }

/**
  * Build the data and columns that are used for the tabvles.
 */

    private void buildDataTable() {

        varNum = myCDFExport.vars.size();

	numColumns1 = Array.getLength(headcol);
	columnNames1 = new String[numColumns1];
	istart = myCDFExport.recordNum + myCDFExport.indiceNum;

	numRows = varNum + istart;
        for (int i = 0; i < numColumns1; i++)
            columnNames1[i] = headcol[i];

	if (myCDFExport.getFilterChanged()) {
	  myCDFExport.setFilterChanged(false);
	  numColumns2 = 0;
	  if (myCDFExport.getFiltering() == 1) {
	    if (myCDFExport.getValidMin() == 1) numColumns2++;
	    if (myCDFExport.getValidMax() == 1) numColumns2++;
	    if (myCDFExport.getFillVal() == 1) numColumns2++; 
	    if (numColumns2 > 0) numColumns2 ++;
	  }
	}

	dataObject = new Object[numRows][numColumns1];
	    
	if (myCDFExport.recordNum == 1) {
	  myHash.put(headrow1, new Integer(0));
	  dataObject[0][0] = headrow1;
	  dataObject[0][1] = na;
	  dataObject[0][2] = na;
	  dataObject[0][3] = na;
	  dataObject[0][4] = na;
	  if (myCDFExport.outSelection != 3 && 
	      myCDFExport.outSelection != 4) 
	       dataObject[0][5] = outputT;
	  else dataObject[0][5] = outputF;
	}

	if (myCDFExport.indiceNum == 1) {
	  myHash.put(headrow2, new Integer(1));
	  dataObject[myCDFExport.recordNum][0] = headrow2;
	  dataObject[myCDFExport.recordNum][1] = na;
	  dataObject[myCDFExport.recordNum][2] = na;
	  dataObject[myCDFExport.recordNum][3] = na;
	  dataObject[myCDFExport.recordNum][4] = na;
	  if (myCDFExport.outSelection != 3 &&
	      myCDFExport.outSelection != 4) 
	    dataObject[myCDFExport.recordNum][5] = outputT;
	  else
	    dataObject[myCDFExport.recordNum][5] = outputF;
	}

        /******************************************************************/
        /* Get the Variable information along with some of its attributes */ 
        /******************************************************************/
        String  varName, dataType, catdesc, description, dimensionality;
	StringBuffer tempBuffer = new StringBuffer();
        long    numDims;
	int ii = 0;
	String labptr = "LABL_PTR_";
	Variable v = null;

        for (Enumeration e = myCDFExport.vars.elements(); e.hasMoreElements(); ii++) {

            v = (Variable) e.nextElement();
            varName = v.getName();

	    try {
              Object tmp = v.getEntryData("LABLAXIS");
	      description = tmp.toString();
	    } catch (CDFException ex) {
	      description = null;
	    }
	    if (description == null) {
	      String label;
	      StringBuffer ds = new StringBuffer();
	      int ix;
	      for (ix = 1; ix < Integer.MAX_VALUE; ix++) {
	         label = labptr + ix;
	         try {
	           Object tmp = v.getEntryData(label);
	           String desc = tmp.toString();
	           if (ix > 1) ds.append(" ");
	           ds.append(desc);
	         } catch (CDFException ex) {
	           break;
	         }
	      }
	      if (ix > 1) description = ds.toString();
	    }
 	    if (description == null) description = " "; 

	    try {
	      Object tmp = v.getEntryData("CATDESC");
	      catdesc = tmp.toString();
	    } catch (CDFException ex) {
	      catdesc = " ";
	    }

            long[] dimSizes = v.getDimSizes();
	    StringBuffer dt = new StringBuffer();
            dt.append(CDFUtils.getStringDataType(v.getDataType()));
            dt.append("/");
            dataType = dt.append(String.valueOf(v.getNumElements())).toString();

            numDims = v.getNumDims();
	    StringBuffer nd = new StringBuffer();
                nd.append(numDims).append(":[");
                for (int i = 0; i < numDims; i++) {
                    if (i > 0) nd.append(",");
                    nd.append(dimSizes[i]); 
                }
            dimensionality = nd.append("]").toString();
	    myHash.put(varName, new Integer(istart+ii));
	    dataObject[istart+ii][0] = varName;
	    dataObject[istart+ii][1] = description;
	    dataObject[istart+ii][2] = catdesc;
	    dataObject[istart+ii][3] = dataType;
	    dataObject[istart+ii][4] = dimensionality;
	    dataObject[istart+ii][5] = outputT;
        }
        if (numColumns2 > 0) // filtering is on
	  setupFiltering();

    }

/** Set up filtering table
 */

    private void setupFiltering() {


	filterObject = new Object[numRows][numColumns2];
	filterInitialObject = new Object[numRows][numColumns2];
	if (myCDFExport.recordNum == 1) {
          filterObject[0][0] = headrow1;
	  filterInitialObject[0][0] = headrow1;
        }

	if (myCDFExport.indiceNum == 1) {
	  filterObject[myCDFExport.recordNum][0] = headrow2;
          filterInitialObject[myCDFExport.recordNum][0] = headrow2;
	}

	int i = 1; // for two rows are for record # and indices

	if (myCDFExport.getValidMin() == 1) {
	  if (myCDFExport.recordNum == 1) { 
	    filterObject[0][i] = na;
	    filterInitialObject[0][i] = na;
	  }
	  if (myCDFExport.indiceNum == 1) {
	    filterObject[myCDFExport.recordNum][i] = na;
	    filterInitialObject[myCDFExport.recordNum][i] = na;
	  }
	  i++;
	}

        if (myCDFExport.getValidMax() == 1) {
	  if (myCDFExport.recordNum == 1) {
	    filterObject[0][i] = na;
	    filterInitialObject[0][i] = na;
	  }
	  if (myCDFExport.indiceNum == 1) {
	    filterObject[myCDFExport.recordNum][i] = na;
	    filterInitialObject[myCDFExport.recordNum][i] = na;
	  }
	  i++;
	}

        if (myCDFExport.getFillVal() == 1) {
	  if (myCDFExport.recordNum == 1) {
	    filterObject[0][i] = na;
	    filterInitialObject[0][i] = na;
	  }
	  if (myCDFExport.indiceNum == 1) {
	    filterObject[myCDFExport.recordNum][i] = na;
	    filterInitialObject[myCDFExport.recordNum][i] = na;
	  }
	}

        columnNames2 = new String[numColumns2];
        columnNames2[0] = headfil[0];
        i = 1;
        if (myCDFExport.getValidMin() == 1)
          columnNames2[i++] = headfil[1];
        if (myCDFExport.getValidMax() == 1)
          columnNames2[i++] = headfil[2];
        if (myCDFExport.getFillVal() == 1)
          columnNames2[i++] = headfil[3];

	int ii = 0;
	String varName = null;
	Variable v = null;
        int style;
        for (Enumeration e = myCDFExport.vars.elements(); e.hasMoreElements(); ii++) {

            v = (Variable) e.nextElement();
            varName = v.getName();
            long dataType = v.getDataType();
            if (dataType == CDF_TIME_TT2000) style = 3;
            else if (dataType == CDF_EPOCH) style = 1;
            else if (dataType == CDF_EPOCH16) style = 2;
            else style = 0;
	    i = 1;
	    filterObject[istart+ii][0] = varName;

	    if (myCDFExport.getValidMin() == 1) {
	      try { 
	        Object validMin = v.getEntryData("VALIDMIN");
	        String tmp = CDFToolUtils.breakdownData(validMin, style);
                filterObject[istart+ii][i] = tmp;
	        filterInitialObject[istart+ii][i++] = tmp;
	      } catch (CDFException ex) {
                filterObject[istart+ii][i] = " ";
	        filterInitialObject[istart+ii][i++] = " ";
	      }
	    }

	    if (myCDFExport.getValidMax() == 1) {
              try {  
                Object validMax = v.getEntryData("VALIDMAX");
	        String tmp = CDFToolUtils.breakdownData(validMax, style);
                filterObject[istart+ii][i] = tmp;
	        filterInitialObject[istart+ii][i++] = tmp;
              } catch (CDFException ex) {
                filterObject[istart+ii][i] = " ";
	        filterInitialObject[istart+ii][i++] = " ";
              }
	    }

	    if (myCDFExport.getFillVal() == 1) {
              try {  
                Object fillVal = v.getEntryData("FILLVAL");
	        String tmp = CDFToolUtils.breakdownData(fillVal, style);
                filterObject[istart+ii][i] = tmp;
	        filterInitialObject[istart+ii][i] = tmp;
              } catch (CDFException ex) {
                filterObject[istart+ii][i] = " ";
	        filterInitialObject[istart+ii][i] = " ";
              }
	    }

	}
    }

/** Build User Interface items for Check Box
 */

    private void createCBUIs(JPanel bp) {

      if (avc1 == null) {
        avc1 = new JCheckBox("Select all variables", true);
	avc2 = new JCheckBox("Deselect all variables", false);

        OptionsListener optionsListener = new OptionsListener();
        avc1.addActionListener(optionsListener);
        avc1.setToolTipText("Select all variables for export");
        avc2.addActionListener(optionsListener);
        avc2.setToolTipText("Deselect all variables for export");

        bpX = new JPanel(new GridLayout(2,3), true);
        bpX.add(avc1); bpX.add(Box.createRigidArea(new Dimension(60,1)));
        bpX.add(Box.createRigidArea(new Dimension(60,1)));
        bpX.add(avc2); bpX.add(Box.createRigidArea(new Dimension(60,1)));
        bpX.add(Box.createRigidArea(new Dimension(60,1)));
	bp.add(bpX, BorderLayout.NORTH);
      }
//      bp.add(bpX, BorderLayout.NORTH);

    }

/** Create selection buttons at the bottom of panel
 */

    private void createButtons(JPanel jp) {

      if (enterButton == null) {
        enterButton = new JButton(ENTER);
        resetButton = new JButton(RESET);
        cancelButton = new JButton(CANCEL);
        buttonPanel = new JPanel(true);

        buttonPanel.add(enterButton);
        buttonPanel.add(Box.createRigidArea(new Dimension(20,1)));
	buttonPanel.add(resetButton);
	buttonPanel.add(Box.createRigidArea(new Dimension(20,1)));
	buttonPanel.add(cancelButton);

	SelectionListener aSelectionListener = new SelectionListener();
	enterButton.addActionListener(aSelectionListener);
        resetButton.addActionListener(aSelectionListener);
        cancelButton.addActionListener(aSelectionListener);

        enterButton.setToolTipText("Continue to the next step ...");
        resetButton.setToolTipText("Reset the selections to their defaults...");
        cancelButton.setToolTipText("Stop but keep the selections and retrun to the previous step...");
	jp.add(buttonPanel, BorderLayout.SOUTH);
      }
//      jp.add(buttonPanel, BorderLayout.SOUTH);
    }

/** Reset the selection to the defaults
 */

    private void reset() {

	for (int i = 0; i < numRows; i++) {
	   dataObject[i][5] = outputT;
	   aVarListTable.getModel().setValueAt(outputT, i, 5);
	   if (fromRESET) {
	     if (numColumns2 > 0) {
	       for (int j = 1; j < numColumns2; j++) {
	     	  filterObject[i][j] = filterInitialObject[i][j]; 
	     	  aVarFilterTable.getModel().setValueAt(filterObject[i][j], i, j);
//		  updateTable[i][j] = false;
	       }
	     }
	   }
	}
    }

/** Reset all variable selections to false
 */

    private void deselect() {

        for (int i = istart; i < numRows; i++) {
           dataObject[i][5] = outputF;
           aVarListTable.getModel().setValueAt(outputF, i, 5);
        }
    }

/** Action for checkboxs for selecting/deselecting all variables
 */

    class OptionsListener implements ActionListener {
      public void actionPerformed(ActionEvent evt) {
        Object obj = evt.getSource();
        if (obj == avc1) { // "Select all variables" checkbox
              if (avc1.isSelected()) {
		avc2.setSelected(false);
		fromRESET = false; // not from the reset button
                reset();
              } 
        } else if (obj == avc2) { // "Deselect all variables" checkbox
              if (avc2.isSelected()) {
		avc1.setSelected(false);
                deselect();
		avc2.setSelected(true);
              }
	} 
      }
    }

/**
 * Process button events.
 */
    class SelectionListener implements ActionListener {
        public void actionPerformed(ActionEvent event) {
          Object source = event.getSource();

          if (source instanceof JButton) {
            String action = event.getActionCommand();
            if (action.equals(CANCEL)) {              // Cancel the operation
                aCDFTable.setVisible(false);
		if (myCDFExport.MacOS) System.gc();
            } else if (action.equals(ENTER)) {        // Continue the operation
                aCDFTable.setVisible(false);
		if (numColumns2 > 0) 
		  aVarFilterTable.getTable().editingStopped(new ChangeEvent(aVarFilterTable));

		if (myCDFExport.outSelection == 3 ||
		    myCDFExport.outSelection == 4) { // to a CDF file
		  ExportCreateCDF aCreateCDF = new ExportCreateCDF(myCDFExport, aCDFTable);
		  new Thread(aCreateCDF).start();
		} else {
		  boolean tORf;
		  if (myCDFExport.outSelection == 1) // to the screen
		    tORf = true;
		  else
		    tORf = false;
		  ExportToText  aToText = ExportToText.createToText(myFrame, myCDFExport, 
								    aCDFTable, tORf);
		} 
            } else if (action.equals(RESET)) {        // Reset the selection
		fromRESET = true;
                reset();
            }
          }
        }
   }

/**
 * Process button events.
 */
    class TabChangeListener implements ChangeListener {
        public void stateChanged(ChangeEvent ev) {
          Object source = ev.getSource();
          if (((JTabbedPane)source).getSelectedIndex() != 0) {
	    avc1.setEnabled(false);
	    avc2.setEnabled(false);
	  } else {
	    if (avc1 != null) avc1.setEnabled(true);
	    if (avc2 != null) avc2.setEnabled(true);
	  }
	}
    }

    public Object[][] getDataObject() {
	return dataObject;
    }

    public Object[][] getFilterObject() {
        return filterObject;
    }

    public JCheckBox getSelectAllCB() {
        return avc1;
    }

    public JCheckBox getDeSelectAllCB() {
        return avc2;
    }


}
