package ucar.nc2.iosp.grib;

import ucar.grib.Index;

import ucar.ma2.Array;
import ucar.ma2.InvalidRangeException;
import ucar.ma2.Range;
import ucar.ma2.IndexIterator;

import ucar.nc2.*;
import ucar.nc2.util.CancelTask;

import ucar.unidata.io.RandomAccessFile;

import java.io.*;
import java.util.*;
import java.net.URL;
import java.net.MalformedURLException;

/**
 * superclass for grib1 and grib2 iosp
 */
public abstract class GribServiceProvider implements IOServiceProvider {
  protected RandomAccessFile raf;

  // debugging
  static boolean debugOpen = false, debugMissing = false, debugProj = false, debugTiming = false;
  static public void setDebugFlags(ucar.nc2.util.DebugFlags debugFlag) {
    debugOpen = debugFlag.isSet("Grib/open");
    debugMissing = debugFlag.isSet("Grib/missing");
    debugProj = debugFlag.isSet("Grib/projection");
    debugTiming = debugFlag.isSet("Grib/timing");
  }


  protected Index getIndex(int edition, String location, RandomAccessFile raf, CancelTask cancelTask) throws IOException {

    // get an Index
    Index index = null;
    String indexLocation = location + ".gbx";
    boolean canWrite;

    if (indexLocation.startsWith("http:")) {
      InputStream ios = indexExistsAsURL(indexLocation);
      if (ios == null)
        canWrite = false; // LOOK what to do - need temporary file storage
      else {
        index = new Index();
        index.open(indexLocation, ios);
        if (debugOpen) System.out.println("  opened HTTP index = "+indexLocation);
      }

    } else {

      boolean ok = false;
      File indexLocationFile = new File(indexLocation);
      if (indexLocationFile.exists()) {
        index = new Index();
        ok = index.open(indexLocation);

        if (!ok) {
          index = writeIndex( edition, indexLocation, raf);
          if (debugOpen) System.out.println("  rewrite index = "+indexLocation);
        } else
          if (debugOpen) System.out.println("  opened index = "+indexLocation);
        
      } else {
        // write it for the first time
        index = writeIndex( edition, indexLocation, raf);
        if (debugOpen) System.out.println("  write index = "+indexLocation);
      }

    }

    return index;
  }

   protected Index writeIndex(int edition, String indexLocation, RandomAccessFile raf) throws IOException {
    Index index = null;

    if (edition == 1) {
      // boolean canWrite = indexLocationFile.isWriteable();
      ucar.grib.grib1.Grib1Indexer indexer = new ucar.grib.grib1.Grib1Indexer();
      PrintStream ps =  new PrintStream( new BufferedOutputStream( new FileOutputStream( indexLocation, false )));
      index = indexer.writeFileIndex( raf, ps, true);

    }  else if (edition == 2) {
      // boolean canWrite = indexLocationFile.isWriteable();
      ucar.grib.grib2.Grib2Indexer indexer2 = new ucar.grib.grib2.Grib2Indexer();
      PrintStream ps =  new PrintStream( new BufferedOutputStream( new FileOutputStream( indexLocation, false )));
      index = indexer2.writeFileIndex( raf, ps, true);
    }

    return index;
  }

  // if exists, return input stream, otherwise null
  private InputStream indexExistsAsURL(String indexLocation) throws MalformedURLException {
    try {
      URL url = new URL(indexLocation);
      return url.openStream();
    } catch (MalformedURLException e) {
      throw e;
    }  catch (IOException e) {
      return null;
    }
  }

  /////////////////////////////////////////////////////////////////////////////////////////////////
  public Array readData(Variable v2, List section) throws IOException, InvalidRangeException {
    long start = System.currentTimeMillis();

    Array dataArray = Array.factory( DataType.FLOAT.getClassType(), Range.getShape(section));
    GribVariable pv = (GribVariable) v2.getSPobject();

    int count = 0;
    Range timeRange = (Range) section.get(count++);
    Range levRange = pv.hasVert() ? (Range) section.get(count++) : null;
    Range yRange = (Range) section.get(count++);
    Range xRange = (Range) section.get(count++);

    int xyCount = yRange.length() * xRange.length();
    IndexIterator ii = dataArray.getIndexIteratorFast();

    // loop over time
    for (int timeIdx = timeRange.first(); timeIdx <= timeRange.last(); timeIdx += timeRange.stride()) {
      if (pv.hasVert())
        readLevel(v2, timeIdx, levRange, xyCount, ii);
      else
        readXY(v2, timeIdx, 0, xyCount, ii);
    }

    if (debugTiming) {
        long took = System.currentTimeMillis() - start;
        System.out.println("  read data took="+took+" msec ");
    }

    return dataArray;
  }

  // loop over level
  public void readLevel(Variable v2, int timeIdx, Range levelRange, int xyCount, IndexIterator ii) throws IOException, InvalidRangeException {
    for (int levIdx = levelRange.first(); levIdx <= levelRange.last(); levIdx += levelRange.stride()) {
      readXY(v2, timeIdx, levIdx, xyCount, ii);
    }
  }

  // read one product
  public void readXY(Variable v2, int timeIdx, int levIdx, int xyCount, IndexIterator ii) throws IOException, InvalidRangeException {
    Attribute att = v2.findAttribute("missing_value");
    float missing_value = (att == null) ? -9999.0f : att.getNumericValue().floatValue();

    GribVariable pv = (GribVariable) v2.getSPobject();
    Index.GribRecord record = (Index.GribRecord) pv.findRecord(timeIdx, levIdx);
    if (record == null) {
      for (int j=0; j<xyCount; j++)
        ii.setFloatNext( missing_value);
      return;
    }

    // otherwise read it
    float[] data = null;
    try {
      data = _readData( record.offset1, record.offset2, pv.getDecimalScale(), record.bmsExists);
    } catch (Exception e) {
      e.printStackTrace();
      return;
    }

    for (int i=0; i<data.length; i++)
      ii.setFloatNext( data[i]);
  }

  protected abstract float[] _readData( long offset1, long offset2, int decimalScale, boolean bmsExists ) throws IOException;

  public Array readNestedData(Variable v2, List section, boolean flatten) throws IOException, InvalidRangeException {
    return null;  //To change body of implemented methods use File | Settings | File Templates.
  }

  public void create(String filename, NetcdfFile ncfile, boolean fill) throws IOException {
    //To change body of implemented methods use File | Settings | File Templates.
  }

  public void writeData(Variable v2, List section, Array values) throws IOException, InvalidRangeException {
    //To change body of implemented methods use File | Settings | File Templates.
  }

  public void flush() throws IOException {
    //To change body of implemented methods use File | Settings | File Templates.
  }

  public void close() throws IOException {
    raf.close();
  }

  public void setProperty(String name, String value) {
    //To change body of implemented methods use File | Settings | File Templates.
  }

  public String toStringDebug(Object o) {
    return null;  //To change body of implemented methods use File | Settings | File Templates.
  }


} // end GribServiceProvider
