// $Id: UnidataStationObsDataset.java,v 1.3 2004/12/15 23:21:09 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.*;

/**
 * This handles datasets in "Unidata Point Format"
 *
 * @author John Caron
 * @version $Id: UnidataStationObsDataset.java,v 1.3 2004/12/15 23:21:09 caron Exp $
 */

public class UnidataStationObsDataset implements StationObsDataset {
  private NetcdfFile ds;
  private Structure recordVar;
  private ArrayList stations = new ArrayList();
  private HashMap stationHash = new HashMap();
  private StationDatasetHelper helper;

  private DateUnit dateUnit;
  private double startDate, endDate;

  static public boolean isValidFile(NetcdfFile ds) {
    return "Unidata Station Format v1.0".equals(ds.findAttValueIgnoreCase(null, "Conventions", null));
  }

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

    Variable timeVar = ds.findVariable("record.time_observation");
    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();
    Array numReportsArray = ds.findVariable("numReports").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),
          numReportsArray.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");
    int nobs = (int) recordVar.getSize();

    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 dateUnit.getStandardDate( startDate);
  }

  public Date getEndDate() {
    return dateUnit.getStandardDate( 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;

    private int lastRecord;
    private List obs = null;

    private StationImpl( String name, String desc, float lat, float lon, float elev, int lastRecord, int count) {
      this.name = name;
      this.desc = desc;
      this.lat = lat;
      this.lon = lon;
      this.elev = elev;
      this.lastRecord = lastRecord;
      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 = lastRecord;

      while (recno >= 0) {
        try {
          StructureData data = recordVar.readStructure(recno);
          double timeObs = data.getScalarDouble("time_observation");
          int prevRecord = data.getScalarInt("prevReport");

          obs.add( 0, new ObsImpl( this, timeObs, recno));
          recno = prevRecord;
        }
        catch (ucar.ma2.InvalidRangeException e) {
          e.printStackTrace();
          throw new IOException( e.getMessage());
        }
      }

      return obs;
    }

  }

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

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

    public Station getStation() { return station; }
    public Date getTime() {
      return dateUnit.getStandardDate( 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: UnidataStationObsDataset.java,v $
   Revision 1.3  2004/12/15 23:21:09  caron
   dont depend on record dimension name

   Revision 1.2  2004/09/28 21:32:18  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

 */