// $Id: ToolsUI.java,v 1.35 2004/12/15 00:11:46 caron Exp $
/*
 * Copyright 1997-2004 Unidata Program Center/University Corporation for
 * Atmospheric Research, P.O. Box 3000, Boulder, CO 80307,
 * support@unidata.ucar.edu.
 *
 * This library is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation; either version 2.1 of the License, or (at
 * your option) any later version.
 *
 * This library is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser
 * General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this library; if not, write to the Free Software Foundation,
 * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
 */

package ucar.nc2.ui;

import ucar.nc2.*;
import ucar.nc2.ncml.NcMLReader;
import ucar.nc2.station.StationObsDataset;
import ucar.nc2.station.StationDatasetFactory;
import ucar.nc2.dataset.*;
import ucar.nc2.dataset.grid.GridDataset;
//import ucar.nc2.geotiff.GeoTiff;
import ucar.nc2.util.DebugFlags;
import ucar.nc2.util.CancelTask;
import ucar.nc2.units.*;

import ucar.util.prefs.*;
import ucar.util.prefs.ui.*;

import thredds.ui.*;
import ucar.nc2.ui.grid.GridUI;
import ucar.nc2.ui.image.ImageViewPanel;
import thredds.util.URLStreamHandlerFactory;
import thredds.catalog.InvDataset;
import thredds.catalog.InvAccess;

import java.awt.*;
import java.awt.event.*;
import java.io.*;
import java.util.*;

import javax.swing.*;
import javax.swing.event.*;

/**
 * Netcdf Tools user interface.
 *
 * @author John Caron
 * @version $Id: ToolsUI.java,v 1.35 2004/12/15 00:11:46 caron Exp $
 */

public class ToolsUI extends JPanel {

  private final static String FRAME_SIZE = "FrameSize";
  private final static String DEBUG_FRAME_SIZE = "DebugWindowSize";
  private final static String GRIDVIEW_FRAME_SIZE = "GridUIWindowSize";
  private ucar.util.prefs.PreferencesExt mainPrefs;

  private NCdumpPanel ncdumpPanel;
  private OpPanel coordSysPanel, ncmlPanel;
  private ViewerPanel viewerPanel;
  private ImagePanel imagePanel;
  private StationTablePanel stnTablePanel;
  private GeoGridPanel gridPanel;
  private UnitsPanel unitsPanel;
  private URLDumpPane urlPanel;
  private ThreddsUI threddsUI;

  private JTabbedPane tabbedPane;
  private JFrame parentFrame;
  private FileManager fileChooser;

  // debugging
  private JMenu debugFlagMenu;
  private DebugFlags debugFlags;
  private AbstractAction useDebugWindowAction;
  private IndependentWindow debugWindow;
  private TextOutputStreamPane debugPane;
  private PrintStream debugOS;
  private boolean debug = false, debugCB = false, debugTab = false, debugNcmlWrite = true;

  private AboutWindow aboutWindow = null;

  public ToolsUI(ucar.util.prefs.PreferencesExt prefs, JFrame parentFrame) {
    this.mainPrefs = prefs;
    this.parentFrame = parentFrame;

    // FileChooser is shared
    javax.swing.filechooser.FileFilter[] filters = new javax.swing.filechooser.FileFilter[2];
    filters[0] = new FileManager.HDF5ExtFilter();
    filters[1] = new FileManager.NetcdfExtFilter();
    fileChooser = new FileManager(parentFrame, null, filters, (PreferencesExt) prefs.node("FileManager"));

    viewerPanel= new ViewerPanel( (PreferencesExt) mainPrefs.node("varTable"));

    // the overall UI
    tabbedPane = new JTabbedPane(JTabbedPane.TOP);
    tabbedPane.addTab("Viewer", viewerPanel);

    // all the other component are defferred for fast startup
    tabbedPane.addTab("NCDump", new JLabel("NCDump"));
    tabbedPane.addTab("CoordSys", new JLabel("Dataset"));
    tabbedPane.addTab("GeoGrids", new JLabel("GeoGrids"));
    tabbedPane.addTab("StationDataset", new JLabel("StationDataset"));
    tabbedPane.addTab("Images", new JLabel("Images"));
    tabbedPane.addTab("THREDDS", new JLabel("THREDDS"));
    tabbedPane.addTab("Units", new JLabel("Units"));
    tabbedPane.addTab("NcML", new JLabel("NcML"));
    tabbedPane.addTab("URLdump", new JLabel("URLdump"));
    tabbedPane.setSelectedIndex(0);
    tabbedPane.addChangeListener( new ChangeListener () {
      public void stateChanged(ChangeEvent e) {
        Component c = tabbedPane.getSelectedComponent();
        if (c instanceof JLabel) {
          int idx = tabbedPane.getSelectedIndex();
          String title = tabbedPane.getTitleAt( idx);
          makeComponent(title);
        }
      }
    });

    setLayout( new BorderLayout());
    add(tabbedPane, BorderLayout.CENTER);

    // dynamic proxy for DebugFlags
    debugFlags = (DebugFlags) java.lang.reflect.Proxy.newProxyInstance(
        DebugFlags.class.getClassLoader(),new Class[] { DebugFlags.class }, new DebugProxyHandler());

    // the debug message window
    debugPane = new TextOutputStreamPane();
    debugWindow = debugPane.makeIndependentWindow("Debug Messages");
    Rectangle bounds = (Rectangle) mainPrefs.getBean(DEBUG_FRAME_SIZE, new Rectangle(100, 50, 500, 700));
    debugWindow.setBounds( bounds);
    debugWindow.setIconImage( BAMutil.getImage("netcdfUI"));

    makeMenuBar();
    setDebugFlags();
  }

  // deffered creation of components to minimize startup
  private void makeComponent(String title) {
    // find the correct index
    int n = tabbedPane.getTabCount();
    int idx;
    for (idx=0; idx<n; idx++) {
      String cTitle = tabbedPane.getTitleAt(idx);
      if (cTitle.equals(title)) break;
    }
    if (idx >= n) {
      if (debugTab) System.out.println("tabbedPane cant find "+title);
      return;
   }

    Component c;
    if (title.equals("NCDump")) {
      ncdumpPanel= new NCdumpPanel( (PreferencesExt) mainPrefs.node("NCDump"));
      c = ncdumpPanel;

    } else if (title.equals("NcML")) {
      ncmlPanel= new NcmlPanel( (PreferencesExt) mainPrefs.node("NcML"));
      c = ncmlPanel;

    } else if (title.equals("CoordSys")) {
      coordSysPanel= new CoordSysPanel( (PreferencesExt) mainPrefs.node("CoordSys"));
      c = coordSysPanel;

    } else if (title.equals("GeoGrids")) {
      gridPanel= new GeoGridPanel( (PreferencesExt) mainPrefs.node("grid"));
      c = gridPanel;

    } else if (title.equals("StationDataset")) {
      stnTablePanel= new StationTablePanel( (PreferencesExt) mainPrefs.node("stations"));
      c = stnTablePanel;

    } else if (title.equals("Images")) {
      imagePanel= new ImagePanel( (PreferencesExt) mainPrefs.node("images"));
      c = imagePanel;

    } else if (title.equals("THREDDS")) {
      threddsUI = new ThreddsUI(ToolsUI.this.parentFrame, (PreferencesExt) mainPrefs.node("thredds"));
      threddsUI.addPropertyChangeListener(  new java.beans.PropertyChangeListener() {
        public void propertyChange( java.beans.PropertyChangeEvent e) {
          if (e.getPropertyName().equals("Dataset")) {
            thredds.catalog.InvDataset ds = (thredds.catalog.InvDataset) e.getNewValue();
            setThreddsDataset( ds);
          }
        }
      });

      c = threddsUI;

    } else if (title.equals("Units")) {
      unitsPanel= new UnitsPanel( (PreferencesExt) mainPrefs.node("units"));
      c = unitsPanel;

    } else if (title.equals("URLdump")) {
      urlPanel= new URLDumpPane( (PreferencesExt) mainPrefs.node("urlDump"));
      c = urlPanel;

    } else {
      System.out.println("tabbedPane unknown component "+title);
      return;
    }

    tabbedPane.setComponentAt( idx, c);
    if (debugTab) System.out.println("tabbedPane changed "+title+" added ");
  }

  private void makeMenuBar() {
    JMenuBar mb = new JMenuBar();
    JRootPane rootPane = parentFrame.getRootPane();
    rootPane.setJMenuBar(mb);

        /// System menu
    JMenu sysMenu = new JMenu("System");
    sysMenu.setMnemonic('S');
    mb.add(sysMenu);
    //BAMutil.addActionToMenu( sysMenu, printAction);

    JMenu plafMenu = new JMenu("Look and Feel");
    plafMenu.setMnemonic('L');
    sysMenu.add(plafMenu);
    PLAF plaf = new PLAF(rootPane);
    plaf.addToMenu( plafMenu);

    AbstractAction exitAction = new AbstractAction() {
      public void actionPerformed(ActionEvent e) {
        System.exit(0); }
    };
    BAMutil.setActionProperties( exitAction, "Exit", "Exit", false, 'C', -1);
    BAMutil.addActionToMenu( sysMenu, exitAction);

    JMenu modeMenu = new JMenu("Modes");
    modeMenu.setMnemonic('M');
    mb.add(modeMenu);
    makeModesMenu( modeMenu);

    JMenu debugMenu = new JMenu("Debug");
    debugMenu.setMnemonic('D');
    mb.add(debugMenu);

    // the list of debug flags are in a pull-aside menu
    // they are dynamically discovered, and persisted
    debugFlagMenu = (JMenu) debugMenu.add(new JMenu("Debug Flags"));
    debugFlagMenu.addMenuListener( new MenuListener() {
      public void menuSelected(MenuEvent e) {
        setDebugFlags(); // let Debug know about the flag names
        ucar.util.prefs.ui.Debug.constructMenu(debugFlagMenu); // now construct the menu
      }
      public void menuDeselected(MenuEvent e) {
        setDebugFlags(); // transfer menu values
      }
      public void menuCanceled(MenuEvent e) {}
    });

    // this deletes all the flags, then they start accululating again
    AbstractAction clearDebugFlagsAction = new AbstractAction() {
      public void actionPerformed(ActionEvent e) { ucar.util.prefs.ui.Debug.removeAll(); }
    };
    BAMutil.setActionProperties( clearDebugFlagsAction, null, "Delete All Debug Flags", false, 'C', -1);
    BAMutil.addActionToMenu( debugMenu, clearDebugFlagsAction);

    // send output to the debug message window
    useDebugWindowAction = new AbstractAction() {
      public void actionPerformed(ActionEvent e) {
        Boolean state = (Boolean) getValue( BAMutil.STATE);
        setDebugOutputStream( state.booleanValue());
      }
    };
    BAMutil.setActionProperties( useDebugWindowAction, null, "Use Debug Window", true, 'C', -1);
    BAMutil.addActionToMenu( debugMenu, useDebugWindowAction);
    if (mainPrefs.getBoolean("useDebugWindow", false)) {
      useDebugWindowAction.putValue( BAMutil.STATE, Boolean.TRUE);
      setDebugFlags();
      setDebugOutputStream(true);
    }

    // show the debug window
    AbstractAction showDebugAction = new AbstractAction() {
      public void actionPerformed(ActionEvent evt) {
        // System.out.println("debugWindow.show() "+debugWindow.getBounds());
        debugWindow.show();
      }
    };
    BAMutil.setActionProperties( showDebugAction, null, "Show Debug Window", false, 'D', 0);
    BAMutil.addActionToMenu( debugMenu, showDebugAction);

    JMenu helpMenu = new JMenu("Help");
    helpMenu.setMnemonic('H');
    mb.add(helpMenu);

    // "about" this application
    AbstractAction aboutAction = new AbstractAction() {
      public void actionPerformed(ActionEvent evt) {
        if (aboutWindow == null)
          aboutWindow = new AboutWindow();
        aboutWindow.show();
      }
    };
    BAMutil.setActionProperties( aboutAction, null, "About", false, 'A', 0);
    BAMutil.addActionToMenu( helpMenu, aboutAction);
  }

  public void setDebugFlags() {
    if (debug) System.out.println("checkDebugFlags ");
    NetcdfFile.setDebugFlags( debugFlags); // let Debug know about the flag names
    ucar.nc2.ncml.NcMLReader.setDebugFlags( debugFlags); // let Debug know about the flag names
    ucar.nc2.dods.DODSNetcdfFile.setDebugFlags( debugFlags);
    ucar.nc2.iosp.grib.GribServiceProvider.setDebugFlags(debugFlags);
    ucar.nc2.thredds.ThreddsDataFactory.setDebugFlags(debugFlags);
  }

  public void setDebugOutputStream( boolean b) {
    // System.out.println("setDebugOutputStream "+b);
    if (b) {
      if (debugOS == null) debugOS = new PrintStream( debugPane.getOutputStream());
      NetcdfFile.setDebugOutputStream( debugOS);
   } else {
     NetcdfFile.setDebugOutputStream(System.out);
   }
  }

  private void makeModesMenu( JMenu modeMenu) {
    AbstractAction a;

    JMenu ncMenu = new JMenu("NetcdfFile");
    modeMenu.add( ncMenu);

    a = new AbstractAction() {
      public void actionPerformed(ActionEvent e) {
        Boolean state = (Boolean) getValue( BAMutil.STATE);
        NetcdfFile.setUseRecordStructure( state.booleanValue());
      }
    };
    BAMutil.setActionPropertiesToggle( a, null, "nc3UseRecords", NetcdfFile.getUseRecordStructure(), 'V', -1);
    BAMutil.addActionToMenu( ncMenu, a);

    JMenu dsMenu = new JMenu("NetcdfDataset");
    modeMenu.add( dsMenu);

    a = new AbstractAction() {
      public void actionPerformed(ActionEvent e) {
        Boolean state = (Boolean) getValue( BAMutil.STATE);
        NetcdfDataset.setUseNaNs( state.booleanValue());
      }
    };
    BAMutil.setActionPropertiesToggle( a, null, "set NaNs for missing values", NetcdfDataset.getUseNaNs(), 'N', -1);
    BAMutil.addActionToMenu( dsMenu, a);

    a = new AbstractAction() {
      public void actionPerformed(ActionEvent e) {
        Boolean state = (Boolean) getValue( BAMutil.STATE);
        NetcdfDataset.setFillValueIsMissing( state.booleanValue());
      }
    };
    BAMutil.setActionPropertiesToggle( a, null, "use _FillValue attribute for missing values",
         NetcdfDataset.getFillValueIsMissing(), 'F', -1);
    BAMutil.addActionToMenu( dsMenu, a);

    a = new AbstractAction() {
      public void actionPerformed(ActionEvent e) {
        Boolean state = (Boolean) getValue( BAMutil.STATE);
        NetcdfDataset.setInvalidDataIsMissing( state.booleanValue());
      }
    };
    BAMutil.setActionPropertiesToggle( a, null, "use valid_range attribute for missing values",
         NetcdfDataset.getInvalidDataIsMissing(), 'V', -1);
    BAMutil.addActionToMenu( dsMenu, a);

    a = new AbstractAction() {
      public void actionPerformed(ActionEvent e) {
        Boolean state = (Boolean) getValue( BAMutil.STATE);
        NetcdfDataset.setMissingDataIsMissing( state.booleanValue());
      }
    };
    BAMutil.setActionPropertiesToggle( a, null, "use mssing_value attribute for missing values",
         NetcdfDataset.getMissingDataIsMissing(), 'M', -1);
    BAMutil.addActionToMenu( dsMenu, a);
  }

  public void save() {
    fileChooser.save();
    if (debugWindow != null) {
      mainPrefs.putBeanObject(DEBUG_FRAME_SIZE, debugWindow.getBounds());
    }
    Boolean useDebugWindow = (Boolean) useDebugWindowAction.getValue( BAMutil.STATE);
    if (useDebugWindow.booleanValue())
      mainPrefs.putBoolean("useDebugWindow", true);

    if( viewerPanel != null) viewerPanel.save();
    if( coordSysPanel != null) coordSysPanel.save();
    if( ncdumpPanel != null) ncdumpPanel.save();
    if( ncmlPanel != null) ncmlPanel.save();
    if( imagePanel != null) imagePanel.save();
    if( gridPanel != null) gridPanel.save();
    if( stnTablePanel != null) stnTablePanel.save();
    if( threddsUI != null) threddsUI.storePersistentData();
    if( unitsPanel != null) unitsPanel.save();
    if( urlPanel != null) urlPanel.save();
  }

  //////////////////////////////////////////////////////////////////////////////////

  // jump to the appropriate tab based on datatype of InvDataset
  private void setThreddsDataset(thredds.catalog.InvDataset invDataset) {
    if (invDataset == null) return;

    ucar.nc2.thredds.ThreddsDataFactory tdf = new ucar.nc2.thredds.ThreddsDataFactory();

    if (invDataset.getDataType() == thredds.catalog.DataType.GRID) {

      NetcdfDataset ds = null;
      try {
        ds = tdf.openDataset( invDataset, null);
      } catch (IOException e) {
        JOptionPane.showMessageDialog(null, "ThreddsDatasetFactory cant open "+invDataset);
        return;
      }
      if (ds == null) return;

      makeComponent("GeoGrids");
      gridPanel.setDataset( ds);
      tabbedPane.setSelectedComponent( gridPanel);
      return;
    }

    else if (invDataset.getDataType() == thredds.catalog.DataType.STATION) {

      InvAccess access = tdf.getStationAccess( invDataset);
      if (access == null) return;

      makeComponent("StationDataset");
      stnTablePanel.setStationObsDataset( access);
      tabbedPane.setSelectedComponent( stnTablePanel);
      return;
    }

    else if (invDataset.getDataType() == thredds.catalog.DataType.IMAGE) {

      InvAccess access = tdf.getImageAccess( invDataset);
      if (access == null) return;

      makeComponent("Images");
      imagePanel.setImageLocation( access.getStandardUrlName());
      tabbedPane.setSelectedComponent( imagePanel);
      return;
    }

    else {

      NetcdfDataset ds = null;
      try {
        ds = tdf.openDataset(invDataset, null);
      } catch (IOException e) {
        JOptionPane.showMessageDialog(null, "ThreddsDatasetFactory cant open " + invDataset);
      }
      if (ds == null) return;

      makeComponent("Viewer");
      viewerPanel.setDataset(ds);
      tabbedPane.setSelectedComponent(viewerPanel);
      return;
    }


  }

  private NetcdfDataset openDataset(String location, boolean addCoords, CancelTask task) {
    NetcdfDataset ncfile = null;
    try {
      ncfile = NetcdfDataset.openDataset( location, addCoords, task);
      if (ncfile == null) {
        JOptionPane.showMessageDialog(null, "NetcdfDataset.open cant open "+location);
      }
    } catch (IOException ioe) {
       JOptionPane.showMessageDialog(null, "NetcdfDataset.open cant open "+ioe.getMessage());
    }
    return ncfile;
  }

  /////////////////////////////////////////////////////////////////////////////////////////////////////////////
  // the panel contents

  // abstract superclass
  // subclasses must implement process()
  private abstract class OpPanel extends JPanel {
    PreferencesExt prefs;
    TextHistoryPane ta;
    ComboBox cb;
    JPanel buttPanel;
    AbstractButton v3Butt, dsButt;
    boolean addCoords;
    long lastEvent = -1;
    boolean eventOK = true;

    OpPanel(PreferencesExt prefs, String title, String command) {
      this( prefs, title, command, true);
    }

    OpPanel(PreferencesExt prefs, String title, String command, boolean useFileButton) {
      this.prefs = prefs;
      ta = new TextHistoryPane(true);

      cb = new ComboBox(prefs);
      cb.addActionListener(new ActionListener() {
        public void actionPerformed(ActionEvent e) {
          if (debugCB) System.out.println("cb event="+e);
          if ((e.getWhen() != lastEvent) && eventOK) // eliminate multiple events from same selection
            doit( cb.getSelectedItem());
          lastEvent = e.getWhen();
        }
      });

      /* button and comboBox dont work!
      AbstractAction getAction =  new AbstractAction() {
        public void actionPerformed(ActionEvent e) {
          doit( cb.getSelectedItem());
        }
      };
      BAMutil.setActionProperties( getAction, "Doit", "open selected", false, 'O', -1);  */

      AbstractAction fileAction =  new AbstractAction() {
        public void actionPerformed(ActionEvent e) {
          String filename = fileChooser.chooseFilename();
          if (filename == null) return;
          cb.setSelectedItem(filename);
          //doit( filename);
        }
      };
      BAMutil.setActionProperties( fileAction, "FileChooser", "open Local dataset...", false, 'L', -1);

      /* AbstractAction v3Action =  new AbstractAction() {
        public void actionPerformed(ActionEvent e) {
          Boolean state = (Boolean) getValue( BAMutil.STATE);
          nc3useRecords = state.booleanValue();
          String tooltip = nc3useRecords ? "nc3 use Records" : "nc3 dont use Records";
          v3Butt.setToolTipText(tooltip);
          doit( cb.getSelectedItem());
        }
      };
      nc3useRecords = prefs.getBoolean( "nc3useRecords", false);
      String tooltip = nc3useRecords ? "nc3 use Records" : "nc3 dont use Records";
      BAMutil.setActionProperties( v3Action, "V3", tooltip, true, 'V', -1);
      v3Action.putValue(BAMutil.STATE, new Boolean(nc3useRecords)); */

      AbstractAction dsAction =  new AbstractAction() {
        public void actionPerformed(ActionEvent e) {
          Boolean state = (Boolean) getValue( BAMutil.STATE);
          addCoords = state.booleanValue();
          String tooltip = addCoords ? "Convention parsing is ON" : "Convention parsing is OFF";
          dsButt.setToolTipText(tooltip);
          //doit( cb.getSelectedItem());
        }
      };
      addCoords = prefs.getBoolean( "dsState", false);
      String tooltip2 = addCoords ? "Convention parsing is ON" : "Convention parsing is OFF";
      BAMutil.setActionProperties( dsAction, "ConventionParsing", tooltip2, true, 'C', -1);
      dsAction.putValue(BAMutil.STATE, new Boolean(addCoords));

      buttPanel = new JPanel(new FlowLayout(FlowLayout.LEFT, 5, 0));
      // BAMutil.addActionToContainer(buttPanel, getAction);
      if (useFileButton) {
        BAMutil.addActionToContainer(buttPanel, fileAction);
        //v3Butt = BAMutil.addActionToContainer(buttPanel, v3Action);
        dsButt = BAMutil.addActionToContainer(buttPanel, dsAction);
      }

      JPanel topPanel = new JPanel( new BorderLayout());
      topPanel.add(new JLabel(command), BorderLayout.WEST);
      topPanel.add(cb, BorderLayout.CENTER);
      topPanel.add(buttPanel, BorderLayout.EAST);

      setLayout( new BorderLayout());
      add( topPanel, BorderLayout.NORTH);
      add( ta, BorderLayout.CENTER);
    }

    void doit(Object command) {
      if (command == null) return;
      if (command instanceof String)
        command = ((String)command).trim();

      if (debug) System.out.println(getClass().getName()+" process=" +command);
      if (process( command))
        cb.addItem( command);
    }

    abstract boolean process( Object command);

    void save() {
      cb.save();
      //if (v3Butt != null) prefs.putBoolean("nc3useRecords", v3Butt.getModel().isSelected());
      if (dsButt != null) prefs.putBoolean("dsState", dsButt.getModel().isSelected());
    }

    void setSelectedItem( Object item) { cb.setSelectedItem( item); };
  }

  private class NCdumpPanel extends OpPanel {
    private GetContentsTask task;
    private boolean ready = true;
    private StopButton stopButton;
    NetcdfFile ncfile = null;

    NCdumpPanel(PreferencesExt prefs) {
      super( prefs, "call NCdump", "command:");
      stopButton = new StopButton("stop NCDump");
      buttPanel.add( stopButton);

     stopButton.addActionListener(new ActionListener() {
        public void actionPerformed(ActionEvent e) {
          if (debug) System.out.println(" ncdump stopButton event="+e.getActionCommand());
          ta.setText(task.contents);
          ta.gotoTop();
          ready = true;

          try {
            if (ncfile != null) ncfile.close();
            ncfile = null;
          } catch (IOException ioe) { }
        }
      });
    }

    boolean process(Object o) {
      if (!ready) return false; // unbelievable crap with combobox
      ready = false;

      String input = (String) o;
      StringTokenizer stoke = new StringTokenizer( input);
      String name = stoke.nextToken();
      String command = input.substring( name.length());

      boolean err = false;
      try {
        if (addCoords)
          ncfile = NetcdfDataset.openDataset( name, true, null);
        else
          ncfile = NetcdfDataset.openFile( name, null);

        task = new GetContentsTask(ncfile, command);
        stopButton.startProgressMonitorTask( task);

      } catch (FileNotFoundException ioe) {
        ta.setText( "Cant open "+name);
        err = true;

      } catch (Exception ioe) {
        ByteArrayOutputStream bos = new ByteArrayOutputStream( 10000);
        ioe.printStackTrace();
        ioe.printStackTrace( new PrintStream(bos));
        ta.setText( bos.toString());
        err = true;
      }

      ready = err;
      return !err;
    }

    // for calling from the outside
    boolean setNetcdfFile( NetcdfFile ncfile) {
      boolean err = false;
      try {
        task = new GetContentsTask(ncfile, "");
        stopButton.startProgressMonitorTask( task);

        eventOK = false;
        cb.setSelectedItem(ncfile.getLocation());
        eventOK = true;

      } catch (Exception ioe) {
        ByteArrayOutputStream bos = new ByteArrayOutputStream( 10000);
        ioe.printStackTrace();
        ioe.printStackTrace( new PrintStream(bos));
        ta.setText( bos.toString());
        err = true;
      }

      ready = err;
      return !err;
    }

    private class GetContentsTask extends thredds.ui.ProgressMonitorTask implements ucar.nc2.util.CancelTask {
      NetcdfFile ncfile;
      String contents, command;

      GetContentsTask(NetcdfFile ncfile, String command) {
        this.ncfile = ncfile;
        this.command = command;
      }

      public void run() {
        // LOOK: might be able to use JTextArea.read(Reader)
        ByteArrayOutputStream bos = new ByteArrayOutputStream(100000);
        PrintStream ps = new PrintStream(bos);
        try {
          NCdump.print( ncfile, command, ps, this);

        } catch (Exception e) {
          e.printStackTrace( new PrintStream(bos));
          contents = bos.toString();

          setError(e.getMessage());
          done = true;
          return;
        }


        if (cancel)
          ps.println("\n***Cancelled by User");
        contents = bos.toString();

        success = !cancel;
        done = true;
      }
    }
  }

  private class UnitsPanel extends JPanel {
    PreferencesExt prefs;
    JSplitPane split;
    UnitDatasetCheck unitDataset;
    UnitConvert unitConvert;

    UnitsPanel(PreferencesExt prefs) {
      super();
      this.prefs = prefs;
      unitDataset = new UnitDatasetCheck((PreferencesExt) prefs.node("unitDataset"));
      unitConvert = new UnitConvert((PreferencesExt) prefs.node("unitConvert"));
      split = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, unitDataset, unitConvert);
      split.setDividerLocation( prefs.getInt("splitPos", 500));
      setLayout( new BorderLayout());
      add( split, BorderLayout.CENTER);
    }

    void save() {
      prefs.putInt("splitPos", split.getDividerLocation());
      unitConvert.save();
      unitDataset.save();
    }
  }

  private class UnitDatasetCheck extends OpPanel{
     UnitDatasetCheck(PreferencesExt p) {
      super( p, "check units", "dataset:");
    }

    boolean process(Object o) {
      String command = (String) o;
      boolean err = false;

      NetcdfFile ncfile = null;
      try {
        ncfile = NetcdfDataset.openDataset( command, addCoords, null);

        ta.setText("Variables for "+command+":");
        for (Iterator iter = ncfile.getVariables().iterator(); iter.hasNext(); ) {
          VariableEnhanced vs = (VariableEnhanced) iter.next();
          String units = vs.getUnitsString();
          StringBuffer sb = new StringBuffer();
          sb.append("   "+vs.getName()+" has unit= <"+units+">");
          if (units != null)

            try {
              SimpleUnit su = SimpleUnit.factoryWithExceptions( units);
              sb.append(" unit convert = "+su.toString());
            } catch (Exception ioe) {
              sb.append(" unit convert failed ");
              sb.insert(0, "**** Fail ");
            }
          ta.appendLine(sb.toString());
        }

      } catch (FileNotFoundException ioe) {
        ta.setText("Failed to open <"+command+">");
        err = true;

      } catch (IOException ioe) {
          ioe.printStackTrace();
          err = true;
      } finally {
        try { if (ncfile != null) ncfile.close(); }
        catch (IOException ioe) { }
      }

      return !err;
    }
  }

  private class UnitConvert extends OpPanel {
    UnitConvert(PreferencesExt prefs) {
      super( prefs, "convert", "unit:", false);

      JButton compareButton = new JButton("Compare");
      compareButton.addActionListener(new ActionListener() {
        public void actionPerformed(ActionEvent e) {
          compare( cb.getSelectedItem());
        }
      });
      buttPanel.add( compareButton);

      JButton dateButton = new JButton("Date");
      dateButton.addActionListener(new ActionListener() {
        public void actionPerformed(ActionEvent e) {
          checkDate( cb.getSelectedItem());
        }
      });
      buttPanel.add( dateButton);
    }

    boolean process(Object o) {
      String command = (String) o;
      try {
        SimpleUnit su = SimpleUnit.factoryWithExceptions( command);
        ta.setText( su.toString());
        return true;

      } catch (Exception e) {

        if (Debug.isSet("Xdeveloper")) {
          ByteArrayOutputStream bos = new ByteArrayOutputStream(10000);
          e.printStackTrace(new PrintStream(bos));
          ta.setText(bos.toString());
        } else {
          ta.setText(e.getClass().getName() +":" + e.getMessage()+"\n"+command);
        }
        return false;
      }
    }

    void compare(Object o) {
      String command = (String) o;
      StringTokenizer stoke = new StringTokenizer( command);
      ArrayList list = new ArrayList();
      while( stoke.hasMoreTokens())
        list.add( stoke.nextToken());

      try {
        String unitS1 = (String) list.get(0);
        String unitS2 = (String) list.get(1);
        SimpleUnit su1 = SimpleUnit.factoryWithExceptions( unitS1);
        SimpleUnit su2 = SimpleUnit.factoryWithExceptions( unitS2);
        ta.setText( "<"+su1.toString()+"> isConvertable to <"+su2.toString()+">="+
          SimpleUnit.isCompatibleWithExceptions( unitS1, unitS2));

      } catch (Exception e) {

        if (Debug.isSet("Xdeveloper")) {
          ByteArrayOutputStream bos = new ByteArrayOutputStream(10000);
          e.printStackTrace(new PrintStream(bos));
          ta.setText(bos.toString());
        } else {
          ta.setText(e.getClass().getName() +":" + e.getMessage()+"\n"+command);
        }

      }
    }

    void checkDate(Object o) {
      String command = (String) o;

      try {
        SimpleUnit su = SimpleUnit.factory(command);
        boolean isDate = su instanceof DateUnit;
        boolean isTime = su instanceof TimeUnit;
        ta.setText( "<"+command+"> isDateUnit= "+ isDate+" isTimeUnit= "+ isTime);
        if (isDate) {
          DateUnit du = (DateUnit) su;
          ta.appendLine("\nDateUnit = "+du);
          ta.appendLine("Unit = "+du.getUnit());
          Date d = du.getStandardDate( 0);
          ta.appendLine("getStandardDateString = "+DateUnit.getStandardDateString(d));
          ta.appendLine("getDateOrigin = "+DateUnit.getStandardDateString(du.getDateOrigin()));
        }
        if (isTime) {
          TimeUnit du = (TimeUnit) su;
          ta.appendLine("\nTimeUnit = "+du);
          ta.appendLine("Unit = "+du.getUnit());
        }

      } catch (Exception e) {
        if (Debug.isSet("Xdeveloper")) {
          ByteArrayOutputStream bos = new ByteArrayOutputStream(10000);
          e.printStackTrace(new PrintStream(bos));
          ta.setText(bos.toString());
        } else {
          ta.setText(e.getClass().getName() +":" + e.getMessage()+"\n"+command);
        }
      }
    }

  }

  private class CoordSysPanel extends OpPanel {
    IndependentWindow detailWindow;
    TextHistoryPane detailTA;
    NetcdfDataset ds = null;
    CoordSysTable coordSysTable;

    CoordSysPanel(PreferencesExt p) {
      super( p, "open", "dataset:");

      coordSysTable = new CoordSysTable( prefs);
      add( coordSysTable, BorderLayout.CENTER);

      /* JButton ncmlButton = new JButton("NcML");
      ncmlButton.addActionListener(new ActionListener() {
        public void actionPerformed(ActionEvent e) {
          if ((ds != null) && (ds.getParseInfo() != null)) {
            ByteArrayOutputStream bos = new ByteArrayOutputStream(10000);
            try {
              ds.writeNcML( bos, true, null);
            } catch (IOException e1) {
              e1.printStackTrace();
            }
            detailTA.setText( bos.toString());
            detailTA.gotoTop();
            detailWindow.show();
          }
        }
      });
      buttPanel.add( ncmlButton); */

      JButton infoButton = new JButton("ParseInfo");
      infoButton.addActionListener(new ActionListener() {
        public void actionPerformed(ActionEvent e) {
          if ((ds != null) && (ds.getParseInfo() != null)) {
            detailTA.setText( ds.getParseInfo());
            detailTA.gotoTop();
            detailWindow.show();
          }
        }
      });
      buttPanel.add( infoButton);

      JButton dsButton = new JButton("DSdump");
      dsButton.addActionListener(new ActionListener() {
        public void actionPerformed(ActionEvent e) {
          if (ds != null) {
            ByteArrayOutputStream bos = new ByteArrayOutputStream(10000);
            NetcdfDataset.debugDump(new PrintStream(bos), ds);
            detailTA.setText( bos.toString());
            detailTA.gotoTop();
            detailWindow.show();
          }
        }
      });
      buttPanel.add( dsButton);

      detailTA = new TextHistoryPane();
      detailTA.setFont( new Font("Monospaced", Font.PLAIN, 12));
      detailWindow = new IndependentWindow("Dataset Parsing Details", BAMutil.getImage("netcdfUI"), new JScrollPane(detailTA));
      Rectangle bounds = (Rectangle) prefs.getBean(FRAME_SIZE, new Rectangle(200, 50, 500, 700));
      detailWindow.setBounds( bounds);
    }

    boolean process(Object o) {
      String command = (String) o;
      boolean err = false;

      ByteArrayOutputStream bos = new ByteArrayOutputStream( 10000);
      try {
        ds = NetcdfDataset.openDataset( command, addCoords, null);
        if (ds == null) {
          ta.setText( "Failed to open <"+command+">");
        } else {
          coordSysTable.setDataset( ds);
        }

      } catch (FileNotFoundException ioe) {
        ta.setText( "Failed to open <"+command+">");
        err = true;

      } catch (Exception e) {
        e.printStackTrace();
        e.printStackTrace( new PrintStream(bos));
        ta.setText( bos.toString());
        err = true;
      } finally {
        try {
          if (ds != null) ds.close();
        } catch (IOException ioe) { }
      }

      return !err;
    }

    void save() {
      coordSysTable.save();
      prefs.putBeanObject(FRAME_SIZE, detailWindow.getBounds());
      super.save();
    }

  }

  private class NcmlPanel extends OpPanel {
    //PrefPanel.Dialog writeDataDialog, saveNcmlDialog;
    //PrefPanel writeDataPP, saveNcmlPP;
    NetcdfDataset ds = null;
    String ncmlLocation = null;

    NcmlPanel(PreferencesExt p) {
      super( p, "Create NcML", "dataset:");

      JButton saveButton = new JButton("Save NcML");
      saveButton.addActionListener(new ActionListener() {
        public void actionPerformed(ActionEvent e) {
          String filename = fileChooser.chooseFilename();
          if (filename == null) return;
          doSave(ta.getText(), filename);

//          if (ncmlLocation != null)
//          ((Field.Text) saveNcmlPP.getField("filename")).setText( ncmlLocation);
 //         saveNcmlDialog.show();
        }
      });
      buttPanel.add( saveButton);

      JButton writeButton = new JButton("Write netCDF");
      writeButton.addActionListener(new ActionListener() {
        public void actionPerformed(ActionEvent e) {
          String filename = fileChooser.chooseFilename();
          if (filename == null) return;
          doWrite(ta.getText(), filename);

          // writeDataDialog.show();
        }
      });
      buttPanel.add( writeButton);

      /* writeData dialog box
      writeDataDialog = new PrefPanel.Dialog( parentFrame, true, "Write Dataset", (PreferencesExt) prefs.node("writeDataset"));
      writeDataPP = writeDataDialog.getPrefPanel();
      writeDataPP.addTextField("filename", "Write data to netcdf file", "");
      writeDataPP.finish();
      writeDataPP.addActionListener(new ActionListener() {
        public void actionPerformed(ActionEvent e) {
          String filename = ((Field.Text)writeDataPP.getField("filename")).getText();
          doWrite(ta.getText(), filename);
        }
      });
      Rectangle bounds = (Rectangle) prefs.getBean(FRAME_SIZE, new Rectangle(200, 250, 500, 200));
      writeDataDialog.setBounds( bounds);

      // saveNcML dialog box
      saveNcmlDialog = new PrefPanel.Dialog( parentFrame, true, "Save NCML", (PreferencesExt) prefs.node("saveNcml"));
      saveNcmlPP = saveNcmlDialog.getPrefPanel();
      saveNcmlPP.addTextField("filename", "Save NcML to file", "");
      saveNcmlPP.finish();
      saveNcmlPP.addActionListener(new ActionListener() {
        public void actionPerformed(ActionEvent e) {
          String filename = ((Field.Text)saveNcmlPP.getField("filename")).getText();
          doSave(ta.getText(), filename);
        }
      });
      saveNcmlDialog.setBounds( (Rectangle) prefs.getBean(FRAME_SIZE, new Rectangle(200, 250, 500, 200))); */
    }

    boolean process(Object o) {
      ncmlLocation = (String) o;
      if (ncmlLocation.endsWith(".xml") || ncmlLocation.endsWith(".ncml")) {
        if (!ncmlLocation.startsWith("http:") && !ncmlLocation.startsWith("file:"))
          ncmlLocation = "file:" + ncmlLocation;
        String text = thredds.util.IO.readURLcontents( ncmlLocation);

        ta.setText( text);
      } else {
        writeNcml( ncmlLocation);
      }
      return true;
    }

    boolean writeNcml(String location) {
      boolean err = false;

      try {
        if (ds != null) ds.close();
      } catch (IOException ioe) { }

      ByteArrayOutputStream bos = new ByteArrayOutputStream( 10000);
      try {
        ds = NetcdfDataset.openDataset( location, addCoords, null);
        if (ds == null) {
          ta.setText( "Failed to open <"+location+">");
        } else {
          ds.writeNcML( bos, null);
          ta.setText( bos.toString());
          ta.gotoTop();
        }

      } catch (FileNotFoundException ioe) {
        ta.setText( "Failed to open <"+location+">");
        err = true;

      } catch (Exception e) {
        e.printStackTrace();
        e.printStackTrace( new PrintStream(bos));
        ta.setText( bos.toString());
        err = true;
      }

      return !err;
    }

    void doWrite( String text, String filename) {
      if (debugNcmlWrite) {
        System.out.println("filename="+filename);
        System.out.println("text="+text);
      }
      try {
        ByteArrayInputStream bis = new ByteArrayInputStream( text.getBytes());
        NcMLReader reader = new NcMLReader();
        NetcdfDataset nds = reader.readNcML (bis, null);
        ucar.nc2.FileWriter.writeToFile( nds, filename);
        try { JOptionPane.showMessageDialog(this, "File successfully written"); } catch (HeadlessException e) { }

      } catch (IOException ioe) {
        try { JOptionPane.showMessageDialog(this, "ERROR: "+ioe.getMessage()); } catch (HeadlessException e) { }
        ioe.printStackTrace();
      }
      // writeDataDialog.setVisible(false);
    }

    void doSave( String text, String filename) {
      if (debugNcmlWrite) {
        System.out.println("filename="+filename);
        System.out.println("text="+text);
      }

      try {
        thredds.util.IO.writeToFile( text, new File(filename));
        try { JOptionPane.showMessageDialog(this, "File successfully written"); } catch (HeadlessException e) { }
      } catch (IOException ioe) {
        try { JOptionPane.showMessageDialog(this, "ERROR: "+ioe.getMessage()); } catch (HeadlessException e) { }
        ioe.printStackTrace();
      }
      // saveNcmlDialog.setVisible(false);
    }

    void save() {
      // prefs.putBeanObject(FRAME_SIZE, writeDataDialog.getBounds());
      // prefs.putBeanObject(FRAME_SIZE, saveNcmlDialog.getBounds());
      super.save();
    }

  }

  private class GeoGridPanel extends OpPanel {
    GeoGridTable dsTable;
    JSplitPane split;
    IndependentWindow viewerWindow;
    GridUI gridUI = null;

    NetcdfDataset ds = null;
    GridDataset gridDataset;

    GeoGridPanel(PreferencesExt prefs) {
      super( prefs, "Dataset", "dataset:");
      dsTable = new GeoGridTable( prefs, true);
      add( dsTable, BorderLayout.CENTER);

      JButton viewButton = new JButton("View");
      viewButton.addActionListener(new ActionListener() {
        public void actionPerformed(ActionEvent e) {
          if (ds != null) {
            gridDataset = new GridDataset(ds);
            if (gridUI == null) makeGridUI();
            gridUI.setDataset( gridDataset);
            viewerWindow.show();
          }
        }
      });
      buttPanel.add( viewButton);
    }

    private void makeGridUI() {
      // a little tricky to get the parent right for GridUI
      viewerWindow = new IndependentWindow("Grid Viewer", BAMutil.getImage("netcdfUI"));

      gridUI = new GridUI((PreferencesExt) prefs.node("GridUI"), viewerWindow, fileChooser, 800);
      gridUI.addMapBean( new thredds.viewer.gis.worldmap.WorldMapBean());
      gridUI.addMapBean( new thredds.viewer.gis.shapefile.ShapeFileBean("WorldDetailMap", "Global Detailed Map", "WorldDetailMap", "/resourcesOptional/maps/Countries.zip"));
      gridUI.addMapBean( new thredds.viewer.gis.shapefile.ShapeFileBean("USDetailMap", "US Detailed Map", "USMap", "/resourcesOptional/maps/US.zip"));

      viewerWindow.setComponent( gridUI);
      viewerWindow.setBounds( (Rectangle) mainPrefs.getBean(GRIDVIEW_FRAME_SIZE, new Rectangle(77, 22, 700, 900)));
    }

    boolean process(Object o) {
      String command = (String) o;
      boolean err = false;

      NetcdfDataset newds;
      ByteArrayOutputStream bos = new ByteArrayOutputStream( 10000);
      try {
        newds = NetcdfDataset.openDataset( command, addCoords, null);
        if (newds == null) {
          JOptionPane.showMessageDialog(null, "NetcdfDataset.open cant open "+command);
          return false;
        }
        setDataset( newds);

      } catch (FileNotFoundException ioe) {
        JOptionPane.showMessageDialog(null, "NetcdfDataset.open cant open "+command);
        err = true;

      } catch (IOException ioe) {
        ioe.printStackTrace();
        ioe.printStackTrace( new PrintStream(bos));
        ta.setText( bos.toString());
        err = true;
      }

      return !err;
    }

    void setDataset( NetcdfDataset newds) {
      if (newds == null) return;
      try {
        if (ds != null) ds.close();
      } catch (IOException ioe) { }

      this.ds = newds;
      dsTable.setDataset( newds);
      eventOK = false;
      cb.setSelectedItem( newds.getLocation());
      eventOK = true;
    }

    void save() {
      super.save();
      dsTable.save();
      if (gridUI != null) gridUI.storePersistentData();
      if (viewerWindow != null) mainPrefs.putBeanObject(GRIDVIEW_FRAME_SIZE, viewerWindow.getBounds());
    }
  }

  private class ViewerPanel extends OpPanel {
    DatasetViewer dsViewer;
    JSplitPane split;
    NetcdfDataset ncfile = null;

    ViewerPanel(PreferencesExt dbPrefs) {
      super( dbPrefs, "Open", "dataset:");
      dsViewer = new DatasetViewer( dbPrefs);
      add( dsViewer, BorderLayout.CENTER);

      AbstractAction dumpAction = new AbstractAction() {
        public void actionPerformed(ActionEvent e) {
          NetcdfFile ds = dsViewer.getDataset();
          if (ds != null) {
            if (ncdumpPanel == null) {
              makeComponent("NCDump");
            }
            ncdumpPanel.setNetcdfFile(ds);
            tabbedPane.setSelectedComponent( ncdumpPanel);
          }
        }
      };
      BAMutil.setActionProperties( dumpAction, "Dump", "NCDump", false, 'D', -1);
      BAMutil.addActionToContainer(buttPanel, dumpAction);
    }

    boolean process(Object o) {
      String command = (String) o;
      boolean err = false;

      try {
        if (ncfile != null) ncfile.close();
      } catch (IOException ioe) { }

      try {
        NetcdfDataset ncnew = openDataset( command, addCoords, null);
        if (ncnew != null)
          setDataset( ncnew);

      } catch (Exception ioe) {
        ioe.printStackTrace();
        ByteArrayOutputStream bos = new ByteArrayOutputStream( 10000);
        ioe.printStackTrace( new PrintStream(bos));
        ta.setText( bos.toString());
        err = true;
      }

      return !err;
    }

    void setDataset(NetcdfDataset nc) {
      try {
        if (ncfile != null) ncfile.close();
      } catch (IOException ioe) { }
      ncfile = nc;

      if (ncfile != null) {
        dsViewer.setDataset(nc);
      }
    }


    void save() {
      super.save();
      dsViewer.save();
    }

  }

  private class StationTablePanel extends OpPanel {
    StationObsViewer stnTable;
    JSplitPane split;
    StationDatasetFactory stnDatasetFactory = null;
    StationObsDataset stationDataset = null;

    StationTablePanel(PreferencesExt dbPrefs) {
      super( dbPrefs, "Station", "dataset:");
      stnTable = new StationObsViewer( dbPrefs);

      stnTable.addMapBean( new thredds.viewer.gis.worldmap.WorldMapBean());
      stnTable.addMapBean( new thredds.viewer.gis.shapefile.ShapeFileBean("WorldDetailMap", "Global Detailed Map", "WorldDetailMap", "/resourcesOptional/maps/Countries.zip"));
      stnTable.addMapBean( new thredds.viewer.gis.shapefile.ShapeFileBean("USDetailMap", "US Detailed Map", "USMap", "/resourcesOptional/maps/US.zip"));

      add( stnTable, BorderLayout.CENTER);
    }

    boolean process(Object o) {
      String location = (String) o;
      return setStationObsDataset( location);
    }

    void save() {
      super.save();
      stnTable.save();
    }

    boolean setStationObsDataset( String location) {
      if (location == null) return false;

      try {
        if (stationDataset != null) stationDataset.close();
      } catch (IOException ioe) { }

      ByteArrayOutputStream bos = new ByteArrayOutputStream( 10000);
      try {
        if (stnDatasetFactory == null) stnDatasetFactory = new StationDatasetFactory();
        stationDataset = stnDatasetFactory.open( location);
        if (stationDataset == null)
          return false;

        stnTable.setDataset( stationDataset);
        eventOK = false;
        cb.setSelectedItem( location);
        eventOK = true;
        return true;

      } catch (IOException ioe) {
        ioe.printStackTrace();
        ioe.printStackTrace( new PrintStream(bos));
        ta.setText( stnDatasetFactory.getErrorMessages());
        ta.appendLine( bos.toString());
        return false;
      }
    }

    boolean setStationObsDataset(InvAccess access) {
      if (access == null) return false;


      try {
        if (stationDataset != null) stationDataset.close();
      } catch (IOException ioe) {
      }

      try {
        if (stnDatasetFactory == null) stnDatasetFactory = new StationDatasetFactory();
        stationDataset = stnDatasetFactory.open(access);
        if (stationDataset == null)
          return false;

        stnTable.setDataset(stationDataset);
        if (stationDataset instanceof NetcdfDataset) {
          eventOK = false;
          cb.setSelectedItem( ((NetcdfDataset)stationDataset).getLocation());
          eventOK = true;
        }
        return true;

      } catch (IOException ioe) {
        ioe.printStackTrace();
        ByteArrayOutputStream bos = new ByteArrayOutputStream(10000);
        ioe.printStackTrace(new PrintStream(bos));
        ta.setText(stnDatasetFactory.getErrorMessages());
        ta.appendLine(bos.toString());
        return false;
      }
    }


  }

  private class ImagePanel extends OpPanel {
    ImageViewPanel imagePanel;
    JSplitPane split;

    ImagePanel(PreferencesExt dbPrefs) {
      super( dbPrefs, "ImagePanel", "dataset:");
      imagePanel = new ImageViewPanel();
      add( imagePanel, BorderLayout.CENTER);
    }

    boolean process(Object o) {
      String command = (String) o;

      ByteArrayOutputStream bos = new ByteArrayOutputStream( 10000);
      try {
        if (null != command)
          imagePanel.setImageFromUrl( command);

      } catch (Exception ioe) {
        ioe.printStackTrace();
        ioe.printStackTrace( new PrintStream(bos));
        // ta.setText( datasetFactory.getErrorMessages());
        ta.appendLine( bos.toString());
        return false;
      }

      return true;
    }

    void setImageLocation( String location) {
      imagePanel.setImageFromUrl( location);
      eventOK = false;
      cb.setSelectedItem( location);
      eventOK = true;
    }

  }

  /*
  private class GeotiffPanel extends OpPanel {
    GeotiffPanel(PreferencesExt p) {
      super( p, "write GeoTiff", "netcdf:");

      JButton readButton = new JButton("read geotiff");
      readButton.addActionListener(new ActionListener() {
        public void actionPerformed(ActionEvent e) {
          String item = cb.getSelectedItem().toString();
          String fname = ((String)item).trim();
          read( fname);
        }
      });
      buttPanel.add( readButton);
    }

    void process(Object o) {
      String filename = (String) o;

      GridDataset gridDs = null;
      try {
        gridDs = GridDataset.factory (filename);
        java.util.List grids = gridDs.getGrids();
        if ( grids.size() == 0 ) {
           System.out.println("No Lat/Lon grids found.");
           return;
        }

        GeoGrid grid = (GeoGrid) grids.get(0);
        ucar.ma2.Array data = grid.readYXData(0 ,0); // first time, level

        int pos = filename.lastIndexOf('/');
        String fout = "C:/data/geotiff/test"+filename.substring(pos)+".tif";
        System.out.println("write to= "+fout);
        ucar.nc2.geotiff.GeotiffWriter writer = new ucar.nc2.geotiff.GeotiffWriter(fout);
        writer.writeGrid(grid, data, true);
        read( fout);

      } catch (IOException ioe) {
        ioe.printStackTrace();

      } finally {
        try {
          if (gridDs != null) gridDs.close();
        } catch (IOException ioe) { }
      }
    }

    void read(String filename) {
      GeoTiff geotiff = null;
      try {
        geotiff = new GeoTiff(filename);
        geotiff.read();
        ta.setText( geotiff.showInfo());
      } catch (IOException ioe) {
        ioe.printStackTrace();

      } finally {
        try {
          if (geotiff != null) geotiff.close();
        } catch (IOException ioe) { }
      }
    }

  } */

/*  private class NioPanel extends OpPanel {

    NioPanel(PreferencesExt prefs) {
      super( prefs, "read NIO", "filename:");
    }

    void process(Object o) {
      String fname = (String) o;
      try {
        ucar.nc2.nio.NetcdfFile nioFile = new ucar.nc2.nio.NetcdfFile( fname);
        ta.setText( nioFile.getDebugReadInfo());
        ta.append( "--------------------------\n");
        ta.append( nioFile.toString());

        Iterator iter = nioFile.getVariableIterator();
        while (iter.hasNext()) {
          ucar.nc2.nio.Variable v = (ucar.nc2.nio.Variable) iter.next();
          v.read();
          ta.append(" "+v.getName()+" read OK\n");
        }

        nioFile.close();
      } catch (IOException ioe) {
        ta.setText( "IOException on "+fname+"\n"+ioe.getMessage());
        ioe.printStackTrace();
      }
    }
  } */

  ///////////////////////////////////////////////////////////////////////////////
  // Dynamic proxy for Debug
  private class DebugProxyHandler implements java.lang.reflect.InvocationHandler {
    public Object invoke(Object proxy, java.lang.reflect.Method method, Object[] args) throws Throwable {
      if (method.getName().equals("toString"))
        return super.toString();
      // System.out.println("proxy= "+proxy+" method = "+method+" args="+args);
      if (method.getName().equals("isSet")) {
        return new Boolean(ucar.util.prefs.ui.Debug.isSet( (String) args[0]));
      }
      if (method.getName().equals("set")) {
        ucar.util.prefs.ui.Debug.set( (String) args[0], ((Boolean) args[1]).booleanValue());
        return null;
      }
      return Boolean.FALSE;
    }
  }

  /////////////////////////////////////////////////////////////////////////////
  // About Window
  private class AboutWindow extends javax.swing.JWindow  {
    public AboutWindow() {
      super( parentFrame);

      JLabel lab1 = new JLabel(
        "<html> <body bgcolor=\"#FFECEC\"> <center>"+
        "<h1>Netcdf 2.2 Toolset</h1>" +
        "<b>"+getVersion()+"</b>" +
        "<br><i>http://www.unidata.ucar.edu/packages/netcdf-java/</i><br>" +
        "</center><br>With thanks to these <b>Open Source</b> contributers:" +
        "<ul>" +
        "<li><b>ADDE/VisAD</b>: Bill Hibbard, Don Murray, Tom Whittaker, et al (http://www.ssec.wisc.edu/~billh/visad.html)</li>" +
        "<li><b>Apache Commons HttpClient</b> library: (http://http://jakarta.apache.org/commons/httpclient//)</li>" +
        "<li><b>Apache Log4J</b> library: (http://logging.apache.org/log4j/) </li>" +
        "<li><b>JDOM</b> library: Jason Hunter, Brett McLaughlin et al (www.jdom.org)</li>" +
        "<li><b>JUnit</b> library: Erich Gamma, Kent Beck, Erik Meade, et al (http://sourceforge.net/projects/junit/)</li>" +
        "<li><b>OpenDAP Java</b> library: Nathan Potter, James Gallagher, Don Denbo, et. al.(http://opendap.org)</li>" +
       " </ul><center>Special thanks to <b>Sun Microsystems</b> (java.sun.com) for the platform on which we stand."+
       " </center></body></html> "
      );


      JPanel main = new JPanel( new BorderLayout());
      main.setBorder( new javax.swing.border.LineBorder( Color.BLACK));
      main.setBackground(new Color(0xFFECEC));

      JLabel icon = new JLabel( new ImageIcon(BAMutil.getImage( "netcdfUI")));
      icon.setOpaque(true);
      icon.setBackground(new Color(0xFFECEC));

      JLabel threddsLogo = new JLabel( Resource.getIcon( "/resources/icons/threddsLogo.png", false));
      threddsLogo.setBackground(new Color(0xFFECEC));
      threddsLogo.setOpaque(true);

      main.add(icon, BorderLayout.NORTH);
      main.add(lab1, BorderLayout.CENTER);
      main.add(threddsLogo, BorderLayout.SOUTH);
      getContentPane().add(main);
      pack();

      //show();
      java.awt.Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
      java.awt.Dimension labelSize = this.getPreferredSize();
      setLocation(screenSize.width/2 - (labelSize.width/2),
                  screenSize.height/2 - (labelSize.height/2));
      addMouseListener(new MouseAdapter() {
        public void mousePressed(MouseEvent e) {
          setVisible(false);
        }
      });
      show();
      //System.out.println("AW ok getPreferredSize="+getPreferredSize()+" screenSize="+screenSize);
    }


      private String version = null;
      private String getVersion() {

        try {
          InputStream is = thredds.util.Resource.getFileResource( "/README");
             // DataInputStream dataIS = new DataInputStream( new BufferedInputStream(ios, 20000));
          BufferedReader dataIS = new BufferedReader(new InputStreamReader(is));
          StringBuffer sbuff = new StringBuffer();
          for (int i=0; i<3; i++) {
            sbuff.append( dataIS.readLine());
            sbuff.append( "<br>");
          }
          version = sbuff.toString();
        } catch( IOException ioe) {
          ioe.printStackTrace();
          version = "version unknown";
        }

        return version;
      }
  }


  private static ToolsUI ui;
  private static PreferencesExt p;
  private static XMLStore store;
  public static void main(String args[]) {

    try {
      String prefStore = ucar.util.prefs.XMLStore.makeStandardFilename(".unidata", "NetcdfUI22.xml");
      store = ucar.util.prefs.XMLStore.createFromFile(prefStore, null);
      p = store.getPreferences();
      Debug.setStore( p.node("Debug"));
    } catch (IOException e) {
      System.out.println("XMLStore Creation failed "+e);
    }

        // put it together in a JFrame
    final JFrame frame = new JFrame("NetCDF (2.2) Tools");
    ui = new ToolsUI(p, frame);
    frame.setIconImage( BAMutil.getImage("netcdfUI"));

    frame.addWindowListener(new WindowAdapter() {
      public void windowClosing(WindowEvent e) {
        ui.save();
        Rectangle bounds = frame.getBounds();
        p.putBeanObject(FRAME_SIZE, bounds);
        try {
          store.save();
        } catch (IOException ioe) { ioe.printStackTrace(); }

        System.exit(0);
      }
    });

    //
    frame.getContentPane().add(ui);
    Rectangle bounds = (Rectangle) p.getBean(FRAME_SIZE, new Rectangle(50, 50, 800, 450));
    frame.setBounds( bounds);

    frame.pack();
    frame.setBounds( bounds);
    frame.setVisible(true);

    // load protocol for ADDE URLs
    URLStreamHandlerFactory.install();
    URLStreamHandlerFactory.register("adde", new edu.wisc.ssec.mcidas.adde.AddeURLStreamHandler());

    // set Authentication for accessing passsword protected services like PUT
    java.net.Authenticator.setDefault(new thredds.ui.UrlAuthenticatorDialog(frame));    
  }

}

/* Change History:
   $Log: ToolsUI.java,v $
   Revision 1.35  2004/12/15 00:11:46  caron
   2.2.05

   Revision 1.34  2004/12/14 15:41:03  caron
   *** empty log message ***

   Revision 1.33  2004/12/10 17:04:18  caron
   *** empty log message ***

   Revision 1.32  2004/12/07 02:43:23  caron
   *** empty log message ***

   Revision 1.31  2004/12/07 01:29:32  caron
   redo convention parsing, use _Coordinate encoding.

   Revision 1.30  2004/12/03 04:46:27  caron
   no message

   Revision 1.29  2004/12/01 05:53:43  caron
   ncml pass 2, new convention parsing

   Revision 1.28  2004/11/21 01:16:48  caron
   ncml pass 1

   Revision 1.27  2004/11/17 19:53:09  caron
   no message

   Revision 1.26  2004/11/16 02:42:56  caron
   no message

   Revision 1.25  2004/11/15 17:24:10  caron
   no message

   Revision 1.24  2004/11/07 03:00:51  caron
   *** empty log message ***

   Revision 1.23  2004/11/07 02:55:13  caron
   no message

   Revision 1.22  2004/11/04 00:40:04  caron
   no message

   Revision 1.21  2004/10/29 00:14:12  caron
   no message

   Revision 1.20  2004/10/23 21:36:12  caron
   no message

   Revision 1.19  2004/10/22 01:01:40  caron
   another round

   Revision 1.18  2004/10/19 19:45:04  caron
   misc

   Revision 1.17  2004/10/13 19:45:12  caron
   add strict NCDump

   Revision 1.16  2004/10/08 23:23:07  caron
   speed up startup

   Revision 1.15  2004/10/06 19:03:43  caron
   clean up javadoc
   change useV3 -> useRecordsAsStructure
   remove id, title, from NetcdfFile constructors
   add "in memory" NetcdfFile

   Revision 1.14  2004/09/30 20:49:07  caron
   *** empty log message ***

   Revision 1.13  2004/09/30 00:33:42  caron
   *** empty log message ***

   Revision 1.12  2004/09/28 21:37:19  caron
   *** empty log message ***

   Revision 1.11  2004/09/25 00:09:45  caron
   add images, thredds tab

   Revision 1.10  2004/09/22 13:46:39  caron
   *** empty log message ***

   Revision 1.9  2004/09/13 22:52:51  caron
   *** empty log message ***

   Revision 1.8  2004/09/09 22:47:41  caron
   station updates

   Revision 1.7  2004/08/26 17:55:09  caron
   no message

   Revision 1.6  2004/08/18 19:56:44  caron
   2.2 alpha (2)

   Revision 1.5  2004/08/17 19:20:07  caron
   2.2 alpha (2)

   Revision 1.4  2004/08/16 20:53:52  caron
   2.2 alpha (2)

   Revision 1.3  2004/07/12 23:40:19  caron
   2.2 alpha 1.0 checkin

   Revision 1.2  2004/07/06 19:28:13  caron
   pre-alpha checkin

 */