// $Id: GribVariable.java,v 1.14 2004/12/14 15:41:02 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.iosp.grib;

import ucar.grib.Index;
import ucar.grib.Parameter;
import ucar.grib.TableLookup;
import ucar.nc2.*;

import java.util.ArrayList;
import java.util.List;

/**
 * @author john
 */
public class GribVariable {
  private String name, desc;
  private Index.GribRecord firstRecord;

  private GribHorizCoordSys hcs;
  private GribCoordSys vcs;
  private GribTimeCoordSys tcs;
  private ArrayList records = new ArrayList(); // Index.GribRecord

  private int nlevels, ntimes;
  private Index.GribRecord[] recordTracker;
  private int decimalScale = 0;
  private boolean hasVert = false;

  GribVariable (String name, String desc,  GribHorizCoordSys hcs) {
    this.name = name; // used to get unique grouping of products
    this.desc = desc;
    this.hcs = hcs;
  }

  void addProduct( Index.GribRecord record) {
    records.add( record);
    if (firstRecord == null) firstRecord = record;
  }

  List getRecords() { return records; }

  GribHorizCoordSys getHorizCoordSys() { return hcs; }
  GribCoordSys getVertCoordSys() { return vcs; }
  boolean hasVert() { return hasVert; }

  void setVertCoordSys(GribCoordSys vcs) { this.vcs = vcs; }

  public String dump() {
   StringBuffer sbuff = new StringBuffer();
   sbuff.append(name+" "+records.size()+"\n");
   for (int i = 0; i < records.size(); i++) {
     Index.GribRecord record = (Index.GribRecord) records.get(i);
     sbuff.append(" level = "+record.levelType1+ " "+ record.levelValue1);
     sbuff.append(" time = "+record.forecastTime);
     sbuff.append("\n");
   }
   return sbuff.toString();
  }

  Variable makeVariable(NetcdfFile ncfile, Group g, TableLookup lookup, boolean useDesc, GribTimeCoordSys tcs) {
    this.tcs = tcs;
    nlevels = vcs.dontUseVertical ? 1 : vcs.getNLevels();
    ntimes = tcs.getNTimes();
    decimalScale = firstRecord.decimalScale;

    String vname = NetcdfFile.createValidNetcdfObjectName(useDesc ? desc : name);
    Variable v = new Variable( ncfile, g, null, vname);
    v.setDataType( DataType.FLOAT);

    String dims = "time";
    if (!vcs.dontUseVertical) {
      dims = dims + " " + vcs.getVerticalName();
      hasVert = true;
    }

    if (hcs.isLatLon())
      dims = dims + " lat lon";
    else
      dims = dims + " y x";

    v.setDimensions( dims);
    Parameter param = lookup.getParameter( firstRecord);

    v.addAttribute( new Attribute("units", param.getUnit()));
    v.addAttribute( new Attribute("long_name", param.getDescription()+" @ "+vcs.getVerticalName()));
    v.addAttribute( new Attribute("missing_value", new Float( lookup.getFirstMissingValue())));
    if (!hcs.isLatLon()) {
      v.addAttribute( new Attribute("coordinates", "lat lon"));
      v.addAttribute( new Attribute("grid_mapping", hcs.getGridName()));
    }

   v.addAttribute( new Attribute("GRIB_param_discipline", lookup.getDisciplineName( firstRecord)));
   v.addAttribute( new Attribute("GRIB param_category", lookup.getCategoryName( firstRecord)));
   v.addAttribute( new Attribute("GRIB param_number", new Integer(param.getNumber())));
   v.addAttribute( new Attribute("GRIB_product_definition_type", lookup.getProductDefinitionName(firstRecord)));
   v.addAttribute( new Attribute("GRIB_type_of_first_fixed_surface", new Integer( firstRecord.levelType1)));
   //if (pds.getTypeSecondFixedSurface() != 255 )
   //  v.addAttribute( new Attribute("GRIB2_type_of_second_fixed_surface", pds.getTypeSecondFixedSurfaceName()));

    String coordSysName = !vcs.dontUseVertical ? vcs.getVerticalName() :
        (hcs.isLatLon() ? "latLonCoordSys" : "projectionCoordSys");
    v.addAttribute( new Attribute("_CoordinateSystems", coordSysName));

    v.setSPobject( this);

    recordTracker = new Index.GribRecord[ntimes * nlevels];
    for (int i = 0; i < records.size(); i++) {
      Index.GribRecord p = (Index.GribRecord) records.get(i);
      int level = vcs.getIndex( p);
      if (vcs.dontUseVertical && level > 0) {
        level = 0; // inconsistent level encoding ??
      }
      int time = tcs.getIndex( p);
      recordTracker[time*nlevels + level] = p;
    }

    if (GribServiceProvider.debugMissing) {
      System.out.println("GribVariable missing data for " + v.getName()+" ntimes (across)= "+ ntimes+" nlevs (down)= "+ nlevels+":");
      for (int j = 0; j < nlevels; j++) {
        System.out.print(" ");
        for (int i = 0; i < ntimes; i++) {
          boolean missing = recordTracker[i * nlevels + j] == null;
          System.out.print(missing ? "-" : "X");
        }
        System.out.println();
      }
    }
    return v;
  }

  public Index.GribRecord findRecord(int time, int level) {
    return recordTracker[time*nlevels + level];
  }

  public boolean equals(Object oo) {
   if (this == oo) return true;
   if ( !(oo instanceof GribVariable)) return false;
   return hashCode() == oo.hashCode();
  }

  public String getName() { return name; }
  public int getDecimalScale() { return decimalScale; }

  /** Override Object.hashCode() to implement equals. */
  public int hashCode() {
   if (hashCode == 0) {
     int result = 17;
     result = 37*result + name.hashCode();
     result += 37*result + firstRecord.levelType1;
     result += 37*result + hcs.getID().hashCode();
     hashCode = result;
   }
   return hashCode;
  }
  private volatile int hashCode = 0;

}

/* Change History:
   $Log: GribVariable.java,v $
   Revision 1.14  2004/12/14 15:41:02  caron
   *** empty log message ***

   Revision 1.13  2004/12/07 02:43:22  caron
   *** empty log message ***

   Revision 1.12  2004/12/01 05:53:42  caron
   ncml pass 2, new convention parsing

   Revision 1.11  2004/11/15 03:25:20  caron
   no message

   Revision 1.10  2004/11/11 23:54:13  rkambic
   level stuff

   Revision 1.9  2004/11/07 03:00:51  caron
   *** empty log message ***

   Revision 1.8  2004/11/03 20:16:21  caron
   fix missing values

   Revision 1.7  2004/10/29 00:14:11  caron
   no message

   Revision 1.6  2004/10/23 21:36:11  caron
   no message

   Revision 1.5  2004/10/13 19:45:12  caron
   add strict NCDump

   Revision 1.4  2004/10/12 02:57:06  caron
   refactor for grib1/grib2: move common functionality up to ucar.grib
   split GribServiceProvider

   Revision 1.3  2004/10/02 20:54:41  caron
   *** empty log message ***

   Revision 1.2  2004/09/30 20:49:07  caron
   *** empty log message ***

   Revision 1.1  2004/09/30 00:33:41  caron
   *** empty log message ***

*/