/*
 * Note: These examples use the old version 1 Java netCDF interface,
 * which we do not recommend for new development. Instead, please use
 * NetCDF Java Library (Version 2), which is more efficient, simpler,
 * and provides better support for remote access using HTTP or
 * DODS. Similar examples are available in the NetCDF Java (version 2)
 * User's Manual.
 */

package ucar.demo;

import ucar.multiarray.ArrayMultiArray;
import ucar.multiarray.IndexIterator;
import ucar.multiarray.MultiArray;
import ucar.multiarray.MultiArrayProxy;
import ucar.multiarray.SliceMap;
import ucar.netcdf.Dimension;
import ucar.netcdf.Netcdf;
import ucar.netcdf.NetcdfFile;
import ucar.netcdf.Variable;

/** 
 * Simple example to overwrite some data and write more data (an
 * additional record, corresponding to a new time) to an existing netCDF
 * file of known structure, corresponding to the following CDL (created
 * by the CreateNetcdf.java demo):
 *
 * <pre>
 *  netcdf example1 {
 *  dimensions:
 *  	lat = 2 ;
 *  	lon = 3 ;
 *  	time = UNLIMITED ;
 *  variables:
 *  	int rh(time, lat, lon) ;
 *              T:long_name="relative humidity" ;
 *  		T:units = "percent" ;
 *  	double T(time, lat, lon) ;
 *              T:long_name="surface temperature" ;
 *  		T:units = "degC" ;
 *  	float lat(lat) ;
 *  		lat:units = "degrees_north" ;
 *  	float lon(lon) ;
 *  		lon:units = "degrees_east" ;
 *  	int time(time) ;
 *  		time:units = "hours" ;
 *  // global attributes:
 *  		:title = "Example Data" ;
 *  data:
 *  }
 * </pre>
 *
 * @author: Russ Rew
 * @version $Id: WriteNetcdf.java,v 1.7 1998/05/20 16:18:33 russ Exp $ */
public class WriteNetcdf {

    static String fileName = "example.nc"; // name of existing file to write

    /** 
     * Writes to an existing netCDF file with a specified file name or
     * the default if no file name is specified.  
     * @param args name of netCDF file to be written, if other than default
     */
    public static void main(String[] args) {
	
	if (args.length > 0)
	    fileName = args[0];

	try {
	    Netcdf nc = new NetcdfFile(fileName,
				       false  // not readonly, open for writing
		                      );

	    /* First just change an existing value by overwriting it:
               increment the value of rh[1][0][2] by 1. */
	    Variable rh = nc.get("rh");
	    int[] index = {1, 0, 2};
	    rh.setInt(index, rh.getInt(index) + 1);
	    /* Note that this would also work if we use a wider type ... */
	    rh.setFloat(index, rh.getFloat(index) + 1);

	    /* Write a new value for the time variable (and hence increment the 
	       corresponding unlimited dimension) */
	    Variable time = nc.get("time");
	    int[] extent = time.getLengths();
	    time.setInt(extent, 24); // add 24 (hours) as a new time value

            /* Append some new relative humidity data for the new time.
               The rank of rh is 3, we want to add a new data slice of
               rank 2. */
	    int[][] rhSlice = { {10, 20, 30, 40}, 
				{20, 30, 40, 50}, 
				{30, 40, 50, 60} };

	    /* Find current length of the unlimited dimension (number of
               "records"), to specify where we want new record written. */
	    Dimension timeD = nc.getDimensions().get("time");
	    int numRecs = timeD.getLength();

	    /* Write the new values of rh one at a time. */
	    int[] origin = new int[3];
	    origin[0] = numRecs - 1; // index of last record
	    for (int ilat = 0; ilat < rhSlice.length; ilat++) {
		origin[1] = ilat;
		for (int ilon = 0; ilon < rhSlice[0].length; ilon++) {
		    origin[2] = ilon;
		    rh.setInt(origin, rhSlice[ilat][ilon]);
		}
	    }

	    /* Here's a way to do the same thing with IndexIterators.
               It only uses one nested loop no matter what the rank of
               the variable. */
	    IndexIterator rhIi = new IndexIterator(
		new int[] {numRecs-1, 0, 0}, // where to start
		rh.getLengths()	             // limits in each dimension
		);
	    MultiArray sliceMa = new ArrayMultiArray(rhSlice);
	    IndexIterator sliceIi = new IndexIterator(sliceMa.getLengths());
	    while(rhIi.notDone()) {
		rh.setInt(rhIi.value(), sliceMa.getInt(sliceIi.value()));
		rhIi.incr();	// increment the IndexIterators
		sliceIi.incr();
	    }
	    
	    /* Write some new temperature data all at once, as a
	       MultiArray.  You might think you could just use a
	       2-dimensional array for the data you want to append to
	       the 3-dimensional temperature MultiArray, but currently
	       the MultiArray.copyin() method requires that the array
	       you are copying in be of the same rank as the MultiArray.  */
	    double[][][] TSlice = {{ 
		{5, 10, 15, 20}, {10, 15, 20, 25}, {15, 20, 25, 30} 
	    }};
	    Variable temperature = nc.get("T");
	    /* Specify where to start writing */
	    origin[0] = numRecs - 1;
	    origin[1] = 0;
	    origin[2] = 0;
	    /* Now write it, using a MultiArray constructed from TSlice. */
	    temperature.copyin(origin, new ArrayMultiArray(TSlice));

	    /* Here's an alternative, where we use a MultiArray adapter
               called SliceMap() to create a target array of the
               2-dimensional slice we want to write.  Then we can use a
               2-dimensional array for MultiArray.copyin().  The
               following overwrites the next-to-last record.  */
	    
	    /* First, create a MultiArrayProxy that adapts the
               underlying 3D MultiArray to make it behave like a 2D
               MultiArray, by fixing its 0th (record) dimension */
	    MultiArray T2d = new MultiArrayProxy(temperature, 
					     new SliceMap(0, numRecs - 2));
	    /* Here's the 2D array we want to write into it */
	    double[][] T2dSlice = {
		{4, 8, 12, 16}, {8, 12, 16, 20}, {12, 16, 20, 24} 
	    };
	    /* This actually does the write to the 3D array in the file,
               by using the 2D proxy */
	    T2d.copyin(new int[2], // just 0's
		       new ArrayMultiArray(T2dSlice));

	    System.out.println("wrote " + fileName + " successfully");

	} catch (java.io.IOException e) {
	    e.printStackTrace();
	}

    }

    /**
     * true to see debug info printed, false for silent running
     */
    static boolean DEBUG = true;

    /**
     * @param s String to print
     */
    static void debug(String s) {
	if (DEBUG)
	    System.out.println(s);
    }

}
