// $Id: GridDataset.java,v 1.3 2004/11/07 03:00:50 caron Exp $
/*
 * Copyright 1997-2000 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.dataset.grid;

import ucar.nc2.dataset.*;
import java.util.*;

/**
 * Make a NetcdfDataset into a collection of GeoGrids with Georeferencing coordinate systems.
 *
 * <p>
 * A variable will be made into a GeoGrid if it has a Georeferencing coordinate system,
 *   using GridCoordSys.isGridCoordSys(), and it has no extra dimensions, ie
 *   GridCoordSys.isComplete( var) is true.
 * If it has multiple Georeferencing coordinate systems, any one that is a product set will be given preference.
 *
 * Example:
 *
 * <pre>
    GridDataset gridDs = GridDataset.factory (uriString);
    List grids = gridDs.getGrids();
    for (int i=0; i&lt;grids.size(); i++) {
      GeoGrid grid = (Geogrid) grids.get(i);
    }
   </pre>
 *
 * @author caron
 * @version $Revision: 1.3 $ $Date: 2004/11/07 03:00:50 $
 */

public class GridDataset {
  private NetcdfDataset ds;
  private ArrayList gcsList = new ArrayList(); // GeoCoordSys
  private ArrayList grids = new ArrayList();  // GeoGrid
  private HashMap gridsetHash = new HashMap();

  /**
   * Open a netcdf dataset, parse Conventions, find all the geoGrids, return a GridDataset.
   *
   * @param netcdfFileURI netcdf dataset to open. May have a dods:, http: or file: prefix,
   *  or just a local filename. If it ends with ".xml", its assumed to be a NetcdfDataset Definition XML file
   * @return GridDataset
   * @throws java.io.IOException
   * @see ucar.nc2.dataset.NetcdfDataset#open
   */
  static public GridDataset open( String netcdfFileURI) throws java.io.IOException {
    NetcdfDataset ds = ucar.nc2.dataset.NetcdfDataset.openDataset( netcdfFileURI, true, null);
    return new GridDataset(ds);
  }

  /**
   * Create a GridDataset from a NetcdfDataset.
   * @param ds underlying NetcdfDataset.
   */
  public GridDataset( NetcdfDataset ds) {
    this.ds = ds;

    // look for geoGrids
    parseInfo.append("GridDataset look for GeoGrids\n");
    List vars  = ds.getVariables();
    for (int i=0; i< vars.size(); i++) {
      VariableEnhanced varDS = (VariableEnhanced) vars.get(i);
      constructCoordinateSystems( ds, varDS);
    }

  }

  private void constructCoordinateSystems(NetcdfDataset ds, VariableEnhanced v) {

      if (v instanceof StructureDS) {
        StructureDS s = (StructureDS) v;
        List members = s.getVariables();
        for (int i = 0; i < members.size(); i++) {
          VariableEnhanced nested =  (VariableEnhanced) members.get(i);
          // LOOK flatten here ??
          constructCoordinateSystems( ds, nested);
        }
      } else {

        // see if it has a GridCS
        // LOOK: should add geogrid it multiple times if there are multiple geoCS ??
        GridCoordSys gcs = null;
        List csys  = v.getCoordinateSystems();
        for (int j=0; j< csys.size(); j++) {
          CoordinateSystem cs = (CoordinateSystem) csys.get(j);
          GridCoordSys gcsTry = GridCoordSys.makeGridCoordSys( parseInfo, cs, v);
          if (gcsTry != null) {
            gcs = gcsTry;
            if (gcsTry.isProductSet()) break;
          }
        }

        if (gcs != null)
          addGeoGrid( v, gcs);
        }

   }

  /** Close all resources associated with this dataset. */
  public void close() throws java.io.IOException {
    ds.close();
  }


  private void addGeoGrid( VariableEnhanced varDS, GridCoordSys gcs) {
    Gridset gridset;
    if (null == (gridset = (Gridset) gridsetHash.get( gcs.getName()))) {
      gridset = new Gridset( gcs);
      gridsetHash.put( gcs.getName(), gridset);
      gcs.makeVerticalTransform( this); // delay this till we know we need it
    }

    GeoGrid geogrid = new GeoGrid( this, varDS, gridset.gcc);
    grids.add( geogrid);
    gridset.add( geogrid);
  }

    /** the name of the dataset */
  public String getName() { return ds.getLocation(); }
    /** the underlying NetcdfDataset */
  public NetcdfDataset getNetcdfDataset() { return ds; }
    /** get the list of GeoGrid objects contained in this dataset. */
  public List getGrids(){ return grids; }
    /**
     * Return GeoGrid objects grouped by GridCoordSys. All GeoGrids in a Gridset
     *   have the same GridCoordSys.
     * @return Collection of type GridDataset.Gridset
     */
  public Collection getGridSets(){ return gridsetHash.values(); }

  /** find the named GeoGrid. */
  public GeoGrid findGridByName( String name) {
    Iterator iter = getGrids().iterator();
    while (iter.hasNext()) {
      GeoGrid ggi = (GeoGrid) iter.next();
      if (name.equals( ggi.getName()))
        return ggi;
    }
    return null;
  }

  protected StringBuffer buf = new StringBuffer(2000);
  private StringBuffer bufline = new StringBuffer(80);

    /** Show Grids and coordinate systems. */
  public String getInfo() {
    int countGridset = 0;
    buf.setLength(0);

    Iterator gsets = gridsetHash.values().iterator();
    while (gsets.hasNext()) {
      Gridset gs = (Gridset) gsets.next();

      buf.append("\nGridset "+ countGridset+" coordSys "+gs.getGeoCoordSys()+"\n");
      buf.append("Name___________Unit___________hasMissing_____Description\n");
      Iterator grids = gs.getGrids().iterator();
      while (grids.hasNext()) {
        GeoGrid grid = (GeoGrid) grids.next();
        buf.append(grid.getInfo());
        buf.append("\n");
      }
      countGridset++;
      buf.append("\n");
    }

    buf.append("\nGeoReferencing Coordinate Axes\n");
    buf.append(   "Name___________Len__Unit________________Type___Description\n");
    Iterator iter = ds.getCoordinateAxes().iterator();
    while (iter.hasNext()) {
      CoordinateAxis axis = (CoordinateAxis) iter.next();
      if (axis.getAxisType() == null) continue;
      buf.append( axis.getInfo());
      buf.append( "\n");
    }
    return buf.toString();
  }

  private StringBuffer parseInfo = new StringBuffer(); // debugging
  StringBuffer parseInfo() { return parseInfo; }

    /** Get Details about the dataset. */
  public String getDetailInfo() {
    StringBuffer buff = new StringBuffer(5000);
    buff.append( ds.toString());
    buff.append( "\n\n----------------------------------------------------\n");
    buff.append( getInfo());
    buff.append( "\n\n----------------------------------------------------\n");
    buff.append( ds.getParseInfo());
    buff.append( "\n\n----------------------------------------------------\n");
    buff.append( parseInfo.toString());

    return buff.toString();
  }


    /** Debugging info about the dataset.
  public abstract String getDebugInfo();


    /** Iterator returns ucar.grid.CoordAxisImpl. CHANGE TO GENERIC
  public Iterator getAxes(){ return coordAxes.iterator(); }

  public String toString() { return name; }

    /// extra services
    /** iterator returns ucar.grid.GeoGridImpl.Gridset.  CHANGE TO GENERIC
  public Iterator getGridsets(){
    if (null == gridsets) makeGridsets();
    return gridsets.values().iterator();
  }

    /** find the named GeoGrid.
  public GeoGridImpl getGridByName( String name) {
    Iterator iter = getGrids();
    while (iter.hasNext()) {
      GeoGridImpl ggi = (GeoGridImpl) iter.next();
      if (name.equals( ggi.getName()))
        return ggi;
    }
    return null;
  }

    /** find the named GeoGrid.
  public GeoGridImpl getGridByStandardName( String name) {
    Iterator iter = getGrids();
    while (iter.hasNext()) {
      GeoGridImpl ggi = (GeoGridImpl) iter.next();
      thredds.catalog.StandardQuantity sq = ggi.getStandardQuantity();
      if ((sq != null) && name.equals( sq.getName()))
        return ggi;
    }
    return null;
  } */

    //// methods used by the data providers to construct the Dataset

  /** Add a GeoGrid. If g is not a GeoGridImpl, it is wrapped by a GeoGridAdapter to
   * make it into one. This allows us to assume GeoGridImpl without loss of generality.
   *
  public void addGrid( GeoGrid g) {
    if (g instanceof GeoGridImpl)
      grids.add(g);
    else
      grids.add( new GeoGridAdapter( g));
  }

    // construct the gridsets : sets of grids with the same coord.sys
  public void makeGridsets() {
    gridsets = new java.util.HashMap();
    coordAxes = new java.util.HashSet();

    Iterator iter = getGrids();
    while (iter.hasNext()) {
      GeoGridImpl ggi = (GeoGridImpl) iter.next();
      GeoCoordSysImpl gcc = ggi.getGeoCoordSysImpl();
      Gridset gset;
      if (null == (gset = (Gridset) gridsets.get( gcc))) { // problem with equals() ?
        gset = new Gridset(gcc);   // create new gridset with this coordsys
        gridsets.put( gcc, gset);
      }
      gset.add(ggi);

      // also track all coord axes: used in toString().
      if (gcc.getXaxis() != null)
        coordAxes.add( gcc.getXaxis());
      if (gcc.getYaxis() != null)
        coordAxes.add( gcc.getYaxis());
      if (gcc.getZaxis() != null)
        coordAxes.add( gcc.getZaxis());
      if (gcc.getTaxis() != null)
        coordAxes.add( gcc.getTaxis());
    }
  } */



  /**
   * This is a set of GeoGrids with the same GeoCoordSys.
   */
  public class Gridset {

    private GridCoordSys gcc;
    private ArrayList grids = new ArrayList();

    private Gridset ( GridCoordSys gcc) { this.gcc = gcc; }
    private void add( GeoGrid grid) { grids.add( grid); }

    /** Get list of GeoGrid objects */
    public List getGrids() { return grids; }

    /** all GeoGrids point to this GeoCoordSysImpl */
    public GridCoordSys getGeoCoordSys() { return gcc; }
  }

  /** testing */
  public static void main( String arg[]) {
    String filename = "C:/data/conventions/wrf/wrfbdy_d01.nc";
    try {
      GridDataset gridDs = GridDataset.open (filename);
      System.out.println(gridDs.getDetailInfo());
    } catch (Exception ioe) {
      System.out.println("error = "+arg[0]);
      ioe.printStackTrace();
    }
  }



}


/**
 * $Log: GridDataset.java,v $
 * Revision 1.3  2004/11/07 03:00:50  caron
 * *** empty log message ***
 *
 * Revision 1.2  2004/10/06 19:03:41  caron
 * clean up javadoc
 * change useV3 -> useRecordsAsStructure
 * remove id, title, from NetcdfFile constructors
 * add "in memory" NetcdfFile
 *
 * Revision 1.1  2004/08/16 20:53:50  caron
 * 2.2 alpha (2)
 *
 * Revision 1.10  2003/07/14 22:56:40  caron
 * add parseInfo diagnostics
 *
 * Revision 1.9  2003/07/12 22:09:07  caron
 * add vertical transformations
 *
 * Revision 1.8  2003/06/24 22:03:10  caron
 * grids must have complete coordainet system
 *
 * Revision 1.7  2003/06/09 15:23:16  caron
 * add nc2.ui
 *
 * Revision 1.6  2003/06/03 20:06:12  caron
 * fix javadocs
 *
 * Revision 1.5  2003/05/30 19:50:13  caron
 * only list georeferencing axes in getInfo()
 *
 * Revision 1.4  2003/05/23 18:51:28  caron
 * add getParseInfo()
 *
 * Revision 1.3  2003/05/15 23:10:42  caron
 * add GridDataset.factory
 *
 * Revision 1.2  2003/04/10 19:21:57  caron
 * add command line getInfo()
 *
 * Revision 1.1  2003/04/08 15:06:29  caron
 * nc2 version 2.1
 *
 * Revision 1.1  2002/12/13 00:53:09  caron
 * pass 2
 *
 * Revision 1.1.1.1  2002/02/26 17:24:42  caron
 * import sources
 *
 * Revision 1.9  2001/05/10 18:48:58  caron
 * add StandardQuantity
 *
 * Revision 1.8  2001/02/06 22:30:30  caron
 * add  getAxes()
 *
 * Revision 1.7  2001/01/08 22:24:51  caron
 * more better
 *
 * Revision 1.6  2000/12/14 20:48:04  caron
 * tweaks for new datasets
 *
 * Revision 1.5  2000/10/03 21:10:12  caron
 * add CoordAxisVert, better javadoc
 *
 */