// $Id: CoardsStationObsDataset.java,v 1.2 2004/12/10 17:04:17 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.station;

import ucar.ma2.*;
import ucar.nc2.*;
import ucar.nc2.units.SimpleUnit;
import ucar.nc2.units.DateUnit;
import ucar.unidata.geoloc.LatLonRect;

import java.io.*;
import java.util.*;

/**
 * COARDS formated station data, from National Data Buoy Center
 *   http://www.ndbc.noaa.gov/index.shtml
 *
 *
 * @author John Caron
 * @version $Id: CoardsStationObsDataset.java,v 1.2 2004/12/10 17:04:17 caron Exp $
 */

public class CoardsStationObsDataset implements StationObsDataset {
  // the local netcdf case
  private Structure recordVar;
  private ArrayInt.D1 dates;

  // the DODS server case

  // all cases
  private NetcdfFile ds;
  private ArrayList stations = new ArrayList();
  // private HashMap stationHash = new HashMap();
  private StationDatasetHelper helper;

  private DateUnit dateUnit;
  private Date startDate, endDate;

  static public boolean isValidFile(NetcdfFile ds) {
    String conventions = ds.findAttValueIgnoreCase(null, "Conventions", null);
    if ((conventions == null) || !conventions.equalsIgnoreCase("COARDS")) return false;
    if (null == ds.findAttValueIgnoreCase(null, "station", null)) return false;
    if (null == ds.findAttValueIgnoreCase(null, "location", null)) return false;

    if (ds.findVariable("lat") == null) return false;
    if (ds.findVariable("lon") == null) return false;

    return true;
  }

  public CoardsStationObsDataset(NetcdfFile ds) throws IOException {
    this.ds = ds;

    Variable v = ds.findVariable("record");
    if ((v != null) && (v instanceof Structure)) {
      initRecordCase((Structure) v);
    }

  }

  private void initRecordCase(Structure v) throws IOException {
    recordVar = v;

    Variable latVar = ds.findVariable("lat");
    double lat = latVar.readScalarDouble();
    Variable lonVar = ds.findVariable("lat");
    double lon = lonVar.readScalarDouble();

    Variable dateVar = ds.findVariable("record.time");
    String dateUnits = ds.findAttValueIgnoreCase(dateVar, "units", null);
    dateUnit = (DateUnit) SimpleUnit.factory( dateUnits);

    dateVar.setCaching(true);
    Array data = null;
    try {
      data = dateVar.readAllStructures( null, true);
    } catch (InvalidRangeException e) {
      e.printStackTrace();  // cant happen
    }
    dates = (ArrayInt.D1) data;
    int firstDate = dates.get(0);
    int lastDate = dates.get( (int)dates.getSize()-1);

    startDate = dateUnit.getStandardDate( (double) firstDate);
    endDate = dateUnit.getStandardDate( (double) lastDate);

    String name = ds.findAttValueIgnoreCase(null, "station", null);
    String desc = ds.findAttValueIgnoreCase(null, "description", null);

    stations.add( new StationImpl( name, desc, lat, lon,  -999.0, (int) recordVar.getSize()));
  }

  /* public void initNonRecordCase(NetcdfFile ds) throws IOException {
    this.ds = ds;

    Variable timeVar = ds.findVariable("record.timeObs");
    String timeUnitString = ds.findAttValueIgnoreCase(timeVar, "units", "seconds since 1970-01-01");
    try {
      dateUnit = (DateUnit) SimpleUnit.factoryWithExceptions(timeUnitString);
    } catch (Exception e) {
      System.out.println("Error on string = "+timeUnitString+" == "+e.getMessage());
      dateUnit = (DateUnit) SimpleUnit.factory("seconds since 1970-01-01");
    }

    // get the station info
    ArrayChar stationIdArray = (ArrayChar) ds.findVariable("id").read();
    ArrayChar stationDescArray = (ArrayChar) ds.findVariable("location").read();
    Array latArray = ds.findVariable("latitude").read();
    Array lonArray = ds.findVariable("longitude").read();
    Array elevArray = ds.findVariable("elevation").read();
    Array lastRecordArray = ds.findVariable("lastReport").read();
    Index ima = lastRecordArray.getIndex();

    int n = ds.findVariable("num_stations").readScalarInt();
    for (int i = 0; i<n; i++) {
      ima.set(i);

      String stationName = stationIdArray.getString(i).trim();
      String stationDesc = stationDescArray.getString(i).trim();

      StationImpl bean = new StationImpl(stationName, stationDesc,
          latArray.getFloat(ima),
          lonArray.getFloat(ima),
          elevArray.getFloat(ima),
          lastRecordArray.getInt(ima));

      stationHash.put(stationName, bean);
      stations.add(bean);
      // System.out.println(" read " + stationName);
    }
    // System.out.println("total stations " + n);

    // get the record info
    recordVar = (Structure) ds.findVariable("record");
    ucar.nc2.Dimension nrecs = ds.findDimension("recNum");
    int nobs = nrecs.getLength();

    try {
      StructureData data = recordVar.readStructure(0);
      startDate = data.getScalarInt("time_observation");
      data = data = recordVar.readStructure(nobs-1);
      endDate = data.getScalarInt("time_observation");
    } catch (InvalidRangeException e) {
      e.printStackTrace();
    }

    helper = new StationDatasetHelper( this);
  } */

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

  public Date getStartDate() {
    return startDate;
  }

  public Date getEndDate() {
    return endDate;
  }

  public LatLonRect getBoundingBox() {
    return helper.getBoundingBox();
  }

  public List getStations() { return stations; }

  public List getStations(LatLonRect boundingBox) throws IOException {
    return helper.getStations(boundingBox);
  }

  public StationObsDataset.Station getStation(String id) {
    return helper.getStation(id);
  }

  public int getStationObsCount(StationObsDataset.Station s) {
    StationImpl si = (StationImpl)s;
    return si.count;
  }

  public List getStationObs(StationObsDataset.Station s, Date start, Date end) throws IOException {
    return helper.getStationObs(s, start, end);
  }

  public List getStationObs(List stations) throws IOException {
    return helper.getStationObs(stations);
  }

  public List getStationObs(List stations, Date start, Date end) throws IOException {
    return helper.getStationObs(stations, start, end);
  }

  public List getStationObs(LatLonRect boundingBox) throws IOException {
    return helper.getStationObs(boundingBox);
  }

  public List getStationObs(LatLonRect boundingBox, Date start, Date end) throws IOException {
    return helper.getStationObs(boundingBox, start, end);
  }

  public void sortByTime(List stationObs) {
    helper.sortByTime(stationObs);
  }

  public List getStationObs( Station s) throws IOException {
    return ((StationImpl)s).getObservations();
  }

  private class StationImpl implements StationObsDataset.Station {
    private String name, desc;
    private double lat, lon, elev;
    private int count;
    List obs = null;

    private StationImpl( String name, String desc, double lat, double lon, double elev, int count) {
      this.name = name;
      this.desc = desc;
      this.lat = lat;
      this.lon = lon;
      this.elev = elev;
      this.count = count;
    }

    public String getName() { return name; }
    public String getDescription() { return desc; }
    public double getLatitude() { return lat; }
    public double getLongitude() { return lon; }
    public double getElevation() { return elev; }

    public int getNumObservations() { return count; }

    public List getObservations() throws IOException {
      if (obs == null)
        obs = findObservations();
      return obs;
    }

    private List findObservations() throws IOException {
      ArrayList obs = new ArrayList();
      int recno = 0;

      while (recno < count) {
        obs.add( 0, new ObsImpl( this, dates.get(recno), recno));
        recno++;
      }

      return obs;
    }

  }

  public class ObsImpl implements StationObsDataset.StationObs {
    private Station station;
    private int dateValue;
    private int recno;

    private ObsImpl( Station station, int dateValue, int recno) {
      this.station = station;
      this.dateValue = dateValue;
      this.recno = recno;
    }

    public Station getStation() { return station; }
    public Date getTime() {
      return dateUnit.getStandardDate( (double) dateValue);
    }

    public StructureData getData() throws IOException {
      try {
        return (StructureData) recordVar.readStructure(recno);
      }
      catch (ucar.ma2.InvalidRangeException e) {
        e.printStackTrace();
        throw new IOException ( e.getMessage());
      }
    }
  }

}

/* Change History:
   $Log: CoardsStationObsDataset.java,v $
   Revision 1.2  2004/12/10 17:04:17  caron
   *** empty log message ***

   Revision 1.1  2004/09/28 21:32:17  caron
   *** empty log message ***

   Revision 1.1  2004/09/22 13:46:38  caron
   *** empty log message ***

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

   Revision 1.3  2004/08/26 17:55:08  caron
   no message

   Revision 1.2  2004/07/12 23:40:18  caron
   2.2 alpha 1.0 checkin

   Revision 1.1  2004/07/06 19:31:59  caron
   pre-alpha checkin

   Revision 1.3  2003/10/28 23:57:21  caron
   minor

   Revision 1.2  2003/10/02 20:33:56  caron
   move SimpleUnit to dataset; add <units> tag; add projections to CF

   Revision 1.1  2003/06/09 15:23:17  caron
   add nc2.ui

   Revision 1.1  2003/04/08 18:19:11  john
   nc2.ui

 */