// $Id: Nidsheader.java,v 1.6 2004/12/09 00:17:32 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, strlenwrite to the Free Software Foundation,
 * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
 */
package ucar.nc2.iosp.nids;

import ucar.ma2.*;
import ucar.nc2.Attribute;
import ucar.nc2.DataType;
import ucar.nc2.*;
import ucar.nc2.units.DateUnit;
import ucar.unidata.io.*;

import java.io.*;
import java.io.RandomAccessFile;
import java.nio.*;
import java.util.*;
import java.util.zip.*;

class Nidsheader{
  final static int  NEXR_PID_READ = 100;
  final static int  DEF_NUM_ELEMS = 640;   /* default num of elements to send         */
  final static int  DEF_NUM_LINES = 480;   /* default num of lines to send            */
  final static int  NEXR_FILE_READ = -1;   /* # flag to read entire NIDS file         */
  final static int  NEXR_DIR_READ = 356 ;  /* just enough bytes for NIDS directory    */
  final static int  READ_BUFFER_SIZE = 1;   /* # of image lines to buffer on read      */
  final static int  ZLIB_BUF_LEN = 4000;   /* max size of an uncompressed ZLIB buffer */
  byte Z_DEFLATED  = 8;
  byte DEF_WBITS  = 15;
  final static int   Other = 0;
  final static int   Base_Reflect = 1;
  final static int   Velocity = 2;
  final static int   Comp_Reflect = 3;
  final static int   Layer_Reflect_Avg = 4;
  final static int   Layer_Reflect_Max = 5;
  final static int   Echo_Tops = 6;
  final static int   Vert_Liquid = 7;
  final static int   Precip_1 = 8;
  final static int   Precip_3 = 9;
  final static int   Precip_Accum = 10;
  final static int   Precip_Array = 11;
  final static int   BaseReflect248 = 12;
  final static int   StrmRelMeanVel = 13;
  final static int   VAD = 14;
  // message header block
  short mcode = 0;
  short mdate = 0;
  int mtime = 0;
  int mlength = 0;
  short msource = 0;
  short mdestId = 0;
  short mNumOfBlock = 0;
  // production dessciption block
  short divider = 0;
  double latitude = 0.0;
  double longitude = 0.0;
  short height = 0;
  short pcode = 0;
  short opmode = 0;
  short volumnScanPattern = 0;
  short sequenceNumber = 0;
  short volumeScanNumber = 0;
  short volumeScanDate = 0;
  int volumeScanTime = 0;
  short productDate = 0;
  int productTime = 0;
  short p1 = 0;
  short p2 = 0;
  short elevationNumber = 0;
  short p3 = 0;
  short threshold1 = 0;
  short threshold2 = 0;
  short threshold3 = 0;
  short threshold4 = 0;
  short threshold5 = 0;
  short threshold6 = 0;
  short threshold7 = 0;
  short threshold8 = 0;
  short threshold9 = 0;
  short threshold10 = 0;
  short threshold11 = 0;
  short threshold12 = 0;
  short threshold13 = 0;
  short threshold14 = 0;
  short threshold15 = 0;
  short threshold16 = 0;
  short p4 = 0;
  short p5 = 0;
  short p6 = 0;
  short p7 = 0;
  short p8 = 0;
  short p9 = 0;
  short p10 = 0;
  short numberOfMaps = 0;
  int offsetToSymbologyBlock = 0;
  int offsetToGraphicBlock = 0;
  int offsetToTabularBlock = 0;

  int block_length = 0;
  short number_layers = 0;

  public boolean isValidFile( ucar.unidata.io.RandomAccessFile raf) {
        try
        {
            long t = raf.length();
        }
        catch ( IOException e )
        {
            return( false );
        }

        try{
            int p = this.readWMO( raf );
            if( p == 0 ) return false; // not unidata radar mosiac gini file
        }
        catch ( IOException e )
        {
            return( false );
        }
    return true;
  }

  int readWMO(ucar.unidata.io.RandomAccessFile raf ) throws IOException
  {
    int pos = 0;
    long     actualSize = 0;
    raf.seek(pos);
    int readLen = 25;
    int rc = 0;

    // Read in the contents of the NEXRAD Level III product heade
    byte[] b = new byte[readLen];
    rc = raf.read(b);
    if ( rc != readLen )
    {
        out.println(" error reading nids product header");
        return 0;
    }

    //Get product message header into a string for processing

    String pib = new String(b);
    if(  pib.indexOf("SDUS")!= -1){
        return 1;
    } else {
        return 0;
    }
  }


 //////////////////////////////////////////////////////////////////////////////////

  private ucar.unidata.io.RandomAccessFile raf;
  private ucar.nc2.NetcdfFile ncfile;
  private PrintStream out = System.out;
  private Vinfo myInfo;
  private String   cmemo, ctilt, ctitle, cunit, cname ;
  public void setProperty( String name, String value) { }

  private   int numX = 0;
  private   int numX0 = 0;
  private   int numY = 0;
  private   int numY0 = 0;
  private   boolean isR = false;
  /////////////////////////////////////////////////////////////////////////////
  // reading header

  void read(ucar.unidata.io.RandomAccessFile raf, ucar.nc2.NetcdfFile ncfile ) throws IOException {

    int      hedsiz;                  /* NEXRAD header size            */
    int      rc;                      /* function return status        */
    int      hoff = 0;
    int      type;
    int      zlibed = 0;
    boolean  isZ = false;
    int      encrypt = 0;
    long     actualSize = 0;
    int      rcode;                   /* radial/raster indicator       */
    float    scale;                   /* data/raster resolution ratio  */
    String   stationId;
    Dimension radialDim;
    int      readLen = 0;

    this.ncfile = ncfile;
    actualSize = raf.length();

    int pos = 0;
    raf.seek(pos);

    // Read in the whole contents of the NEXRAD Level III product since
    // some product require to go through the whole file to build the  struct of file.

    readLen = (int)actualSize;

    byte[] b = new byte[readLen];
    rc = raf.read(b);
    if ( rc != readLen )
    {
        out.println(" error reading nids product header");
    }

    //Get product message header into a string for processing
    String pib = new String(b, 0, 100);
    type = 0;
    pos = pib.indexOf ( "\r\r\n" );
    while ( pos != -1 ) {
        hoff = (int) pos + 3;
        type++;
        pos = pib.indexOf ( "\r\r\n" , pos+1);
    }
    raf.seek(hoff);

    // Test the next two bytes to see if the image portion looks like
    // it is zlib-compressed.
    byte[] b2 = new byte[2];
    byte[] b4 = new byte[4];
    System.arraycopy(b, hoff, b2, 0, 2);

    zlibed = isZlibHed( b2 );
    if ( zlibed == 0)
      encrypt = IsEncrypt( b2 );

   // process product description for station ID
    byte[] b3 = new byte[3];
    byte[] uncompdata = null;

    switch ( type ) {
      case 0:
        out.println( "ReadNexrInfo:: Unable to seek to ID ");
        break;
      case 1:
      case 2:
      case 3:
      case 4:
        System.arraycopy(b, hoff - 6, b3, 0, 3);
        stationId  = new String(b3);
        break;

      default:
        break;
    }

    if ( zlibed == 1 ) {
          isZ = true;
          uncompdata = GetZlibedNexr( b, readLen,  hoff );
          //uncompdata = Nidsiosp.readCompData(hoff, 160) ;
          if ( uncompdata == null ) {
            out.println( "ReadNexrInfo:: error uncompressing image" );
          }
    }
    else {
         uncompdata = new byte[b.length-hoff];
         System.arraycopy(b, hoff, uncompdata, 0, b.length- hoff);
    }

    ByteBuffer bos = ByteBuffer.wrap(uncompdata);
    rc = read_msghead( bos, 0 );
    hedsiz = 18;

    rc = read_proddesc( bos, hedsiz );

    if ( rc == 0   ) {
      out.println( "GetNexrDirs:: error reading proddesc" );
    }
    int t3 = bos.position();
    hedsiz += 102;


    // Set product-dependent information
    int prod_type = code_typelookup(Pinfo.pcode);
    setProductInfo( prod_type );


    // Get product symbology header (needed to get image shape)
    if ( Pinfo.offsetToSymbologyBlock != 0 ) {

      // Symbology header
      rc = read_dividlen( bos, hedsiz );
      if( rc == 0 || Pinfo.divider != -1 )
      {
          out.println( "error in product symbology header" );
      }
      hedsiz += 10;

      // Symbology layer
      int klayer = Pinfo.offsetToSymbologyBlock*2 + 10;
      for(int i=0; i<Sinfo.nlayers ; i++ ){
          hedsiz = klayer;
          bos.position(hedsiz);
          short Divlen_divider = bos.getShort();
          hedsiz += 2;
          int Divlen_length = bos.getInt();
          hedsiz += 4;

          if ( Divlen_divider != -1 ) {
            out.println( "error reading length divider" );
          }

          scale = 1;
          int icount = 0;
          int plen = 0;
          while (icount < Divlen_length ) {
              int boff = klayer + icount + 6;
              bos.position(boff);
              bos.get(b2);
              int pkcode =  getUInt(b2, 2);
              hedsiz += 2;
              boff += 2;
              switch(pkcode)
              {
  //               case 18:  //  DPA
  //                case 17: //   (pkcode == 0x11)   Digital Precipitation Array
  //                    hedsiz += 8;
  //                    plen = pcode_DPA( bos, boff );
  //                    myInfo = new Vinfo (cname, numX, numX0, numY, numY0, hoff, hedsiz, isR, isZ);
  //                    break;
  //                case 10:    //     (pkcode == 0xA)
  //                    plen =  bos.getShort();   // for unlinked Vector Packet the length of data block
  //                    hedsiz += pcode_10( bos, boff );
  //                    myInfo = new Vinfo (cname, numX, numX0, numY, numY0, hoff, hedsiz, isR, isZ);
  //                    break;
  //                 case 1:
  //                 case 8:       //text string
  //                    plen = bos.getShort();
  //                     hedsiz += pcode_1n8( bos, boff, pkcode );
                  default:
                      if ( pkcode == 0xAF1F ) {              /* radial image                  */
                          hedsiz += pcode_radial( bos, hoff, hedsiz, isZ ) ;
                          //myInfo = new Vinfo (cname, numX, numX0, numY, numY0, hoff, hedsiz, isR, isZ);
                          plen = Divlen_length;
                          break;
                      }
                      else if (pkcode == 0xBA0F ||pkcode == 0xBA07 )
                      {      /* raster image                  */
                          hedsiz += pcode_raster( bos, (short)pkcode, hoff, hedsiz, isZ );
                          //myInfo = new Vinfo (cname, numX, numX0, numY, numY0, hoff, hedsiz, isR, isZ);
                          plen = Divlen_length;
                          break;
                      }
                      else
                      {
                          out.println( "error reading pkcode" );
                          throw new IOException("error reading pkcode, unable to handle the product");
                      }

                  // size and beginning data position in file

              } //end of switch
              icount = icount + plen + 4;
          }
          klayer = klayer + Divlen_length + 6;
     }

      int curDoff = hedsiz;

    } else {
      out.println ( "GetNexrDirs:: no product symbology block found (no image data)" );

    }

    // finish
    ncfile.finish();
  }
   int pcode_1n8( ByteBuffer bos, int pos, int code)
   {
        int soff = 0;
        ArrayList dims =  new ArrayList();
        bos.position(pos);
        short ivalue = 0;
        short iStart = 0;
        short jStart = 0;
        Variable v = null;

        int plen = bos.getShort();

        if( code == 1 ) {
            soff = 4;
        } else {
            ivalue = bos.getShort();
            soff = 6;
            v = new Variable(ncfile, null, null, "iValue");
            v.setDataType(DataType.SHORT);
            v.setDimensions((String)null);
            ncfile.addVariable(null, v);
        }

        iStart = bos.getShort();
        iStart = bos.getShort();

        Dimension iDim = new Dimension("ii", plen, true);
        dims.add( iDim);

        v = new Variable(ncfile, null, null, "textString");
        v.setDataType(DataType.BYTE);
        v.setDimensions(dims);
        ncfile.addVariable(null, v);
        v.addAttribute( new Attribute("long_name", "text information about the product"));

        return soff;
    }

  int pcode_10( ByteBuffer bos, int pos )
  {
      byte[] b2 = new byte[2];
      int soff = 0;
      ArrayList dims =  new ArrayList();

      bos.position(pos);
      int vlen = (bos.getShort() - 2 )/8;

      Dimension sDim = new Dimension("size", vlen, true);
      ncfile.addDimension( null, sDim);
      soff = 2;

      dims.add( sDim);

      Structure dist = new Structure(ncfile, null, null, cname);
      dist.setDimensions(dims);
      ncfile.addVariable(null, dist);
      dist.addAttribute( new Attribute("long_name", ctitle));

      Variable ii0 = new Variable(ncfile, null, dist, "i0");
      ii0.setDimensions((String)null);
      ii0.setDataType(DataType.SHORT);
      dist.addMemberVariable(ii0);
      Variable ii1 = new Variable(ncfile, null, dist, "j0");
      ii1.setDimensions((String)null);
      ii1.setDataType(DataType.SHORT);
      dist.addMemberVariable(ii1);
      Variable jj0 = new Variable(ncfile, null, dist, "i1");
      jj0.setDimensions((String)null);
      jj0.setDataType(DataType.SHORT);
      dist.addMemberVariable(jj0);
      Variable jj1 = new Variable(ncfile, null, dist, "j1");
      jj1.setDimensions((String)null);
      jj1.setDataType(DataType.SHORT);
      dist.addMemberVariable(jj1);

      Variable v = new Variable(ncfile, null, null, "vectorLevel");
      v.setDataType(DataType.SHORT);
      v.setDimensions((String)null);
      ncfile.addVariable(null, v);

      numY0 = 0;
      numX0 = 0;
      numX =  0;
      numY = vlen;

      return soff;
  }

  int pcode_DPA( ByteBuffer bos, int pos)
  {
      byte[] b2 = new byte[2];
      int soff = 0;
      ArrayList dims =  new ArrayList();
      bos.position(pos);
      bos.get(b2, 0, 2);  // reserved
      bos.get(b2, 0, 2);  // reserved

      bos.get(b2, 0, 2);
      short numBox = (short)getInt(b2, 2);
      bos.get(b2, 0, 2);
      short numRow = (short)getInt(b2, 2);
      soff = 8;

      numY0 = 0;
      numX0 = 0;
      numX = numBox;
      numY = numRow;
      Dimension jDim = new Dimension("Row", numY, true);
      Dimension iDim = new Dimension("Box", numX, true);
      dims.add( jDim);
      dims.add( iDim);

      Variable v = new Variable(ncfile, null, null, cname);
      v.setDataType(DataType.BYTE);
      v.setDimensions(dims);
      ncfile.addVariable(null, v);
      v.addAttribute( new Attribute("long_name", ctitle));

      for (int row=0; row < numRow; row++) {

        int runLen = bos.getShort();

        byte[] rdata = new byte[runLen];
        bos.get(rdata, 0, runLen);
        if (runLen < 2) {
             return soff;
        } else {
             soff += runLen + 2;
        }

      }   //end of for loop

      return soff;
  }
  int pcode_raster( ByteBuffer bos, short pkcode, int hoff, int hedsiz, boolean isZ )
  {
      byte[] b2 = new byte[2];
      int soff = 0;
      ArrayList dims =  new ArrayList();

      short[] rasp_code = new short[3];

      rasp_code[0] = (short)pkcode;

      bos.get(b2, 0, 2);
      rasp_code[1] = (short)getInt(b2, 2);
      bos.get(b2, 0, 2);
      rasp_code[2] = (short)getInt(b2, 2);
      bos.get(b2, 0, 2);
      short rasp_i = (short)getInt(b2, 2);
      bos.get(b2, 0, 2);
      short rasp_j = (short)getInt(b2, 2);
      bos.get(b2, 0, 2);
      short rasp_xscale = (short)getInt(b2, 2);
      bos.get(b2, 0, 2);
      short rasp_xscalefract = (short)getInt(b2, 2);
      bos.get(b2, 0, 2);
      short rasp_yscale = (short)getInt(b2, 2);
      bos.get(b2, 0, 2);
      short rasp_yscalefract = (short)getInt(b2, 2);
      bos.get(b2, 0, 2);
      short num_rows = (short)getInt(b2, 2);
      bos.get(b2, 0, 2);
      short packing = (short)getInt(b2, 2);
      soff= 20;
      hedsiz = hedsiz + soff;
      //prod_info_size = (int) (num_rows * scale);

      numY0 = rasp_j;
      numX0 = rasp_i;
      numX = num_rows;
      numY = num_rows;
      Dimension jDim = new Dimension("J", numY, true);
      Dimension iDim = new Dimension("I", numX, true);
      dims.add( jDim);
      dims.add( iDim);


      Variable dist = new Variable(ncfile, null, null, "distance");
      dist.setDataType(DataType.INT);
      dist.setDimensions(dims);
      ncfile.addVariable(null, dist);
      dist.setSPobject( new Vinfo ("distance", numX, numX0, numY, numY0, hoff, hedsiz, isR, isZ));

      Variable v = new Variable(ncfile, null, null, cname);
      v.setDataType(DataType.BYTE);
      v.setDimensions(dims);
      ncfile.addVariable(null, v);
      v.addAttribute( new Attribute("long_name", ctitle));
      v.setSPobject( new Vinfo (cname, numX, numX0, numY, numY0, hoff, hedsiz, isR, isZ));
      return soff;

  }
  int pcode_radial( ByteBuffer  bos, int hoff, int hedsiz, boolean isZ )
  {
      byte[] b2 = new byte[2];
      int soff = 0;
      ArrayList dims =  new ArrayList();

      bos.get(b2, 0, 2);
      short first_bin = (short)getInt(b2, 2);
      bos.get(b2, 0, 2);
      short num_bin = (short)getInt(b2, 2);
      bos.get(b2, 0, 2);
      short radp_i = (short)getInt(b2, 2);
      bos.get(b2, 0, 2);
      short radp_j = (short)getInt(b2, 2);
      bos.get(b2, 0, 2);
      short radp_scale = (short)getInt(b2, 2);
      bos.get(b2, 0, 2);
      short num_radials = (short)getInt(b2, 2);
      soff = 12;
      hedsiz = hedsiz + soff;
      numY0 = 0;
      numY = num_radials;
      numX0 = first_bin;
      numX = num_bin;
      //prod_info_size = 2 * (int) (num_bin * scale + 0.5);
      //dimensions: radial, bin

      Dimension radialDim = new Dimension("azimuth", num_radials, true);
      ncfile.addDimension( null, radialDim);

      Dimension binDim = new Dimension("gate", num_bin, true);
      ncfile.addDimension( null, binDim);
      dims.add( radialDim);
      dims.add( binDim);

      ArrayList dims1 =  new ArrayList();
      dims1.add(radialDim);
      Variable aziVar = new Variable(ncfile, null, null, "azimuth");
      aziVar.setDataType(DataType.FLOAT);
      aziVar.setDimensions(dims1);
      ncfile.addVariable(null, aziVar);
      aziVar.addAttribute( new Attribute("long_name", "azimuth angle in degrees: 0 = true north, 90 = east"));
      aziVar.addAttribute( new Attribute("units", "degrees"));
      aziVar.setSPobject( new Vinfo ("azimuth", numX, numX0, numY, numY0, hoff, hedsiz, isR, isZ));

      dims1 =  new ArrayList();
      dims1.add(binDim);
      Variable gateV = new Variable(ncfile, null, null, "gate");
      gateV.setDataType(DataType.FLOAT);
      gateV.setDimensions(dims1);
      ncfile.addVariable(null, gateV);
      gateV.addAttribute( new Attribute("long_name", "radial distance to start of gate"));
      gateV.addAttribute( new Attribute("units", "m"));
      gateV.setSPobject( new Vinfo ("gate", numX, numX0, numY, radp_scale, hoff, hedsiz, isR, isZ));
      isR = true;

      Variable v = new Variable(ncfile, null, null, cname);
      v.setDataType(DataType.BYTE);
      v.setDimensions(dims);
      ncfile.addVariable(null, v);
      v.addAttribute( new Attribute("long_name", ctitle));
      v.addAttribute( new Attribute("units", cunit));
      v.setSPobject( new Vinfo (cname, numX, numX0, numY, numY0, hoff, hedsiz, isR, isZ));

      return soff;
  }
  
  String StnIdFromLatLon(float lat, float lon )
  {
    String d = "ID";
    return d;
  }

  void setProductInfo(int prod_type)
  {
                                 /* memo field                */
    String[] cmode = new String[]{"Maintenance", "Clear Air", "Precip Mode"};

    short prod_max = Pinfo.p4;
    short prod_min;
    int prod_elevation;
    int prod_info;
    int prod_top ;
    int prod_end_time ;
    int prod_start_time ;
    float prod_raster_res;
    int radial;

    if (prod_type == Base_Reflect) {
      radial               = 1;
      prod_elevation  = Pinfo.p3;
      cmemo = "Base Reflct " + prod_elevation/10 + " DEG " + cmode[Pinfo.opmode];

      ctilt = cmemo.substring(12, 20);
      ctitle = "BREF: Base Reflectivity";
      cunit = "DBZ";
      cname = "BaseReflectivity";
    } else if (prod_type == BaseReflect248) {
      radial               = 1;
      prod_elevation  = Pinfo.p3;

      cmemo = "Base Reflct 248 " + prod_elevation/10 + " DEG " + cmode[Pinfo.opmode];

      ctilt = cmemo.substring(12, 20 );
      ctitle = "BREF: 248 nm Base Reflectivity";
      cunit = "DBZ";
      cname = "BaseReflectivity248";
    } else if (prod_type == Comp_Reflect) {
      radial               = 0;
      prod_elevation  = -1;

      cmemo = "Composite Reflect " + cmode[Pinfo.opmode];
      ctilt = "-99" ;
      ctitle = "CREF Composite Reflectivity" ;
      cunit = "DBZ" ;

    } else if (prod_type == Layer_Reflect_Avg ||
             prod_type == Layer_Reflect_Max)   {
      radial               = 0;
      prod_elevation  = Pinfo.p5;
      prod_top        = Pinfo.p6;

      cmemo = "Layer Reflct " + prod_elevation + " - " + prod_top + cmode[Pinfo.opmode];

      ctilt = cmemo.substring(13, 19 );
      ctitle = "LREF: Layer Composite Reflectivity" ;
      cunit = "DBZ" ;
      cname = "LayerCompReflect";
    } else if (prod_type == Echo_Tops) {
      radial               = 0;
      prod_elevation  = -1;

      cmemo = "Echo Tops [K FT] " + cmode[Pinfo.opmode];
      ctilt = "-99" ;
      ctitle = "TOPS: Echo Tops";
      cunit = "K FT" ;
      cname = "EchoTop";
    } else if (prod_type == Precip_1)   {
      radial               = 0;
      prod_elevation  = -1;
      prod_max       /= 10;
      prod_end_time   = (Pinfo.p7 - 1) * 86400 + Pinfo.p8 * 60;

      cmemo = "1-hr Rainfall [IN] " + cmode[Pinfo.opmode];
      ctilt = "-99" ;
      ctitle = "PRE1: Surface 1-hour Rainfall Total";
      cunit = "IN";
      cname = "Precip1hr";
    } else if (prod_type == Precip_3)   {
      radial               = 0;
      prod_elevation  = -1;
      prod_max       /= 10;
      prod_end_time   = (Pinfo.p7 - 1) * 86400 + Pinfo.p8 * 60;

      cmemo = "3-hr Rainfall [IN] " + cmode[Pinfo.opmode] ;
      ctilt = "-99" ;
      ctitle = "PRE3: Surface 3-hour Rainfall Total" ;
      cunit = "IN" ;
      cname = "Precip3hr";
    } else if (prod_type == Precip_Accum) {
      radial               = 0;
      prod_elevation  = -1;
      prod_start_time = (Pinfo.p5 - 1) * 86400 + Pinfo.p6 * 60;
      prod_end_time   = (Pinfo.p7 - 1) * 86400 + Pinfo.p8 * 60;

      cmemo = "Strm Tot Rain [IN] " + cmode[Pinfo.opmode] ;
      ctilt = "-99" ;
      ctitle = "PRET: Surface Storm Total Rainfall" ;
      cunit = "IN" ;
      cname = "PricipAccum";
     } else if (prod_type == Precip_Array) {
      radial          = 0;
      prod_elevation  = -1;
      prod_start_time = (Pinfo.p5 - 1) * 86400 + Pinfo.p6 * 60;
      prod_end_time   = (Pinfo.p7 - 1) * 86400 + Pinfo.p8 * 60;

      cmemo = "Precip Array [IN] " + cmode[Pinfo.opmode] ;
      ctilt = "-99" ;
      ctitle = "PRET: Hourly Digital Precipitation Array" ;
      cunit = "IN" ;
      cname = "PrecipArray";
    } else if (prod_type == Vert_Liquid) {
      radial               = 0;
      prod_elevation  = -1;

      cmemo = "Vert Int Lq H2O [mm] " + cmode[Pinfo.opmode] ;
      ctilt = "-99" ;
      ctitle =  "VIL: Vertically-integrated Liquid Water" ;
      cunit =  "kg/m^2" ;
      cname = "VertLiquid";
    } else if (prod_type == Velocity) {
      radial               = 1;
      prod_elevation  = Pinfo.p3;
      prod_min        = Pinfo.p4;
      prod_max        = Pinfo.p5;

      cmemo = "Rad Vel "+ prod_elevation/10. + " DEG " + cmode[Pinfo.opmode];

      ctilt = cmemo.substring(8, 16);
      ctitle = "VEL: Radial Velocity" ;
      cunit = "KT" ;
      cname = "Velocity";
    } else if (prod_type == StrmRelMeanVel) {
      radial               = 1;
      prod_elevation  = Pinfo.p3;
      prod_min        = Pinfo.p4;
      prod_max        = Pinfo.p5;

      cmemo = "StrmRelMnVl " + prod_elevation/10. + " DEG " + cmode[Pinfo.opmode];

      ctilt = cmemo.substring(12, 20);
      ctitle = "SRMV: Storm Relative Mean Velocity" ;
      cunit = "KT" ;
      cname = "StormMeanVelocity";
    } else if (prod_type == VAD) {
      radial               = 1;
      prod_elevation  = Pinfo.p3;
      prod_min        = Pinfo.p4;
      prod_max        = Pinfo.p5;

      cmemo = "StrmRelMnVl " + prod_elevation/10. + " DEG " + cmode[Pinfo.opmode];

      ctilt = cmemo.substring(12, 20);
      ctitle = "SRMV: Velocity Azimuth Display" ;
      cunit = "KT" ;
      cname = "VADWindProfile";
    } else {
      ctilt = "error";
      ctitle = "error" ;
      cunit = "error" ;
      cname = "error";
    }

  }
  /*
  ** Name:       read_dividlen
  **
  ** Purpose:    Read divider ID header from NEXRAD Level III product
  **
  */
  int read_dividlen( ByteBuffer buf, int offset  )
  {
      int off = offset;
      byte[] b2 = new byte[2];
      byte[] b4 = new byte[4];
      short D_divider = 0;
      short D_id = 0;
      Short tShort = null;

      buf.position(offset);
      buf.get(b2, 0, 2);
      tShort = (Short)convert(b2, DataType.SHORT, -1);
      D_divider  = tShort.shortValue();
      buf.get(b2, 0, 2);
      D_id  = (short)getInt(b2, 2);
      buf.get(b4, 0, 4);
      block_length  = getInt(b4, 4);
      buf.get(b2, 0, 2);
      number_layers  = (short)getInt(b2, 2);
      off = off + 10;

      new Sinfo ( D_divider, D_id, block_length, number_layers);

      return 1;

  }


  /*
  ** Name:       read_msghead
  **
  ** Purpose:    Read message header from NEXRAD Level III product
  **
  **
  */
  int read_msghead( ByteBuffer buf, int offset)
  {

      byte[] b2 = new byte[2];
      byte[] b4 = new byte[4];

      buf.position(0);
      buf.get(b2, 0, 2);
      mcode = (short) getInt(b2, 2);
      buf.get(b2, 0, 2);
      mdate = (short) getInt(b2, 2);
      buf.get(b4, 0, 4);
      mtime = getInt(b4, 4);
      buf.get(b4, 0, 4);
      java.util.Date volumnDate = getDate( mdate, mtime*1000);
      String dstring = DateUnit.getStandardDateString(volumnDate);
      mlength = getInt(b4, 4);
      buf.get(b2, 0, 2);
      msource = (short) getInt(b2, 2);
      buf.get(b2, 0, 2);
      mdestId = (short) getInt(b2, 2);
      buf.get(b2, 0, 2);
      mNumOfBlock = (short) getInt(b2, 2);

      return 1;

  }
     int getUInt( byte[] b, int num )
  {
      int            base=1;
      int            i;
      int            word=0;

      int bv[] = new int[num];

      for (i = 0; i<num; i++ )
      {
        bv[i] = convertunsignedByte2Short(b[i]);
      }

      /*
      ** Calculate the integer value of the byte sequence
      */

      for ( i = num-1; i >= 0; i-- ) {
        word += base * bv[i];
        base *= 256;
      }

      return word;

  }
   int getInt( byte[] b, int num )
  {
      int            base=1;
      int            i;
      int            word=0;

      int bv[] = new int[num];

      for (i = 0; i<num; i++ )
      {
        bv[i] = convertunsignedByte2Short(b[i]);
      }

      if( bv[0] > 127 )
      {
         bv[0] -= 128;
         base = -1;
      }
      /*
      ** Calculate the integer value of the byte sequence
      */

      for ( i = num-1; i >= 0; i-- ) {
        word += base * bv[i];
        base *= 256;
      }

      return word;

  }

  public Vinfo getVarInfo( )
  {
     return myInfo;
  }
  public short convertunsignedByte2Short(byte b)
  {
     return (short)((b<0)? (short)b + 256 : (short)b);
  }

  static public java.util.Date getDate(int julianDays, int msecs) {
    long total = ((long) (julianDays - 1)) * 24 * 3600 * 1000 + msecs;
    return new Date( total);
  }
  /*
  ** Name:       read_proddesc
  **
  ** Purpose:    Read product description header from NEXRAD Level III product
  **
  **
  */
  int read_proddesc(  ByteBuffer buf, int offset ){
      byte[] b2 = new byte[2];
      byte[] b4 = new byte[4];
      int off = offset;
      Short tShort = null;
      Integer tInt = null;
      Double tDouble = null;

      buf.position(offset);
      buf.get(b2, 0, 2);
      tShort = (Short)convert(b2, DataType.SHORT, -1);
      divider  =  tShort.shortValue();
      ncfile.addAttribute(null, new Attribute("Divider", tShort));
      buf.get(b4, 0, 4);
      tInt = (Integer)convert(b4, DataType.INT, -1);
      latitude = tInt.intValue()/ 1000.0;
      ncfile.addAttribute(null, new Attribute("latitude", new Double(latitude)));
      buf.get(b4, 0, 4);
      tInt = (Integer)convert(b4, DataType.INT, -1);
      longitude = tInt.intValue()/ 1000.0;
      ncfile.addAttribute(null, new Attribute("longitude",new Double(longitude)));
      buf.get(b2, 0, 2);
      height = (short)getInt(b2, 2);
      ncfile.addAttribute(null, new Attribute("height",new Short(height)));
      buf.get(b2, 0, 2);
      pcode = (short)getInt(b2, 2);
      ncfile.addAttribute(null, new Attribute("Product_code",new Short(pcode)));
      buf.get(b2, 0, 2);
      opmode = (short)getInt(b2, 2);
      ncfile.addAttribute(null, new Attribute("Operational_Mode",new Short(opmode)));
      buf.get(b2, 0, 2);
      volumnScanPattern = (short)getInt(b2, 2);
      ncfile.addAttribute(null, new Attribute("volumn_scan_pattern",new Short(volumnScanPattern)));
      buf.get(b2, 0, 2);
      sequenceNumber = (short)getInt(b2, 2);
      ncfile.addAttribute(null, new Attribute("sequence_number",new Short(sequenceNumber)));
      buf.get(b2, 0, 2);
      volumeScanNumber = (short)getInt(b2, 2);
      ncfile.addAttribute(null, new Attribute("volume_scan_number",new Short(volumeScanNumber)));
      buf.get(b2, 0, 2);
      volumeScanDate = (short)getUInt(b2, 2);
      buf.get(b4, 0, 4);
      volumeScanTime = getUInt(b4, 4);
      java.util.Date volumnDate = getDate( volumeScanDate, volumeScanTime*1000);
      String dstring = DateUnit.getStandardDateString(volumnDate);
      ncfile.addAttribute(null, new Attribute("volume_scan_time", dstring));
      buf.get(b2, 0, 2);
      productDate = (short)getUInt(b2, 2);
      buf.get(b4, 0, 4);
      productTime = getUInt(b4, 4);
      java.util.Date pDate = getDate( productDate, productTime*1000);
      dstring = DateUnit.getStandardDateString(pDate);
      ncfile.addAttribute(null, new Attribute("product_generation_time", dstring));
      buf.get(b2, 0, 2);
      p1 = (short)getInt(b2, 2);
      buf.get(b2, 0, 2);
      p2 = (short)getInt(b2, 2);
      buf.get(b2, 0, 2);
      elevationNumber = (short)getInt(b2, 2);
      ncfile.addAttribute(null, new Attribute("elevation_number",new Short(elevationNumber)));
      buf.get(b2, 0, 2);
      p3 = (short)getInt(b2, 2);
      off += 40;

      buf.get(b2, 0, 2);
      threshold1 = (short)getInt(b2, 2);
      buf.get(b2, 0, 2);
      threshold2 = (short)getInt(b2, 2);
      buf.get(b2, 0, 2);
      threshold3 = (short)getInt(b2, 2);
      buf.get(b2, 0, 2);
      threshold4 = (short)getInt(b2, 2);
      buf.get(b2, 0, 2);
      threshold5 = (short)getInt(b2, 2);
      buf.get(b2, 0, 2);
      threshold6 = (short)getInt(b2, 2);
      buf.get(b2, 0, 2);
      threshold7 = (short)getInt(b2, 2);
      buf.get(b2, 0, 2);
      threshold8 = (short)getInt(b2, 2);
      buf.get(b2, 0, 2);
      threshold9 = (short)getInt(b2, 2);
      buf.get(b2, 0, 2);
      threshold10 = (short)getInt(b2, 2);
      buf.get(b2, 0, 2);
      threshold11 = (short)getInt(b2, 2);
      buf.get(b2, 0, 2);
      threshold12 = (short)getInt(b2, 2);
      buf.get(b2, 0, 2);
      threshold13 = (short)getInt(b2, 2);
      buf.get(b2, 0, 2);
      threshold14 = (short)getInt(b2, 2);
      buf.get(b2, 0, 2);
      threshold15 = (short)getInt(b2, 2);
      buf.get(b2, 0, 2);
      threshold16 = (short)getInt(b2, 2);
      off += 32;
      buf.get(b2, 0, 2);
      p4 = (short)getInt(b2, 2);
      int t1 = getUInt(b2, 2);
      buf.get(b2, 0, 2);
      p5 = (short)getInt(b2, 2);
      t1 = getUInt(b2, 2);
      buf.get(b2, 0, 2);
      p6 = (short)getInt(b2, 2);
      t1 = getUInt(b2, 2);
      buf.get(b2, 0, 2);
      p7 = (short)getInt(b2, 2);
      buf.get(b2, 0, 2);
      p8 = (short)getInt(b2, 2);
      buf.get(b2, 0, 2);
      p9 = (short)getInt(b2, 2);
      buf.get(b2, 0, 2);
      p10 = (short)getInt(b2, 2);
      off += 14;

      buf.get(b2, 0, 2);
      numberOfMaps = (short)getInt(b2, 2);
      ncfile.addAttribute(null, new Attribute("number_of_maps",new Short(numberOfMaps)));
      off += 2;
      buf.get(b4, 0, 4);
      tInt = (Integer)convert(b4, DataType.INT, -1);
      offsetToSymbologyBlock = getInt(b4, 4);
      ncfile.addAttribute(null, new Attribute("offset_symbology_block",new Integer(offsetToSymbologyBlock)));
      off += 4;
      buf.get(b4, 0, 4);
      tInt = (Integer)convert(b4, DataType.INT, -1);
      offsetToGraphicBlock = getInt(b4, 4);
      ncfile.addAttribute(null, new Attribute("offset_graphic_block",new Integer(offsetToGraphicBlock)));
      off += 4;
      buf.get(b4, 0, 4);
      tInt = (Integer)convert(b4, DataType.INT, -1);
      offsetToTabularBlock = getInt(b4, 4);
      ncfile.addAttribute(null, new Attribute("offset_tabular_block",new Integer(offsetToTabularBlock)));
      off += 4;

      Pinfo v = new Pinfo (divider, latitude, longitude, height, pcode, opmode,
                           sequenceNumber, volumeScanNumber, volumeScanDate, volumeScanTime,
                            productDate, productTime, p1,p2,p3,p4,p5,p6,p7,p8,p9,p10,
                            elevationNumber, numberOfMaps, offsetToSymbologyBlock,
                            offsetToGraphicBlock, offsetToTabularBlock);

      return off;

  }

  // this converts a byte array to another primitive array
  protected Object convert( byte[] barray, DataType dataType, int nelems, int byteOrder) {

    if (dataType == DataType.BYTE) {
      return barray;
    }

    if (dataType == DataType.CHAR) {
      return convertByteToChar( barray);
    }

    ByteBuffer bbuff = ByteBuffer.wrap( barray);
    if (byteOrder >= 0)
      bbuff.order( byteOrder == ucar.unidata.io.RandomAccessFile.LITTLE_ENDIAN? ByteOrder.LITTLE_ENDIAN : ByteOrder.BIG_ENDIAN);

    if (dataType == DataType.SHORT) {
      ShortBuffer tbuff = bbuff.asShortBuffer();
      short[] pa = new short[nelems];
      tbuff.get( pa);
      return pa;

    } else if (dataType == DataType.INT) {
      IntBuffer tbuff = bbuff.asIntBuffer();
      int[] pa = new int[nelems];
      tbuff.get( pa);
      return pa;

    } else if (dataType == DataType.FLOAT) {
      FloatBuffer tbuff = bbuff.asFloatBuffer();
      float[] pa = new float[nelems];
      tbuff.get( pa);
      return pa;

    } else if (dataType == DataType.DOUBLE) {
      DoubleBuffer tbuff = bbuff.asDoubleBuffer();
      double[] pa = new double[nelems];
      tbuff.get( pa);
      return pa;
    }

    throw new IllegalStateException();
  }

  // this converts a byte array to a wrapped primitive (Byte, Short, Integer, Double, Float, Long)
  protected Object convert( byte[] barray, DataType dataType, int byteOrder) {

    if (dataType == DataType.BYTE) {
      return new Byte( barray[0]);
    }

    if (dataType == DataType.CHAR) {
      return new Character((char) barray[0]);
    }

    ByteBuffer bbuff = ByteBuffer.wrap( barray);
    if (byteOrder >= 0)
      bbuff.order( byteOrder == ucar.unidata.io.RandomAccessFile.LITTLE_ENDIAN? ByteOrder.LITTLE_ENDIAN : ByteOrder.BIG_ENDIAN);

    if (dataType == DataType.SHORT) {
      ShortBuffer tbuff = bbuff.asShortBuffer();
      return new Short(tbuff.get());

    } else if (dataType == DataType.INT) {
      IntBuffer tbuff = bbuff.asIntBuffer();
      return new Integer(tbuff.get());

    } else if (dataType == DataType.LONG) {
      LongBuffer tbuff = bbuff.asLongBuffer();
      return new Long(tbuff.get());

    } else if (dataType == DataType.FLOAT) {
      FloatBuffer tbuff = bbuff.asFloatBuffer();
      return new Float(tbuff.get());

    } else if (dataType == DataType.DOUBLE) {
      DoubleBuffer tbuff = bbuff.asDoubleBuffer();
      return new Double(tbuff.get());
    }

    throw new IllegalStateException();
  }




    // convert byte array to char array
  protected char[] convertByteToChar( byte[] byteArray) {
    int size = byteArray.length;
    char[] cbuff = new char[size];
    for (int i=0; i<size; i++)
      cbuff[i] = (char) byteArray[i];
    return cbuff;
  }

   // convert char array to byte array
  protected byte[] convertCharToByte( char[] from) {
    int size = from.length;
    byte[] to = new byte[size];
    for (int i=0; i<size; i++)
      to[i] = (byte) from[i];
    return to;
  }



  //////////////////////////////////////////////////////////////////////////
  // utilities



  /**
   * Flush all data buffers to disk.
   * @throws IOException
   */
  public void flush() throws IOException {
    raf.flush();
  }

  /**
   *  Close the file.
   * @throws IOException
   */
  public void close() throws IOException {
    if (raf != null)
      raf.close();
  }


  /*
  ** Name:       IsZlibed
  **
  ** Purpose:    Check a two-byte sequence to see if it indicates the start of
  **             a zlib-compressed buffer
  **
  */

   int isZlibHed( byte[] buf ){
    short b0 = convertunsignedByte2Short(buf[0]);
    short b1 = convertunsignedByte2Short(buf[1]);

    if ( (b0 & 0xf) == Z_DEFLATED ) {
      if ( (b0 >> 4) + 8 <= DEF_WBITS ) {
        if ( (((b0 << 8) + b1) % 31)==0 ) {
          return 1;
        }
      }
    }

    return 0;

  }

  /*
  ** Name:       IsEncrypt
  **
  ** Purpose:    Check a two-byte sequence to see if it indicates the start of
  **             an encrypted image.
  **
  */
  int IsEncrypt( byte[] buf )
  {
      /*
      ** These tests were deduced from inspection from encrypted NOAAPORT files.
      */
      String b = new String(buf);
      if ( b.startsWith("R3") ) {
        return 1;
      }

      return 0;
  }


  /*
  ** Name:    GetZlibedNexr
  **
  ** Purpose: Read bytes from a NEXRAD Level III product into a buffer
  **
  */
  byte[] GetZlibedNexr( byte[] buf, int buflen, int hoff ) throws IOException
  {
      //byte[]  uncompr = new byte[ZLIB_BUF_LEN ]; /* decompression buffer          */
      //long    uncomprLen = ZLIB_BUF_LEN;        /* length of decompress space    */
      int             doff = 0;                   /* # bytes offset to image       */
      int             numin=0;                /* # input bytes processed       */



      numin = buflen - hoff ;

      if( numin <= 0 )
      {
        out.println(" No compressed data to inflate ");
        return null;
      }
      byte[]  compr = new byte[numin-4];  /* compressed portion */
      /*
      ** Uncompress first portion of the image.  This should include:
      **
      **     SHO\r\r\n             <--+
      **     SEQ#\r\r\n               |  hoff bytes long
      **     WMO header\r\r\n         |
      **     PIL\r\r\n             <--+
      **
      **  -> CCB
      **     WMO header
      **     PIL
      **     portion of the image
      **
      */
      /* a new copy of buff with only compressed bytes */

      System.arraycopy( buf, hoff, compr, 0, numin - 4);

      // decompress the bytes
      int resultLength = 0;
      int result = 0;
      byte[] inflateData = null;
      byte[] tmp = null;
      long  uncompLen = numin * 2;        /* length of decompress space    */
      byte[] uncomp = new byte[(int)uncompLen];
      Inflater inflater = new Inflater( false);

      inflater.setInput(compr, 0, numin-4);

      while ( inflater.getRemaining() > 0 )
      {
          try {
            resultLength = inflater.inflate(uncomp);
          }
          catch (DataFormatException ex) {
            System.out.println("ERROR on inflation");
            ex.printStackTrace();
          }

          if(resultLength > 0 ) {
              result = result + resultLength;
              inflateData = new byte[result];
              if(tmp != null) {
                 System.arraycopy(tmp, 0, inflateData, 0, tmp.length);
                 System.arraycopy(uncomp, 0, inflateData, tmp.length, resultLength);
              } else {
                 System.arraycopy(uncomp, 0, inflateData, 0, resultLength);
              }
              tmp = new byte[result];
              System.arraycopy(inflateData, 0, tmp, 0, result);
          }  else {
               int tt = inflater.getRemaining();
               inflater.reset();
               inflater.setInput(compr, numin-4-tt, tt);
          }

      }

      inflater.end();
      /*
      ** Find out how long CCB is.  This is done by using the lower order
      ** 6 bits from the first uncompressed byte and all 8 bits of the
      ** second uncompressed byte.
      */
      byte   b1, b2;
      b1 = inflateData[0];
      b2 = inflateData[1];
      doff  = 2 * (((b1 & 63) << 8) + b2);

      for ( int i = 0; i < 2; i++ ) {                         /* eat WMO and PIL */
        while ( (doff < inflateData.length ) && (inflateData[doff] != '\n') ) doff++;
        doff++;
      }

      byte[] data = new byte[ inflateData.length - doff];

      System.arraycopy(inflateData, doff, data, 0, inflateData.length - doff);

      //
      /*
      ** Copy header bytes to decompression buffer.  The objective is to
      ** create an output buffer that looks like an uncompressed NOAAPORT
      ** NEXRAD product:
      **
      **   Section               Product               Example             End
      **            +--------------------------------+
      **            |                                |
      **      1     |        start of product        | CTRL-A              \r\r\n
      **            |                                |
      **            +--------------------------------+
      **            |                                |
      **      2     |        sequence number         | 237                 \r\r\n
      **            |                                |
      **            +--------------------------------+
      **            |                                |
      **      3     |          WMO header            | SDUS53 KARX 062213  \r\r\n
      **            |                                |
      **            +--------------------------------+
      **            |                                |
      **      4     |             PIL                | N0RARX              \r\r\n
      **            |                                |
      **            +--------------------------------+
      **            |                                |
      **      5     |                                | AAO130006R2 CH-1
      **            |                                | Interface Control
      **            |             CCB                | Document (ICD)
      **            |                                | for the NWS NWSTG
      **            |                                | Figure 7-1 p 38
      **            |                                |
      **            +--------------------------------+
      **            |                                |
      **      6     |          WMO header            | SDUS53 KARX 062213  \r\r\n
      **            |                                |
      **            +--------------------------------+
      **            |                                |
      **      7     |             PIL                | N0RARX              \r\r\n
      **            |                                |
      **            +--------------------------------+
      **            |                                |
      **            |                                |
      **            |                                |
      **            |                                |
      **      8     |            image               |
      **            |                                |
      **            |                                |
      **            |                                |
      **            |                                |
      **            +--------------------------------+
      **            |                                |
      **      9     |            trailer             | \r\r\nETX
      **            |                                |
      **            +--------------------------------+
      **            |                                |
      **     10     |     Unidata floater trailer    | \0\0
      **            |                                |
      **            +--------------------------------+
      **
      ** Sections 5-8 are zlib compressed.  They must be uncompressed and
      ** read to find out where the image begins.  When this is done, sections
      ** 5-7 are thrown away and 8 is returned immediately following 4.
      ** Section 9 and, if it is there, section 10 are also thrown away.
      **
      */

      return data;

  }




  /*
  ** Name:       code_lookup
  **
  ** Purpose:    Derive some derivable metadata
  **
  */
    static int code_typelookup( int code )
    {
      int type;
      final int[] types = {
        Other, Other, Other, Other, Other,                          /*   0-  9 */
        Other, Other, Other, Other, Other,
        Other, Other, Other, Other, Other,                          /*  10- 19 */
        Other, Base_Reflect, Base_Reflect, Base_Reflect, Base_Reflect,
        BaseReflect248, Base_Reflect, Velocity,                     /*  20- 29 */
        Velocity, Velocity, Velocity, Velocity, Velocity, Other, Other,
        Other, Other, Other, Other, Other,                          /*  30- 39 */
        Comp_Reflect, Comp_Reflect, Comp_Reflect, Comp_Reflect, Other,
        Other, Echo_Tops, Other, Other, Other,                      /*  40- 49 */
        Other, Other, Other, VAD, Other,
        Other, Other, Other, Other, Other,                          /*  50- 59 */
        StrmRelMeanVel, StrmRelMeanVel, Vert_Liquid, Other, Other,
        Other, Other, Other, Layer_Reflect_Avg,                     /*  60- 69 */
        Layer_Reflect_Avg, Layer_Reflect_Max,
        Layer_Reflect_Max, Other, Other, Other,
        Other, Other, Other, Other, Other,                          /*  70- 79 */
        Other, Other, Other, Precip_1, Precip_3,
        Precip_Accum, Precip_Array, Other,                          /*  80- 89 */
        Other, Other, Other, Other, Other, Other, Layer_Reflect_Avg,
        Layer_Reflect_Max, Other, Other, Other,                     /*  90- 99 */
        Other, Other, Other, Other, Other, Other,
        Other, Other, Other, Other, Other,                          /* 100-109 */
        Other, Other, Other, Other, Other,
      };


      if ( code < 0 || code > 109 )
        type     = Other;
      else
        type     = types[code];

      return type;

    }

    static double code_reslookup( int code )
    {

      double data_res;
      final  double[] res = {
        0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    /*   0-  9 */
        0,    0,    0,    0,    0,    0,    1,    2,    4,    1,    /*  10- 19 */
        2,    4, 0.25,  0.5,    1, 0.25,  0.5,    1,    0,    0,    /*  20- 29 */
        0,    0,    0,    0,    0,    1,    4,    1,    4,    0,    /*  30- 39 */
        0,    4,    0,    0,    0,    0,    0,    0,    0,    0,    /*  40- 49 */
        0,    0,    0,    0,    0,  0.5,    1,    4,    0,    0,    /*  50- 59 */
        0,    0,    0,    4,    4,    4,    4,    0,    0,    0,    /*  60- 69 */
        0,    0,    0,    0,    0,    0,    0,    0,    2,    2,    /*  70- 79 */
        2,    0,    0,    0,    0,    0,    0,    0,    0,    4,    /*  80- 89 */
        4,    0,    0,    0,    0,    0,    0,    0,    0,    0,    /*  90- 99 */
        0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    /* 100-109 */
      };


      if ( code < 0 || code > 109 )
        data_res = 0;
      else
        data_res = res[code];

      return data_res;

    }

    static int code_levelslookup( int code )
    {

      int level;
      final int[] levels = {
        0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    /*   0-  9 */
        0,    0,    0,    0,    0,    0,    8,    8,    8,   16,    /*  10- 19 */
       16,   16,    8,    8,    8,   16,   16,   16,    0,    0,    /*  20- 29 */
        0,    0,    0,    0,    0,    8,    8,   16,   16,    0,    /*  30- 39 */
        0,   16,    0,    0,    0,    0,    0,    0,    0,    0,    /*  40- 49 */
        0,    0,    0,    0,    0,   16,   16,   16,    0,    0,    /*  50- 59 */
        0,    0,    0,    8,    8,    8,    8,    0,    0,    0,    /*  60- 69 */
        0,    0,    0,    0,    0,    0,    0,    0,   16,   16,    /*  70- 79 */
       16,    0,    0,    0,    0,    0,    0,    0,    0,    8,    /*  80- 89 */
        8,    0,    0,    0,    0,    0,    0,    0,    0,    0,    /*  90- 99 */
        0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    /* 100-109 */
      };


      if ( code < 0 || code > 109 )
        level = 0;
      else
        level = levels[code];

      return level;

    }
      // Symbology block info for reading/writing
    static class Sinfo {
      static short divider;
      static short id;
      static int blockLength;
      static short nlayers;

      Sinfo( short divider, short id, int length, short layers) {
        this.divider = divider;
        this.id = id;
        this.blockLength = length;
        this.nlayers = layers;
      }
    }

  // variable info for reading/writing
  static class Vinfo {
    String vname;
    int xt;
    int x0;
    int yt;
    int y0;
    boolean isRadial; // is it a radial variable?
    long hoff;    // header offset
    long doff;
    boolean isZlibed;

    Vinfo( String vname, int xt, int x0, int yt, int y0,long hoff, long doff, boolean isRadial, boolean isZ) {
        this.vname = vname;
        this.xt = xt;
        this.yt = yt;
        this.x0 = x0;
        this.y0 = y0;
        this.hoff = hoff;
        this.doff = doff;
        this.isRadial = isRadial;
        this.isZlibed = isZ;
    }
  }


      // product info for reading/writing
  static class Pinfo {
    static short divider, height, pcode, opmode, sequenceNumber, volumeScanNumber, volumeScanDate, productDate;
    static double latitude, longitude;
    static int volumeScanTime,  productTime;
    static short p1, p2, p3, p4, p5, p6, p7, p8, p9, p10;
    static short elevationNumber, numberOfMaps;
    static int offsetToSymbologyBlock, offsetToGraphicBlock, offsetToTabularBlock;

    Pinfo (short divider , double latitude, double longitude, short height, short pcode, short opmode,
                           short sequenceNumber, short volumeScanNumber, short volumeScanDate, int volumeScanTime,
                            short productDate, int productTime, short p1,short p2,short p3,short p4,short p5,
                            short p6,short p7,short p8, short p9,short p10,
                            short elevationNumber, short numberOfMaps, int offsetToSymbologyBlock,
                            int offsetToGraphicBlock, int offsetToTabularBlock)   {
      this.divider = divider;
      this.latitude= latitude;
      this.longitude = longitude;
      this.height = height;
      this.pcode = pcode;
      this.opmode = opmode;
      this.sequenceNumber = sequenceNumber ;
      this.volumeScanNumber = volumeScanNumber ;
      this.volumeScanDate = volumeScanDate ;
      this.volumeScanTime = volumeScanTime ;
      this.productDate = productDate ;
      this.productTime = productTime ;
      this.p1 = p1 ;
      this.p2 = p2 ;
      this.p3 = p3 ;
      this.p4 = p4;
      this.p5 = p5 ;
      this.p6 = p6 ;
      this.p7 = p7 ;
      this.p8 = p8 ;
      this.p9 = p9 ;
      this.p10 = p10 ;
      this.elevationNumber = elevationNumber ;
      this.numberOfMaps = numberOfMaps ;
      this.offsetToSymbologyBlock = offsetToSymbologyBlock ;
      this.offsetToGraphicBlock = offsetToGraphicBlock ;
      this.offsetToTabularBlock = offsetToTabularBlock ;

    }
  }

}



/* Change History:
   $Log: Nidsheader.java,v $
   Revision 1.6  2004/12/09 00:17:32  caron
   *** empty log message ***

   Revision 1.5  2004/12/08 21:46:17  yuanho
   read gate data

   Revision 1.4  2004/12/08 21:38:42  yuanho
   read gate data

   Revision 1.3  2004/12/08 20:41:32  caron
   no message

   Revision 1.2  2004/12/07 21:51:59  yuanho
   test nids code

   Revision 1.1  2004/12/07 21:51:41  yuanho
   test nids code

   Revision 1.5  2004/08/17 19:20:03  caron
   2.2 alpha (2)

   Revision 1.4  2004/08/16 20:53:45  caron
   2.2 alpha (2)

   Revision 1.3  2004/07/12 23:40:16  caron
   2.2 alpha 1.0 checkin

   Revision 1.2  2004/07/06 19:28:09  caron
   pre-alpha checkin

   Revision 1.1.1.1  2003/12/04 21:05:27  caron
   checkin 2.2

 */