// $Id: MadisStationObsDataset.java,v 1.1 2004/09/28 21:32: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.*;

/**
 * This might actually be ok for any AWIPS station file ??
 *
 * @author John Caron
 * @version $Id: MadisStationObsDataset.java,v 1.1 2004/09/28 21:32:17 caron Exp $
 */

public class MadisStationObsDataset 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) {
    if (ds.findVariable("staticIds") == null) return false;
    if (ds.findVariable("nStaticIds") == null) return false;
    if (ds.findVariable("lastRecord") == null) return false;

    if (ds.findVariable("record") == null) return false;
    if (ds.findVariable("record.timeObs") == null) return false;

    return true;
  }

  public MadisStationObsDataset(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 non-record info
    Variable stationNameVar = ds.findVariable("staticIds");
    Variable inventoryVar = ds.findVariable("inventory");
    Variable lastRecordVar = ds.findVariable("lastRecord");

    Array inventory = inventoryVar.read();
    Array lastRecord = lastRecordVar.read();
    Index ima = lastRecord.getIndex();

    Variable v = ds.findVariable("nStaticIds");
    int n = v.readScalarInt();
    ArrayChar stationNames = (ArrayChar) stationNameVar.read();
    for (int i = 0; i<n; i++) {
      String stationName = stationNames.getString(i).trim();
      int invValue = inventory.getInt(ima.set(i));
      int lastValue = lastRecord.getInt(ima);

      StationImpl bean = new StationImpl(stationName, invValue, lastValue);
      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");
    Variable locationVar = recordVar.findVariable("locationName");

    ucar.nc2.Dimension nrecs = ds.findDimension("recNum");
    // System.out.println("total beans " + nrecs.getLength());

    startDate = Double.MAX_VALUE;
    endDate = Double.MIN_VALUE;
    for (int i=0; i<nrecs.getLength(); i++) {
      StructureData data = null;
      try {
        data = recordVar.readStructure(i);
        // System.out.println(" read recrd " + i);
      } catch (ucar.ma2.InvalidRangeException e) {
        e.printStackTrace();
        throw new IOException( e.getMessage());
      }

      String stnName = data.getScalarString("stationName");
      StationImpl station = (StationImpl) stationHash.get(stnName.trim());
      if (station != null) {
        if (locationVar != null)
          station.desc = data.getScalarString("locationName");
        station.lat = data.getScalarFloat("latitude");
        station.lon = data.getScalarFloat("longitude");
        station.elev = data.getScalarFloat("elevation");

        // get time range
        double timeObs = data.getScalarDouble("timeObs");
        startDate = Math.min( startDate, timeObs);
        endDate = Math.max( endDate, timeObs);

        station.count++;
      } else
        System.out.println(" cant find station "+stnName);
    }

    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.obs == null)  ? -1 : si.obs.size();
  }

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

  /* public int readScalarInt( String name) throws IOException {
    Variable v = ds.findVariable(name);
    return v.readScalarInt();
  } */

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

    private int inventory, lastRecord;
    private List obs;

    private StationImpl( String name, int inventory, int lastRecord) {
      this.name = name;
      this.inventory = inventory;
      this.lastRecord = lastRecord;
    }

    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("timeObs");
          int prevRecord = data.getScalarInt("prevRecord");

          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: MadisStationObsDataset.java,v $
   Revision 1.1  2004/09/28 21:32:17  caron
   *** empty log message ***

   Revision 1.5  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

 */