package ucar.nc2.iosp.grib;

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

import java.io.*;
import java.util.*;

/**
 * Create a Netcdf File from a ucar.grib.Index
 */
public class Index2NC  {

  private HashMap hcsHash = new HashMap( 10); // GribHorizCoordSys

  public void open(Index index, TableLookup lookup, int version, NetcdfFile ncfile, CancelTask cancelTask) throws IOException {

    // create the HorizCoord Systems : one for each gds
    List hcsList = index.getHorizCoordSys();
    boolean needGroups = (hcsList.size() > 1);
    for (int i = 0; i < hcsList.size(); i++) {
      Index.GdsRecord gdsIndex =  (Index.GdsRecord) hcsList.get(i);

      Group g = null;
      if (needGroups) {
        g = new Group(ncfile, null, "proj"+i);
        ncfile.addGroup( null, g);
      }

      // (Index.GdsRecord gdsIndex, String grid_name, String shape_name, Group g)
      GribHorizCoordSys hcs = new GribHorizCoordSys( gdsIndex, lookup, g);
      // hcs.addToNetcdfFile( ncfile);

      hcsHash.put(gdsIndex.gdsKey, hcs);
     }

    // run through each record
    Index.GribRecord firstRecord = null;
    ArrayList records = index.getGribRecords();
    if (GribServiceProvider.debugOpen) System.out.println(" number of products = "+records.size());
    for( int i = 0; i < records.size(); i++ ) {
      Index.GribRecord gribRecord = (Index.GribRecord) records.get( i );
      if (firstRecord == null) firstRecord = gribRecord;

      GribHorizCoordSys hcs = (GribHorizCoordSys) hcsHash.get(gribRecord.gdsKey);
      String pname = lookup.getParameter(gribRecord).getDescription();
      String vname = lookup.getLevelNameShort( gribRecord);
      String name = pname +"_"+vname;
      GribVariable pv = (GribVariable) hcs.varHash.get(name); // combo gds, param name and levelType name
      if (null == pv) {

        pv = new GribVariable( name, pname, hcs);
        hcs.varHash.put(name, pv);

         // keep track of all products with same desc
        ArrayList plist = (ArrayList) hcs.productHash.get(pname);
        if (null == plist) {
          plist = new ArrayList();
          hcs.productHash.put(pname, plist);
        }
        plist.add( pv);
      }
      pv.addProduct( gribRecord);
    }

    // global stuff
    ncfile.addAttribute(null, new Attribute("Conventions", "CF-1.0"));

    // LOOK : might want to put in groups?
    ncfile.addAttribute(null, new Attribute("Originating_center", lookup.getFirstCenterName() +
        " subcenter = "+lookup.getFirstSubcenterId()) );
    ncfile.addAttribute(null, new Attribute("Product_Status", lookup.getFirstProductStatusName()) );
    ncfile.addAttribute(null, new Attribute("Product_Type", lookup.getFirstProductTypeName()) );
    ncfile.addAttribute(null, new Attribute("FileFormat", "GRIB-"+version));
    ncfile.addAttribute(null, new Attribute("DataType", "GRID"));
    ncfile.addAttribute(null, new Attribute("DatasetLocation", ncfile.getLocation()));
    ncfile.addAttribute(null, new Attribute("Processing", "direct read of GRIB into NetCDF-Java 2.2 API"));

    // loop over HorizCoordSys
    Iterator iterHcs = hcsHash.values().iterator();
    while (iterHcs.hasNext()) {
      GribHorizCoordSys hcs =  (GribHorizCoordSys) iterHcs.next();

      // create the time coordinate : just union on all the times
      // LOOK: only one tcs per GribHorizCoordSys ?
      GribTimeCoordSys tcs = new GribTimeCoordSys();
      Iterator iter = hcs.varHash.values().iterator();
      while (iter.hasNext()) {
        GribVariable pv =  (GribVariable) iter.next();
        List products = pv.getRecords();
        tcs.addTimes( products);
      }

      // create the coordinate systems
      iter = hcs.varHash.values().iterator();
      while (iter.hasNext()) {
        GribVariable pv =  (GribVariable) iter.next();
        List recordList = pv.getRecords();
        Index.GribRecord record = (Index.GribRecord) recordList.get(0);
        String vname = lookup.getLevelNameShort( record);

        GribCoordSys gvcs = (GribCoordSys) hcs.vcsHash.get(vname);
        if (gvcs == null) {
          gvcs = new GribCoordSys(hcs, record, vname, lookup);
          hcs.vcsHash.put( vname, gvcs);
        }

        gvcs.addLevels(  recordList);
        pv.setVertCoordSys( gvcs);
      }

      // add needed dimensions
      tcs.addDimensionsToNetcdfFile( ncfile, hcs.getGroup(), lookup);
      hcs.addDimensionsToNetcdfFile( ncfile);
      iter = hcs.vcsHash.values().iterator();
      while (iter.hasNext()) {
        GribCoordSys gvcs =  (GribCoordSys) iter.next();
        gvcs.addDimensionsToNetcdfFile( ncfile, hcs.getGroup());
      }

      // create a variable for each entry, but check for other products with same desc
      // to disambiguate by vertical coord
      ArrayList products = new ArrayList(hcs.productHash.values());
      Collections.sort( products, new CompareList());
      for (int i = 0; i < products.size(); i++) {
        ArrayList plist = (ArrayList) products.get(i);
        if (plist.size() == 1) {
          GribVariable pv = (GribVariable) plist.get(0);
          ncfile.addVariable( hcs.getGroup(), pv.makeVariable(ncfile, hcs.getGroup(), lookup, true, tcs));
        } else {

          // find the one with the most vertical levels
          int maxLevels = 0;
          GribVariable maxV = null;
          for (int j = 0; j < plist.size(); j++) {
            GribVariable pv = (GribVariable) plist.get(j);
            if (pv.getVertCoordSys().getNLevels() > maxLevels) {
              maxLevels = pv.getVertCoordSys().getNLevels();
              maxV = pv;
            }
          }
          // finally, add the variables
          for (int k = 0; k < plist.size(); k++) {
            GribVariable pv = (GribVariable) plist.get(k);
            ncfile.addVariable( hcs.getGroup(), pv.makeVariable(ncfile, hcs.getGroup(), lookup, (pv == maxV), tcs));
          }
        } // multipe vertical levels
      } // create variable

      // add coordinate systems at end
      tcs.addToNetcdfFile( ncfile, hcs.getGroup(), lookup);
      hcs.addToNetcdfFile( ncfile);
      iter = hcs.vcsHash.values().iterator();
      while (iter.hasNext()) {
        GribCoordSys gvcs =  (GribCoordSys) iter.next();
        gvcs.addToNetcdfFile( ncfile, hcs.getGroup());
      }

    } // loop over hcs
  }

  private class CompareList implements Comparator {

    public int compare(Object o1, Object o2) {
      ArrayList list1 = (ArrayList) o1;
      ArrayList list2 = (ArrayList) o2;

      GribVariable gv1 = (GribVariable) list1.get(0);
      GribVariable gv2 = (GribVariable) list2.get(0);

      return gv1.getName().compareToIgnoreCase( gv2.getName());
    }
  }

}
