// $Id: RecordDataset.java,v 1.1 2004/12/10 19:44:49 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 station datasets by reading entire thing into memory.
 *
 * @author John Caron
 * @version $Id: RecordDataset.java,v 1.1 2004/12/10 19:44:49 caron Exp $
 */

class RecordDataset implements StationObsDataset {
  private NetcdfFile ds;
  private ArrayList stations = new ArrayList();
  private HashMap stationHash = new HashMap();
  private StationDatasetHelper helper;

  private DateUnit dateUnit;
  private Date startDate, endDate;

  private String latName, lonName, elevName, descName, timeName;

  protected void init(NetcdfFile ds) throws IOException {
    this.ds = ds;

    String recordName = ds.findAttValueIgnoreCase(null, "_StationRecordVar", null);
    String stationIdName = ds.findAttValueIgnoreCase(null, "_StationIdVar", null);
    Variable stationIdVar = ds.findVariable(recordName+"."+stationIdName);
    DataType stationIdType = stationIdVar.getDataType();

    descName = ds.findAttValueIgnoreCase(null, "_StationDescVar", null); // ok if null
    latName = ds.findAttValueIgnoreCase(null, "_StationLatVar", null);
    lonName = ds.findAttValueIgnoreCase(null, "_StationLonVar", null);
    elevName = ds.findAttValueIgnoreCase(null, "_StationElevVar", null);
    timeName = ds.findAttValueIgnoreCase(null, "_StationTimeVar", null);

    // need the time units
    Variable timeVar = ds.findVariable(timeName);
    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");
    }

    double firstDate = Double.MAX_VALUE;
    double lastDate = Double.MIN_VALUE;

    // read all the data, store in a station object
    Variable record = ds.findVariable(recordName);
    Array data = record.read();
    IndexIterator ii = data.getIndexIterator();
    while (ii.hasNext()) {
      StructureData sdata = (StructureData) ii.getObjectNext();

      String stationId;
      if ( stationIdType == DataType.INT) {
        int stationNum = sdata.getScalarInt(stationIdName);
        stationId = Integer.toString(stationNum);
      } else
        stationId = sdata.getScalarString( stationIdName).trim();

      StationImpl station =(StationImpl) stationHash.get( stationId);
      if (null == station) {
        station = new StationImpl(stationId, sdata);
        stationHash.put(stationId, station);
        stations.add(station);
      } else
        station.add(sdata);

      double dateValue = sdata.getScalarDouble(timeName);
      firstDate = Math.min( firstDate, dateValue);
      lastDate = Math.max( lastDate, dateValue);
    }

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

    helper = new StationDatasetHelper( this);
  }

  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.getNumObservations();
  }

  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 List obs = new ArrayList();
    boolean sorted = false;

    private StationImpl( String name, StructureData sdata) {
      this.name = name;
      add( sdata);

      this.lat = sdata.getScalarDouble(latName);
      this.lon = sdata.getScalarDouble(lonName);

      if (elevName != null)
        this.elev = sdata.getScalarDouble(elevName);

      if (descName != null)
        this.desc = sdata.getScalarString(descName);
    }

    private void add( StructureData sdata) {
      obs.add( new ObsImpl(this, sdata));
    }

    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 obs.size(); }

    public List getObservations() {
      if (!sorted) {
        Collections.sort(obs);
        sorted = true;
      }
      return obs;
    }

  }

  public class ObsImpl implements StationObsDataset.StationObs, Comparable {
    private Station station;
    private double dateValue;
    private StructureData sdata;

    private ObsImpl( Station station, StructureData sdata) {
      this.station = station;
      this.dateValue = sdata.getScalarDouble(timeName);
      this.sdata = sdata;
    }

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

    public StructureData getData() throws IOException {
      return sdata;
    }

    public int compareTo(Object o) {
      ObsImpl oo = (ObsImpl) o;
      return (int) (dateValue - oo.dateValue);
    }
  }

}

/* Change History:
   $Log: RecordDataset.java,v $
   Revision 1.1  2004/12/10 19:44:49  caron
   no message

   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

 */