/*
 * To change this license header, choose License Headers in Project Properties.
 * To change this template file, choose Tools | Templates
 * and open the template in the editor.
 */
package cn.ac.csns.nexus.csnsnexustemplate;

import org.apache.commons.io.IOUtils; 

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;

import java.util.ArrayList;
import java.util.Random;
import java.util.logging.Level;
import java.util.logging.Logger;

import org.nexusformat.NXlink;
import org.nexusformat.NexusException;
import org.nexusformat.NexusFile;

/**
 *
 * @author Tang Ming
 */
public class CsnsNexusTemplateGenerator {

    public static final String TOOL_NAME = CsnsNexusTemplateGenerator.class.getCanonicalName();
    public static final String VERSION = "1.0-Alpha";

    public static void genNexusTemplate(String file, String instrumentName, String fileFormat) {
        NexusFile nexusFile = null;

        try {
            if (fileFormat != null) {
                fileFormat = fileFormat.toLowerCase();
                switch (fileFormat) {
                    case "hdf5":
                        nexusFile = new NexusFile(file, NexusFile.NXACC_CREATE5);
                        break;
                    case "hdf4":
                        nexusFile = new NexusFile(file, NexusFile.NXACC_CREATE4);
                        break;
                    case "xml":
                        nexusFile = new NexusFile(file, NexusFile.NXACC_CREATEXML);
                        break;
                    default:
                        throw new NexusException("File format not supported: " + fileFormat + ".");
                }
            } else {
                nexusFile = new NexusFile(file, NexusFile.NXACC_CREATE5);
            }
        } catch (NexusException ex) {
            Logger.getLogger(CsnsNexusTemplateGenerator.class.getName()).log(Level.SEVERE, ex.getMessage(), ex);
            System.exit(0);
        }

        try {
            genGlobalAttributes(nexusFile);
            // NXentry
            nexusFile.makegroup("entry", "NXentry");
            nexusFile.opengroup("entry", "NXentry");
            // Top level fields
            genPublicFields(nexusFile, instrumentName);
            // NXuser
            genUserInfo(nexusFile);
            // NXprocess
            genProcess(nexusFile);
            // NXlog
            genLogs(nexusFile);
            // NXinstrument
            genInstrument(nexusFile, instrumentName);
            // NXmonitor
            //genMonitor(nexusFile);
            // NXdata
            genHistogramData(nexusFile, instrumentName);
            // NXevent_data
            genEventData(nexusFile, instrumentName);
            nexusFile.closegroup();
        } catch (NexusException ex) {
            Logger.getLogger(CsnsNexusTemplateGenerator.class.getName()).log(Level.SEVERE, ex.getMessage(), ex);
        } finally {
            try {
                nexusFile.close();
            } catch (NexusException ex) {
                Logger.getLogger(CsnsNexusTemplateGenerator.class.getName()).log(Level.SEVERE, ex.getMessage(), ex);
            }
        }

    }

    private static void genGlobalAttributes(NexusFile nexusFile) throws NexusException {
        String creator = TOOL_NAME + ", version: " + VERSION;
        nexusFile.putattr("creator", creator.getBytes(), NexusFile.NX_CHAR);
    }

    private static void genUserInfo(NexusFile nexusFile) throws NexusException {
        int[] dim;
        dim = new int[1];
        final String facility_user_id = "zhangsan";
        final String name = "Zhang San";
        final String role = "principal_investigator";
        final String affiliation = "CSNS";
        final String address = "Zhongziyuan Road No.1, Dongguan, Guangdong, China";
        final String email = "zhangsan@gmail.com";

        nexusFile.makegroup("user1", "NXuser");
        nexusFile.opengroup("user1", "NXuser");

        // write facility_user_id
        dim[0] = facility_user_id.length();
        nexusFile.makedata("facility_user_id", NexusFile.NX_CHAR, 1, dim);
        nexusFile.opendata("facility_user_id");
        nexusFile.putdata(facility_user_id.getBytes());
        nexusFile.closedata();

        // write name
        dim[0] = name.length();
        nexusFile.makedata("name", NexusFile.NX_CHAR, 1, dim);
        nexusFile.opendata("name");
        nexusFile.putdata(name.getBytes());
        nexusFile.closedata();

        // write role
        dim[0] = role.length();
        nexusFile.makedata("role", NexusFile.NX_CHAR, 1, dim);
        nexusFile.opendata("role");
        nexusFile.putdata(role.getBytes());
        nexusFile.closedata();

        // write affiliation
        dim[0] = affiliation.length();
        nexusFile.makedata("affiliation", NexusFile.NX_CHAR, 1, dim);
        nexusFile.opendata("affiliation");
        nexusFile.putdata(affiliation.getBytes());
        nexusFile.closedata();

        // write address
        dim[0] = address.length();
        nexusFile.makedata("address", NexusFile.NX_CHAR, 1, dim);
        nexusFile.opendata("address");
        nexusFile.putdata(address.getBytes());
        nexusFile.closedata();

        // write email
        dim[0] = email.length();
        nexusFile.makedata("email", NexusFile.NX_CHAR, 1, dim);
        nexusFile.opendata("email");
        nexusFile.putdata(email.getBytes());
        nexusFile.closedata();

        nexusFile.closegroup();
    }

    private static void genProcess(NexusFile nexusFile) throws NexusException {
        int[] dim;
        dim = new int[1];
        final String author = "Li Si";
        final String algorithm = "GPPD_builder_1.0.py";
        final String parameters = "-l 1 -c \"6Li.dat\" -i \"BLxx_xxxx_0000001_001.raw\"";
        final String description = "This is event data builder.";
        final String data = algorithm + " " + parameters;
        final String date = "2015-05-21T16:49:36+08:00";

        nexusFile.makegroup("process", "NXprocess");
        nexusFile.opengroup("process", "NXprocess");

        nexusFile.makegroup("process1", "NXnote");
        nexusFile.opengroup("process1", "NXnote");

        // write author
        dim[0] = author.length();
        nexusFile.makedata("author", NexusFile.NX_CHAR, 1, dim);
        nexusFile.opendata("author");
        nexusFile.putdata(author.getBytes());
        nexusFile.closedata();

        // write value
        dim[0] = data.length();
        nexusFile.makedata("data", NexusFile.NX_CHAR, 1, dim);
        nexusFile.opendata("data");
        nexusFile.putdata(data.getBytes());
        nexusFile.closedata();

        // write description
        dim[0] = description.length();
        nexusFile.makedata("description", NexusFile.NX_CHAR, 1, dim);
        nexusFile.opendata("description");
        nexusFile.putdata(description.getBytes());
        nexusFile.closedata();

        // write date
        dim[0] = date.length();
        nexusFile.makedata("date", NexusFile.NX_CHAR, 1, dim);
        nexusFile.opendata("date");
        nexusFile.putdata(date.getBytes());
        nexusFile.closedata();

        nexusFile.closegroup();

        nexusFile.closegroup();
    }

    private static void genLogs(NexusFile nexusFile) throws NexusException {
        int rank = 1;
        int[] dim;
        int size = 2;
        float[] value;
        float[] time;
        value = new float[]{0.00f, 15.00f};
        time = new float[]{0.00f, 25.00f};
        dim = new int[1];
        dim[0] = 2;

        nexusFile.makegroup("logs", "NXcollection");
        nexusFile.opengroup("logs", "NXcollection");

//        // write accelerator frequency
//        nexusFile.makegroup("AcceleratorFrequency", "NXlog");
//        nexusFile.opengroup("AcceleratorFrequency", "NXlog");
//        nexusFile.makedata("time", NexusFile.NX_FLOAT32, rank, dim);
//        nexusFile.opendata("time");
//        nexusFile.putdata(time);
//        nexusFile.putattr("units", "second".getBytes(), NexusFile.NX_CHAR);
//        nexusFile.closedata();
//        nexusFile.makedata("value", NexusFile.NX_FLOAT32, rank, dim);
//        nexusFile.opendata("value");
//        nexusFile.putdata(value);
//        nexusFile.putattr("units", "hertz".getBytes(), NexusFile.NX_CHAR);
//        nexusFile.closedata();
//        nexusFile.closegroup();
        // write proton charge
        nexusFile.makegroup("proton_charge", "NXlog");
        nexusFile.opengroup("proton_charge", "NXlog");
        nexusFile.makedata("time", NexusFile.NX_FLOAT32, rank, dim);
        nexusFile.opendata("time");
        nexusFile.putdata(time);
        nexusFile.putattr("units", NexusUnit.NX_TIME.getBytes(), NexusFile.NX_CHAR);
        nexusFile.closedata();
        nexusFile.makedata("value", NexusFile.NX_FLOAT32, rank, dim);
        nexusFile.opendata("value");
        nexusFile.putdata(value);
        nexusFile.putattr("units", NexusUnit.NX_CHARGE.getBytes(), NexusFile.NX_CHAR);
        nexusFile.closedata();
        nexusFile.closegroup();

        // write good frame
        nexusFile.makegroup("good_frame", "NXlog");
        nexusFile.opengroup("good_frame", "NXlog");
        nexusFile.makedata("time", NexusFile.NX_FLOAT32, rank, dim);
        nexusFile.opendata("time");
        nexusFile.putdata(time);
        nexusFile.putattr("units", NexusUnit.NX_TIME.getBytes(), NexusFile.NX_CHAR);
        nexusFile.closedata();
        nexusFile.makedata("value", NexusFile.NX_FLOAT32, rank, dim);
        nexusFile.opendata("value");
        nexusFile.putdata(value);
        nexusFile.putattr("units", "frames".getBytes(), NexusFile.NX_CHAR);
        nexusFile.closedata();
        nexusFile.closegroup();

//        // write chopper set speed (fixed)
//        nexusFile.makegroup("ChopperSetSpeed1", "NXlog");
//        nexusFile.opengroup("ChopperSetSpeed1", "NXlog");
//        nexusFile.makedata("time", NexusFile.NX_FLOAT32, rank, new int[]{1});
//        nexusFile.opendata("time");
//        nexusFile.putdata(new float[]{0.0f});
//        nexusFile.putattr("units", "second".getBytes(), NexusFile.NX_CHAR);
//        nexusFile.closedata();
//        nexusFile.makedata("value", NexusFile.NX_FLOAT32, rank, new int[]{1});
//        nexusFile.opendata("value");
//        nexusFile.putdata(new float[]{25.00f});
//        nexusFile.putattr("units", "hertz".getBytes(), NexusFile.NX_CHAR);
//        nexusFile.closedata();
//        nexusFile.closegroup();
//        // write chopper set phase (fixed)
//        nexusFile.makegroup("ChopperSetPhase1", "NXlog");
//        nexusFile.opengroup("ChopperSetPhase1", "NXlog");
//        nexusFile.makedata("time", NexusFile.NX_FLOAT32, rank, new int[]{1});
//        nexusFile.opendata("time");
//        nexusFile.putdata(new float[]{0.0f});
//        nexusFile.putattr("units", "second".getBytes(), NexusFile.NX_CHAR);
//        nexusFile.closedata();
//        nexusFile.makedata("value", NexusFile.NX_FLOAT32, rank, new int[]{1});
//        nexusFile.opendata("value");
//        nexusFile.putdata(new float[]{25.00f});
//        nexusFile.putattr("units", "microsecond".getBytes(), NexusFile.NX_CHAR);
//        nexusFile.closedata();
//        nexusFile.closegroup();
//        // write chopper speed
//        nexusFile.makegroup("ChopperSpeed1", "NXlog");
//        nexusFile.opengroup("ChopperSpeed1", "NXlog");
//        nexusFile.makedata("time", NexusFile.NX_FLOAT32, rank, dim);
//        nexusFile.opendata("time");
//        nexusFile.putdata(time);
//        nexusFile.putattr("units", "second".getBytes(), NexusFile.NX_CHAR);
//        nexusFile.closedata();
//        nexusFile.makedata("value", NexusFile.NX_FLOAT32, rank, dim);
//        nexusFile.opendata("value");
//        nexusFile.putdata(value);
//        nexusFile.putattr("units", "hertz".getBytes(), NexusFile.NX_CHAR);
//        nexusFile.closedata();
//        nexusFile.closegroup();
        // write chopper phase
        nexusFile.makegroup("disk_chopper_phase1", "NXlog");
        nexusFile.opengroup("disk_chopper_phase1", "NXlog");
        nexusFile.makedata("time", NexusFile.NX_FLOAT32, rank, dim);
        nexusFile.opendata("time");
        nexusFile.putdata(time);
        nexusFile.putattr("units", NexusUnit.NX_TIME.getBytes(), NexusFile.NX_CHAR);
        nexusFile.closedata();
        nexusFile.makedata("value", NexusFile.NX_FLOAT32, rank, dim);
        nexusFile.opendata("value");
        nexusFile.putdata(value);
        nexusFile.putattr("units", NexusUnit.NX_TIME.getBytes(), NexusFile.NX_CHAR);
        nexusFile.closedata();
        nexusFile.closegroup();

//        // write slit top position (fixed)
//        nexusFile.makegroup("SlitTop1", "NXlog");
//        nexusFile.opengroup("SlitTop1", "NXlog");
//        nexusFile.makedata("time", NexusFile.NX_FLOAT32, rank, new int[]{1});
//        nexusFile.opendata("time");
//        nexusFile.putdata(new float[]{0.0f});
//        nexusFile.putattr("units", "second".getBytes(), NexusFile.NX_CHAR);
//        nexusFile.closedata();
//        nexusFile.makedata("value", NexusFile.NX_FLOAT32, rank, new int[]{1});
//        nexusFile.opendata("value");
//        nexusFile.putdata(new float[]{25.00f});
//        nexusFile.putattr("units", "millimeter".getBytes(), NexusFile.NX_CHAR);
//        nexusFile.closedata();
//        nexusFile.closegroup();
//        // write slit bottom position (fixed)
//        nexusFile.makegroup("SlitBottom1", "NXlog");
//        nexusFile.opengroup("SlitBottom1", "NXlog");
//        nexusFile.makedata("time", NexusFile.NX_FLOAT32, rank, new int[]{1});
//        nexusFile.opendata("time");
//        nexusFile.putdata(new float[]{0.0f});
//        nexusFile.putattr("units", "second".getBytes(), NexusFile.NX_CHAR);
//        nexusFile.closedata();
//        nexusFile.makedata("value", NexusFile.NX_FLOAT32, rank, new int[]{1});
//        nexusFile.opendata("value");
//        nexusFile.putdata(new float[]{25.00f});
//        nexusFile.putattr("units", "millimeter".getBytes(), NexusFile.NX_CHAR);
//        nexusFile.closedata();
//        nexusFile.closegroup();
//        // write slit left position (fixed)
//        nexusFile.makegroup("SlitLeft1", "NXlog");
//        nexusFile.opengroup("SlitLeft1", "NXlog");
//        nexusFile.makedata("time", NexusFile.NX_FLOAT32, rank, new int[]{1});
//        nexusFile.opendata("time");
//        nexusFile.putdata(new float[]{0.0f});
//        nexusFile.putattr("units", "second".getBytes(), NexusFile.NX_CHAR);
//        nexusFile.closedata();
//        nexusFile.makedata("value", NexusFile.NX_FLOAT32, rank, new int[]{1});
//        nexusFile.opendata("value");
//        nexusFile.putdata(new float[]{25.00f});
//        nexusFile.putattr("units", "millimeter".getBytes(), NexusFile.NX_CHAR);
//        nexusFile.closedata();
//        nexusFile.closegroup();
//        // write slit top position (fixed)
//        nexusFile.makegroup("SlitRight1", "NXlog");
//        nexusFile.opengroup("SlitRight1", "NXlog");
//        nexusFile.makedata("time", NexusFile.NX_FLOAT32, rank, new int[]{1});
//        nexusFile.opendata("time");
//        nexusFile.putdata(new float[]{0.0f});
//        nexusFile.putattr("units", "second".getBytes(), NexusFile.NX_CHAR);
//        nexusFile.closedata();
//        nexusFile.makedata("value", NexusFile.NX_FLOAT32, rank, new int[]{1});
//        nexusFile.opendata("value");
//        nexusFile.putdata(new float[]{25.00f});
//        nexusFile.putattr("units", "millimeter".getBytes(), NexusFile.NX_CHAR);
//        nexusFile.closedata();
//        nexusFile.closegroup();
//        // write sample x position (fixed)
//        nexusFile.makegroup("SampleX", "NXlog");
//        nexusFile.opengroup("SampleX", "NXlog");
//        nexusFile.makedata("time", NexusFile.NX_FLOAT32, rank, new int[]{1});
//        nexusFile.opendata("time");
//        nexusFile.putdata(new float[]{0.0f});
//        nexusFile.putattr("units", "second".getBytes(), NexusFile.NX_CHAR);
//        nexusFile.closedata();
//        nexusFile.makedata("value", NexusFile.NX_FLOAT32, rank, new int[]{1});
//        nexusFile.opendata("value");
//        nexusFile.putdata(new float[]{25.00f});
//        nexusFile.putattr("units", "millimeter".getBytes(), NexusFile.NX_CHAR);
//        nexusFile.closedata();
//        nexusFile.closegroup();
//        // write sample x position (fixed)
//        nexusFile.makegroup("SampleY", "NXlog");
//        nexusFile.opengroup("SampleY", "NXlog");
//        nexusFile.makedata("time", NexusFile.NX_FLOAT32, rank, new int[]{1});
//        nexusFile.opendata("time");
//        nexusFile.putdata(new float[]{0.0f});
//        nexusFile.putattr("units", "second".getBytes(), NexusFile.NX_CHAR);
//        nexusFile.closedata();
//        nexusFile.makedata("value", NexusFile.NX_FLOAT32, rank, new int[]{1});
//        nexusFile.opendata("value");
//        nexusFile.putdata(new float[]{25.00f});
//        nexusFile.putattr("units", "millimeter".getBytes(), NexusFile.NX_CHAR);
//        nexusFile.closedata();
//        nexusFile.closegroup();
//        // write sample x position (fixed)
//        nexusFile.makegroup("SampleZ", "NXlog");
//        nexusFile.opengroup("SampleZ", "NXlog");
//        nexusFile.makedata("time", NexusFile.NX_FLOAT32, rank, new int[]{1});
//        nexusFile.opendata("time");
//        nexusFile.putdata(new float[]{0.0f});
//        nexusFile.putattr("units", "second".getBytes(), NexusFile.NX_CHAR);
//        nexusFile.closedata();
//        nexusFile.makedata("value", NexusFile.NX_FLOAT32, rank, new int[]{1});
//        nexusFile.opendata("value");
//        nexusFile.putdata(new float[]{25.00f});
//        nexusFile.putattr("units", "millimeter".getBytes(), NexusFile.NX_CHAR);
//        nexusFile.closedata();
//        nexusFile.closegroup();
//        // write sample phi position (fixed)
//        nexusFile.makegroup("SamplePhi", "NXlog");
//        nexusFile.opengroup("SamplePhi", "NXlog");
//        nexusFile.makedata("time", NexusFile.NX_FLOAT32, rank, new int[]{1});
//        nexusFile.opendata("time");
//        nexusFile.putdata(new float[]{0.0f});
//        nexusFile.putattr("units", "second".getBytes(), NexusFile.NX_CHAR);
//        nexusFile.closedata();
//        nexusFile.makedata("value", NexusFile.NX_FLOAT32, rank, new int[]{1});
//        nexusFile.opendata("value");
//        nexusFile.putdata(new float[]{25.00f});
//        nexusFile.putattr("units", "degree".getBytes(), NexusFile.NX_CHAR);
//        nexusFile.closedata();
//        nexusFile.closegroup();
//        // write sample phi position (fixed)
//        nexusFile.makegroup("SampleOmega", "NXlog");
//        nexusFile.opengroup("SampleOmega", "NXlog");
//        nexusFile.makedata("time", NexusFile.NX_FLOAT32, rank, new int[]{1});
//        nexusFile.opendata("time");
//        nexusFile.putdata(new float[]{0.0f});
//        nexusFile.putattr("units", "second".getBytes(), NexusFile.NX_CHAR);
//        nexusFile.closedata();
//        nexusFile.makedata("value", NexusFile.NX_FLOAT32, rank, new int[]{1});
//        nexusFile.opendata("value");
//        nexusFile.putdata(new float[]{25.00f});
//        nexusFile.putattr("units", "degree".getBytes(), NexusFile.NX_CHAR);
//        nexusFile.closedata();
//        nexusFile.closegroup();
        genSampleEnvLogs(nexusFile);

        nexusFile.closegroup();
    }

    private static void genSampleEnvLogs(NexusFile nexusFile) throws NexusException {
        int rank = 1;
        int[] dim;
        float[] value;
        float[] time;
        value = new float[]{0.00f, 15.00f};
        time = new float[]{0.00f, 25.00f};
        dim = new int[1];
        dim[0] = 2;

        nexusFile.makegroup("sample_environment", "NXcollection");
        nexusFile.opengroup("sample_environment", "NXcollection");

        // write sample temperature
        nexusFile.makegroup("temperature", "NXlog");
        nexusFile.opengroup("temperature", "NXlog");
        nexusFile.makedata("time", NexusFile.NX_FLOAT32, rank, dim);
        nexusFile.opendata("time");
        nexusFile.putdata(time);
        nexusFile.putattr("units", NexusUnit.NX_TIME.getBytes(), NexusFile.NX_CHAR);
        nexusFile.closedata();
        nexusFile.makedata("value", NexusFile.NX_FLOAT32, rank, dim);
        nexusFile.opendata("value");
        nexusFile.putdata(value);
        nexusFile.putattr("units", NexusUnit.NX_TEMPERATURE.getBytes(), NexusFile.NX_CHAR);
        nexusFile.closedata();
        nexusFile.closegroup();

        // write sample magnetic field
        nexusFile.makegroup("magnetic_field", "NXlog");
        nexusFile.opengroup("magnetic_field", "NXlog");
        nexusFile.makedata("time", NexusFile.NX_FLOAT32, rank, dim);
        nexusFile.opendata("time");
        nexusFile.putdata(time);
        nexusFile.putattr("units", NexusUnit.NX_TIME.getBytes(), NexusFile.NX_CHAR);
        nexusFile.closedata();
        nexusFile.makedata("value", NexusFile.NX_FLOAT32, rank, dim);
        nexusFile.opendata("value");
        nexusFile.putdata(value);
        nexusFile.putattr("units", NexusUnit.NX_MAGNETIC_FIELD.getBytes(), NexusFile.NX_CHAR);
        nexusFile.closedata();
        nexusFile.closegroup();

        // write sample electric field
        nexusFile.makegroup("electric_field", "NXlog");
        nexusFile.opengroup("electric_field", "NXlog");
        nexusFile.makedata("time", NexusFile.NX_FLOAT32, rank, dim);
        nexusFile.opendata("time");
        nexusFile.putdata(time);
        nexusFile.putattr("units", NexusUnit.NX_TIME.getBytes(), NexusFile.NX_CHAR);
        nexusFile.closedata();
        nexusFile.makedata("value", NexusFile.NX_FLOAT32, rank, dim);
        nexusFile.opendata("value");
        nexusFile.putdata(value);
        nexusFile.putattr("units", NexusUnit.NX_ELECTRIC_FIELD.getBytes(), NexusFile.NX_CHAR);
        nexusFile.closedata();
        nexusFile.closegroup();

        // write sample electric field
        nexusFile.makegroup("pressure", "NXlog");
        nexusFile.opengroup("pressure", "NXlog");
        nexusFile.makedata("time", NexusFile.NX_FLOAT32, rank, dim);
        nexusFile.opendata("time");
        nexusFile.putdata(time);
        nexusFile.putattr("units", NexusUnit.NX_TIME.getBytes(), NexusFile.NX_CHAR);
        nexusFile.closedata();
        nexusFile.makedata("value", NexusFile.NX_FLOAT32, rank, dim);
        nexusFile.opendata("value");
        nexusFile.putdata(value);
        nexusFile.putattr("units", NexusUnit.NX_PRESSURE.getBytes(), NexusFile.NX_CHAR);
        nexusFile.closedata();
        nexusFile.closegroup();

        nexusFile.closegroup();
    }

    private static void genInstrument(NexusFile nexusFile, String instrumentName) throws NexusException {
        nexusFile.makegroup("instrument", "NXinstrument");
        nexusFile.opengroup("instrument", "NXinstrument");

        genSource(nexusFile);
        genModerator(nexusFile);
        genAperture(nexusFile, instrumentName);
        genBeamStop(nexusFile, instrumentName);
        genDiskChopper(nexusFile);
        genFlipper(nexusFile);
        genPolarizer(nexusFile);
        genEnvironment(nexusFile);
        genMonitor(nexusFile);
        genSample(nexusFile, instrumentName);
        genDetector(nexusFile, instrumentName);

        nexusFile.closegroup();
    }

    /**
     * Generate description of the source.
     *
     * @param nexusFile the handler of the NexusFile.
     * @throws NexusException
     */
    private static void genSource(NexusFile nexusFile) throws NexusException {
        int[] dim;
        dim = new int[1];

        nexusFile.makegroup("source", "NXsource");
        nexusFile.opengroup("source", "NXsource");

        // write name
        dim[0] = "CSNS".length();
        nexusFile.makedata("name", NexusFile.NX_CHAR, 1, dim);
        nexusFile.opendata("name");
        nexusFile.putdata("CSNS".getBytes());
        nexusFile.closedata();

        // write type
        dim[0] = "Spallation Neutron Source".length();
        nexusFile.makedata("type", NexusFile.NX_CHAR, 1, dim);
        nexusFile.opendata("type");
        nexusFile.putdata("Spallation Neutron Source".getBytes());
        nexusFile.closedata();

        // write probe
        dim[0] = "neutron".length();
        nexusFile.makedata("probe", NexusFile.NX_CHAR, 1, dim);
        nexusFile.opendata("probe");
        nexusFile.putdata("neutron".getBytes());
        nexusFile.closedata();

        // write frequency
        nexusFile.makedata("frequency", NexusFile.NX_FLOAT32, 1, new int[]{1});
        nexusFile.opendata("frequency");
        nexusFile.putdata(new float[]{25.00f});
        nexusFile.putattr("units", NexusUnit.NX_FREQUENCY.getBytes(), NexusFile.NX_CHAR);
        nexusFile.closedata();

        // write facility power
        nexusFile.makedata("power", NexusFile.NX_FLOAT32, 1, new int[]{1});
        nexusFile.opendata("power");
        nexusFile.putdata(new float[]{-1.00f});
        nexusFile.putattr("units", NexusUnit.NX_POWER.getBytes(), NexusFile.NX_CHAR);
        nexusFile.closedata();

        // write current
        //nexusFile.makedata("current", NexusFile.NX_FLOAT32, 1, new int[]{1});
        //nexusFile.opendata("current");
        //nexusFile.putdata(new float[]{-1.00f});
        //nexusFile.putattr("units", NexusUnit.NX_CURRENT.getBytes(), NexusFile.NX_CHAR);
        //nexusFile.closedata();
        // write voltage
        //nexusFile.makedata("voltage", NexusFile.NX_FLOAT32, 1, new int[]{1});
        //nexusFile.opendata("voltage");
        //nexusFile.putdata(new float[]{-1.00f});
        //nexusFile.putattr("units", NexusUnit.NX_VOLTAGE.getBytes(), NexusFile.NX_CHAR);
        //nexusFile.closedata();
        nexusFile.closegroup();
    }

    private static void genModerator(NexusFile nexusFile) throws NexusException {
        int[] dim;
        dim = new int[1];

        nexusFile.makegroup("moderator", "NXmoderator");
        nexusFile.opengroup("moderator", "NXmoderator");

        // write type
        // type can be one of these:
        // Liquid H2 | Solid CH4
        String type = "Liquid H2";
        dim[0] = type.length();
        nexusFile.makedata("type", NexusFile.NX_CHAR, 1, dim);
        nexusFile.opendata("type");
        nexusFile.putdata(type.getBytes());
        nexusFile.closedata();

        // write distance
        nexusFile.makedata("distance", NexusFile.NX_FLOAT32, 1, new int[]{1});
        nexusFile.opendata("distance");
        nexusFile.putdata(new float[]{30.00f});
        nexusFile.putattr("units", NexusUnit.NX_LENGTH.getBytes(), NexusFile.NX_CHAR);
        nexusFile.closedata();

        // write coupled
        // type can be one of these:
        // 1(TRUE) | 0(FALSE)
        //dim[0] = "true".length();
        nexusFile.makedata("coupled", NexusFile.NX_BOOLEAN, 1, new int[]{1});
        nexusFile.opendata("coupled");
        nexusFile.putdata(new short[]{0});
        nexusFile.closedata();

        // write posison material
        // can be: Gd | Cd | NONE
        dim[0] = "Gd".length();
        nexusFile.makedata("poison_material", NexusFile.NX_CHAR, 1, dim);
        nexusFile.opendata("poison_material");
        nexusFile.putdata("NONE".getBytes());
        nexusFile.closedata();

        // write temperature
        nexusFile.makedata("temperature", NexusFile.NX_FLOAT32, 1, new int[]{1});
        nexusFile.opendata("temperature");
        nexusFile.putdata(new float[]{20.00f});
        nexusFile.putattr("units", NexusUnit.NX_TEMPERATURE.getBytes(), NexusFile.NX_CHAR);
        nexusFile.closedata();

        nexusFile.closegroup();
    }

    private static void genAperture(NexusFile nexusFile, String instrumentName) throws NexusException {
        int[] dim;
        dim = new int[1];

        nexusFile.makegroup("aperture1", "NXaperture");
        nexusFile.opengroup("aperture1", "NXaperture");

        nexusFile.makegroup("geometry", "NXgeometry");
        nexusFile.opengroup("geometry", "NXgeometry");

        nexusFile.makegroup("shape", "NXshape");
        nexusFile.opengroup("shape", "NXshape");
        // write shape
        // can be: nxflat | nxcylinder | nxbox | nxsphere | nxcone | nxelliptical
        // | nxtoroidal |nxparabolic | nxpolynomial
        // see NeXus Manual
        if (instrumentName.equalsIgnoreCase("GPPD") || instrumentName.equalsIgnoreCase("MR")) {
            dim[0] = "nxbox".length();
            nexusFile.makedata("shape", NexusFile.NX_CHAR, 1, dim);
            nexusFile.opendata("shape");
            nexusFile.putdata("nxbox".getBytes());
            nexusFile.closedata();
            // write size
            // for nxbox, the size is (length, width, height)
            dim[0] = 3;
            float[] size = new float[]{0.04f, -1.0f, 0.1f};
            nexusFile.makedata("size", NexusFile.NX_FLOAT32, 1, dim);
            nexusFile.opendata("size");
            nexusFile.putdata(size);
            nexusFile.putattr("units", NexusUnit.NX_LENGTH.getBytes(), NexusFile.NX_CHAR);
            nexusFile.closedata();
            // write direction
            // can be: concave | convex
            //dim[0] = "convex".length();
            //nexusFile.makedata("direction", NexusFile.NX_CHAR, 1, dim);
            //nexusFile.opendata("direction");
            //nexusFile.putdata("convex".getBytes());
            //nexusFile.closedata();
        }
        if (instrumentName.equalsIgnoreCase("SANS")) {
            dim[0] = "nxelliptical".length();
            nexusFile.makedata("shape", NexusFile.NX_CHAR, 1, dim);
            nexusFile.opendata("shape");
            nexusFile.putdata("nxelliptical".getBytes());
            nexusFile.closedata();
            // write size
            // for nxelliptical, the size is (semi-major-axis, semi-minor-axis, angle)
            dim[0] = 3;
            float[] size = new float[]{0.01f, 0.01f, 90f};
            nexusFile.makedata("size", NexusFile.NX_FLOAT32, 1, dim);
            nexusFile.opendata("size");
            nexusFile.putdata(size);
            nexusFile.putattr("units", (NexusUnit.NX_LENGTH + "," + NexusUnit.NX_LENGTH + "," + NexusUnit.NX_ANGLE).getBytes(), NexusFile.NX_CHAR);
            nexusFile.closedata();
            // write direction
            // can be: concave | convex
            //dim[0] = "convex".length();
            //nexusFile.makedata("direction", NexusFile.NX_CHAR, 1, dim);
            //nexusFile.opendata("direction");
            //nexusFile.putdata("convex".getBytes());
            //nexusFile.closedata();
        }
        nexusFile.closegroup(); // shape

        nexusFile.makegroup("translation", "NXtranslation");
        nexusFile.opengroup("translation", "NXtranslation");
        // write distances
        // for nxbox, the size is (length, width, height)
        dim[0] = 3;
        float[] distances = new float[]{0.0f, 0.0f, 10.0f};
        nexusFile.makedata("distances", NexusFile.NX_FLOAT32, 1, dim);
        nexusFile.opendata("distances");
        nexusFile.putdata(distances);
        nexusFile.putattr("units", NexusUnit.NX_LENGTH.getBytes(), NexusFile.NX_CHAR);
        nexusFile.closedata();
        nexusFile.closegroup();  // translation

        nexusFile.makegroup("orientation", "NXorientation");
        nexusFile.opengroup("orientation", "NXorientation");
        // write distances
        // for nxbox, the size is (length, width, height)
        dim[0] = 6;
        float[] value = new float[]{1f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f};
        nexusFile.makedata("value", NexusFile.NX_FLOAT32, 1, dim);
        nexusFile.opendata("value");
        nexusFile.putdata(value);
        nexusFile.closedata();
        nexusFile.closegroup();  // orientation

        nexusFile.closegroup(); // geometry 

        // The offset of the aperture
        nexusFile.makedata("offset", NexusFile.NX_FLOAT32, 1, new int[]{2});
        nexusFile.opendata("offset");
        nexusFile.putdata(new float[]{0.01f, 0.01f});
        nexusFile.putattr("units", NexusUnit.NX_LENGTH.getBytes(), NexusFile.NX_CHAR);
        nexusFile.closedata();

        nexusFile.closegroup();  // aperture
    }

    private static void genBeamStop(NexusFile nexusFile, String instrumentName) throws NexusException {
        int[] dim;
        dim = new int[1];

        if (instrumentName.equalsIgnoreCase("SANS")) {

            nexusFile.makegroup("beam_stop", "NXbeam_stop");
            nexusFile.opengroup("beam_stop", "NXbeam_stop");

            nexusFile.makegroup("geometry", "NXgeometry");
            nexusFile.opengroup("geometry", "NXgeometry");

            nexusFile.makegroup("shape", "NXshape");
            nexusFile.opengroup("shape", "NXshape");

            dim[0] = "nxelliptical".length();
            nexusFile.makedata("shape", NexusFile.NX_CHAR, 1, dim);
            nexusFile.opendata("shape");
            nexusFile.putdata("nxelliptical".getBytes());
            nexusFile.closedata();
            // write size
            // for nxelliptical, the size is (semi-major-axis, semi-minor-axis, angle)
            dim[0] = 3;
            float[] size = new float[]{0.01f, 0.01f, 90f};
            nexusFile.makedata("size", NexusFile.NX_FLOAT32, 1, dim);
            nexusFile.opendata("size");
            nexusFile.putdata(size);
            nexusFile.putattr("units", (NexusUnit.NX_LENGTH + "," + NexusUnit.NX_LENGTH + "," + NexusUnit.NX_ANGLE).getBytes(), NexusFile.NX_CHAR);
            nexusFile.closedata();
            // write direction
            // can be: concave | convex
            //dim[0] = "convex".length();
            //nexusFile.makedata("direction", NexusFile.NX_CHAR, 1, dim);
            //nexusFile.opendata("direction");
            //nexusFile.putdata("convex".getBytes());
            //nexusFile.closedata();

            nexusFile.closegroup(); // shape

            nexusFile.makegroup("translation", "NXtranslation");
            nexusFile.opengroup("translation", "NXtranslation");
            // write distances
            // for nxbox, the size is (length, width, height)
            dim[0] = 3;
            float[] distances = new float[]{0.0f, 0.0f, 15.0f};
            nexusFile.makedata("distances", NexusFile.NX_FLOAT32, 1, dim);
            nexusFile.opendata("distances");
            nexusFile.putdata(distances);
            nexusFile.putattr("units", NexusUnit.NX_LENGTH.getBytes(), NexusFile.NX_CHAR);
            nexusFile.closedata();
            nexusFile.closegroup();  // translation

            nexusFile.makegroup("orientation", "NXorientation");
            nexusFile.opengroup("orientation", "NXorientation");
            // write distances
            // for nxbox, the size is (length, width, height)
            dim[0] = 6;
            float[] value = new float[]{1f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f};
            nexusFile.makedata("value", NexusFile.NX_FLOAT32, 1, dim);
            nexusFile.opendata("value");
            nexusFile.putdata(value);
            nexusFile.closedata();
            nexusFile.closegroup();  // orientation

            nexusFile.closegroup(); // geometry 

            // The offset of the aperture
            nexusFile.makedata("offset", NexusFile.NX_FLOAT32, 1, new int[]{2});
            nexusFile.opendata("offset");
            nexusFile.putdata(new float[]{0.00f, 0.00f});
            nexusFile.putattr("units", NexusUnit.NX_LENGTH.getBytes(), NexusFile.NX_CHAR);
            nexusFile.closedata();

            // write description
            // can be: circular | rectangular
            //dim[0] = "circular".length();
            //nexusFile.makedata("description", NexusFile.NX_CHAR, 1, dim);
            //nexusFile.opendata("description");
            //nexusFile.putdata("circular".getBytes());
            //nexusFile.closedata();
            // write the size of the beam stop
            //dim[0] = 1;
            //float[] size = new float[]{0.03f};
            //nexusFile.makedata("size", NexusFile.NX_FLOAT32, 1, dim);
            //nexusFile.opendata("size");
            //nexusFile.putdata(size);
            //nexusFile.putattr("units", "meter".getBytes(), NexusFile.NX_CHAR);
            //nexusFile.closedata();
            // write the x position of the beam stop in relation to the detector
            //nexusFile.makedata("x", NexusFile.NX_FLOAT32, 1, dim);
            //nexusFile.opendata("x");
            //nexusFile.putdata(new float[]{0.0f});
            //nexusFile.putattr("units", "meter".getBytes(), NexusFile.NX_CHAR);
            //nexusFile.closedata();
            // write the y position of the beam stop in relation to the detector
            //nexusFile.makedata("y", NexusFile.NX_FLOAT32, 1, dim);
            //nexusFile.opendata("y");
            //nexusFile.putdata(new float[]{0.0f});
            //nexusFile.putattr("units", "meter".getBytes(), NexusFile.NX_CHAR);
            //nexusFile.closedata();
            // write the distance of the beam stop to the detector
            //nexusFile.makedata("distance_to_detector", NexusFile.NX_FLOAT32, 1, dim);
            //nexusFile.opendata("distance_to_detector");
            //nexusFile.putdata(new float[]{1.0f});
            //nexusFile.putattr("units", "meter".getBytes(), NexusFile.NX_CHAR);
            //nexusFile.closedata();
            // write status
            // can be: in | out
            //dim[0] = "in".length();
            //nexusFile.makedata("status", NexusFile.NX_CHAR, 1, dim);
            //nexusFile.opendata("status");
            //nexusFile.putdata("in".getBytes());
            //nexusFile.closedata();
            nexusFile.closegroup();
        }
    }

    private static void genDiskChopper(NexusFile nexusFile) throws NexusException {
        int[] dim;
        dim = new int[1];

        nexusFile.makegroup("disk_chopper1", "NXdisk_chopper");
        nexusFile.opengroup("disk_chopper1", "NXdisk_chopper");

        // write type
        // can be: Chopper type single | contra_rotating_pair | synchro_pair
        //dim[0] = "contra_rotating_pair".length();
        //nexusFile.makedata("type", NexusFile.NX_CHAR, 1, dim);
        //nexusFile.opendata("type");
        //nexusFile.putdata("contra_rotating_pair".getBytes());
        //nexusFile.closedata();
        // write rotation speed
        nexusFile.makedata("rotation_speed", NexusFile.NX_FLOAT32, 1, new int[]{1});
        nexusFile.opendata("rotation_speed");
        nexusFile.putdata(new float[]{25.00f});
        nexusFile.putattr("units", NexusUnit.NX_FREQUENCY.getBytes(), NexusFile.NX_CHAR);
        nexusFile.closedata();

        // write the number of slits
        //nexusFile.makedata("slits", NexusFile.NX_INT32, 1, new int[]{1});
        //nexusFile.opendata("slits");
        //nexusFile.putdata(new int[]{1});
        //nexusFile.closedata();
        // write angular opening of slit
        dim[0] = 1;
        nexusFile.makedata("slit_angle", NexusFile.NX_FLOAT32, 1, dim);
        nexusFile.opendata("slit_angle");
        nexusFile.putdata(new float[]{30.00f});
        nexusFile.putattr("units", NexusUnit.NX_ANGLE.getBytes(), NexusFile.NX_CHAR);
        nexusFile.closedata();

        // write disc spacing in direction of beam
        //nexusFile.makedata("pair_separation", NexusFile.NX_FLOAT32, 1, dim);
        //nexusFile.opendata("pair_separation");
        //nexusFile.putdata(new float[]{0.002f});
        //nexusFile.putattr("units", "meter".getBytes(), NexusFile.NX_CHAR);
        //nexusFile.closedata();
        // write radius to center of slit
        //nexusFile.makedata("radius", NexusFile.NX_FLOAT32, 1, dim);
        //nexusFile.opendata("radius");
        //nexusFile.putdata(new float[]{0.5f});
        //nexusFile.putattr("units", "meter".getBytes(), NexusFile.NX_CHAR);
        //nexusFile.closedata();
        // write the total slit height
        //nexusFile.makedata("slit_height", NexusFile.NX_FLOAT32, 1, dim);
        //nexusFile.opendata("slit_height");
        //nexusFile.putdata(new float[]{0.03f});
        //nexusFile.putattr("units", "meter".getBytes(), NexusFile.NX_CHAR);
        //nexusFile.closedata();
        // write the phase angle of the chopper
        nexusFile.makedata("phase", NexusFile.NX_FLOAT32, 1, dim);
        nexusFile.opendata("phase");
        nexusFile.putdata(new float[]{10.00f});
        nexusFile.putattr("units", NexusUnit.NX_ANGLE.getBytes(), NexusFile.NX_CHAR);
        nexusFile.closedata();

        // write the effective distance to the origin
        nexusFile.makedata("distance", NexusFile.NX_FLOAT32, 1, dim);
        nexusFile.opendata("distance");
        nexusFile.putdata(new float[]{20.00f});
        nexusFile.putattr("units", NexusUnit.NX_LENGTH.getBytes(), NexusFile.NX_CHAR);
        nexusFile.closedata();

        nexusFile.closegroup();
    }

    private static void genFlipper(NexusFile nexusFile) throws NexusException {
        int[] dim;
        dim = new int[1];

        nexusFile.makegroup("flipper1", "NXflipper");
        nexusFile.opengroup("flipper1", "NXflipper");

        // write type
        // can be: coil | current-sheet
        dim[0] = "coil".length();
        nexusFile.makedata("type", NexusFile.NX_CHAR, 1, dim);
        nexusFile.opendata("type");
        nexusFile.putdata("coil".getBytes());
        nexusFile.closedata();

        nexusFile.closegroup();
    }

    private static void genPolarizer(NexusFile nexusFile) throws NexusException {
        int[] dim;
        dim = new int[1];

        nexusFile.makegroup("polarizer1", "NXpolarizer");
        nexusFile.opengroup("polarizer1", "NXpolarizer");

        // write type
        // can be: crystal | supermirror | 3He
        dim[0] = "crystal".length();
        nexusFile.makedata("type", NexusFile.NX_CHAR, 1, dim);
        nexusFile.opendata("type");
        nexusFile.putdata("crystal".getBytes());
        nexusFile.closedata();

        nexusFile.closegroup();
    }

    /**
     * Describe an external condition applied to the sample
     *
     * @param nexusFile the handler to a NexusFile
     * @throws NexusException
     */
    private static void genEnvironment(NexusFile nexusFile) throws NexusException {
        int[] dim;
        dim = new int[1];

        nexusFile.makegroup("environment", "NXenvironment");
        nexusFile.opengroup("environment", "NXenvironment");

        // write name
        // Apparatus identification code/model number; e.g. OC100 011
        dim[0] = "OC100 011".length();
        nexusFile.makedata("name", NexusFile.NX_CHAR, 1, dim);
        nexusFile.opendata("name");
        nexusFile.putdata("OC100 011".getBytes());
        nexusFile.closedata();

        // write type
        // Type of apparatus. This could be the SE codes in scheduling database; e.g. OC/100
        dim[0] = "OC/100".length();
        nexusFile.makedata("type", NexusFile.NX_CHAR, 1, dim);
        nexusFile.opendata("type");
        nexusFile.putdata("OC/100".getBytes());
        nexusFile.closedata();

        nexusFile.closegroup();
    }

    private static void genSample(NexusFile nexusFile, String instrumentName) throws NexusException {
        int[] dim;
        dim = new int[1];

        nexusFile.makegroup("sample", "NXsample");
        nexusFile.opengroup("sample", "NXsample");

        // write the name of sample
        dim[0] = "Silicon".length();
        nexusFile.makedata("name", NexusFile.NX_CHAR, 1, dim);
        nexusFile.opendata("name");
        nexusFile.putdata("Silicon".getBytes());
        nexusFile.closedata();

        // write the chemical formula of sample
        dim[0] = "Si".length();
        nexusFile.makedata("chemical_formula", NexusFile.NX_CHAR, 1, dim);
        nexusFile.opendata("chemical_formula");
        nexusFile.putdata("Si".getBytes());
        nexusFile.closedata();

        // write the changer position
        //nexusFile.makedata("changer_position", NexusFile.NX_INT32, 1, new int[]{1});
        //nexusFile.opendata("changer_position");
        //nexusFile.putdata(new int[]{1});
        //nexusFile.closedata();
        // write the mass of the sample
        nexusFile.makedata("mass", NexusFile.NX_FLOAT32, 1, new int[]{1});
        nexusFile.opendata("mass");
        nexusFile.putdata(new float[]{5f});
        nexusFile.putattr("units", NexusUnit.NX_MASS.getBytes(), NexusFile.NX_CHAR);
        nexusFile.closedata();

        // write the density of sample
        nexusFile.makedata("density", NexusFile.NX_FLOAT32, 1, new int[]{1});
        nexusFile.opendata("density");
        nexusFile.putdata(new float[]{1.8f});
        nexusFile.putattr("units", NexusUnit.NX_MASS_DENSITY.getBytes(), NexusFile.NX_CHAR);
        nexusFile.closedata();

        // write the type of sample
        // can be: sample | sample+can | can | calibration sample | normalisation sample
        // | simulated data | sample environment | none | sample environment
        dim[0] = "sample".length();
        nexusFile.makedata("type", NexusFile.NX_CHAR, 1, dim);
        nexusFile.opendata("type");
        nexusFile.putdata("sample".getBytes());
        nexusFile.closedata();

        // write the situation
        // can be: air | vacuum
        dim[0] = "vacuum".length();
        nexusFile.makedata("situation", NexusFile.NX_CHAR, 1, dim);
        nexusFile.opendata("situation");
        nexusFile.putdata("vacuum".getBytes());
        nexusFile.closedata();

        // write the thickness of sample
        //nexusFile.makedata("thickness", NexusFile.NX_FLOAT32, 1, new int[]{1});
        //nexusFile.opendata("thickness");
        //nexusFile.putdata(new float[]{0.005f});
        //nexusFile.putattr("units", "meter".getBytes(), NexusFile.NX_CHAR);
        //nexusFile.closedata();
        // write translation of the sample along the Z-direction of the laboratory coordinate system
        nexusFile.makedata("distance", NexusFile.NX_FLOAT32, 1, new int[]{1});
        nexusFile.opendata("distance");
        nexusFile.putdata(new float[]{30.0f});
        nexusFile.putattr("units", NexusUnit.NX_LENGTH.getBytes(), NexusFile.NX_CHAR);
        nexusFile.closedata();

        // write the position and orientation of the center of mass of the sample
        nexusFile.makegroup("geometry", "NXgeometry");
        nexusFile.opengroup("geometry", "NXgeometry");

        nexusFile.makegroup("shape", "NXshape");
        nexusFile.opengroup("shape", "NXshape");
        // write shape
        // can be: nxflat | nxcylinder | nxbox | nxsphere | nxcone | nxelliptical
        // | nxtoroidal |nxparabolic | nxpolynomial
        // see NeXus Manual
        if (instrumentName.equalsIgnoreCase("GPPD")) {
            dim[0] = "nxcylinder".length();
            nexusFile.makedata("shape", NexusFile.NX_CHAR, 1, dim);
            nexusFile.opendata("shape");
            nexusFile.putdata("nxcylinder".getBytes());
            nexusFile.closedata();
            // write size
            // for nxcylinder, the size is (diameter, height) and a three value orientation vector
            dim[0] = 5;
            float[] size = new float[]{0.01f, 0.1f, 0.0f, 1.0f, 0.0f};
            nexusFile.makedata("size", NexusFile.NX_FLOAT32, 1, dim);
            nexusFile.opendata("size");
            nexusFile.putdata(size);
            nexusFile.putattr("units", NexusUnit.NX_LENGTH.getBytes(), NexusFile.NX_CHAR);
            nexusFile.closedata();
            // write direction
            // can be: concave | convex
            //dim[0] = "convex".length();
            //nexusFile.makedata("direction", NexusFile.NX_CHAR, 1, dim);
            //nexusFile.opendata("direction");
            //nexusFile.putdata("convex".getBytes());
            //nexusFile.closedata();
        }
        if (instrumentName.equalsIgnoreCase("MR")) {
            dim[0] = "nxbox".length();
            nexusFile.makedata("shape", NexusFile.NX_CHAR, 1, dim);
            nexusFile.opendata("shape");
            nexusFile.putdata("nxbox".getBytes());
            nexusFile.closedata();
            // write size
            // for nxbox, the size is (length, width, height)
            dim[0] = 3;
            float[] size = new float[]{0.01f, 0.001f, 0.01f};
            nexusFile.makedata("size", NexusFile.NX_FLOAT32, 1, dim);
            nexusFile.opendata("size");
            nexusFile.putdata(size);
            nexusFile.putattr("units", NexusUnit.NX_LENGTH.getBytes(), NexusFile.NX_CHAR);
            nexusFile.closedata();
            // write direction
            // can be: concave | convex
            //dim[0] = "convex".length();
            //nexusFile.makedata("direction", NexusFile.NX_CHAR, 1, dim);
            //nexusFile.opendata("direction");
            //nexusFile.putdata("convex".getBytes());
            //nexusFile.closedata();
        }
        // the sample shape of SANS is a disk or a box
        if (instrumentName.equalsIgnoreCase("SANS")) {
            dim[0] = "nxbox".length();
            nexusFile.makedata("shape", NexusFile.NX_CHAR, 1, dim);
            nexusFile.opendata("shape");
            nexusFile.putdata("nxbox".getBytes());
            nexusFile.closedata();
            // write size
            // if the third value of the size is negetive, then the shape is a disk.
            // for disk, the size is (diameter, thickness)
            dim[0] = 3;
            float[] size = new float[]{0.02f, 0.001f, -1.0f};  // a disk sample
            nexusFile.makedata("size", NexusFile.NX_FLOAT32, 1, dim);
            nexusFile.opendata("size");
            nexusFile.putdata(size);
            nexusFile.putattr("units", NexusUnit.NX_LENGTH.getBytes(), NexusFile.NX_CHAR);
            nexusFile.closedata();
            // write direction
            // can be: concave | convex
            //dim[0] = "convex".length();
            //nexusFile.makedata("direction", NexusFile.NX_CHAR, 1, dim);
            //nexusFile.opendata("direction");
            //nexusFile.putdata("convex".getBytes());
            //nexusFile.closedata();
        }
        nexusFile.closegroup(); // shape

        //nexusFile.makegroup("translation", "NXtranslation");
        // nexusFile.opengroup("translation", "NXtranslation");
        // write distances
        //dim[0] = 3;
        //float[] distances = new float[]{0.0f, 0.0f, 10.0f};
        //nexusFile.makedata("distances", NexusFile.NX_FLOAT32, 1, dim);
        //nexusFile.opendata("distances");
        //nexusFile.putdata(distances);
        //nexusFile.putattr("units", "meter".getBytes(), NexusFile.NX_CHAR);
        //nexusFile.closedata();
        //nexusFile.closegroup();  // translation
        //nexusFile.makegroup("orientation", "NXorientation");
        //nexusFile.opengroup("orientation", "NXorientation");
        // write distances
        //dim[0] = 6;
        //float[] value = new float[]{0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f};
        //nexusFile.makedata("value", NexusFile.NX_FLOAT32, 1, dim);
        //nexusFile.opendata("value");
        //nexusFile.putdata(value);
        //nexusFile.closedata();
        //nexusFile.closegroup();  // orientation
        nexusFile.closegroup(); // geometry

        nexusFile.closegroup();
    }

    private static void genDetector(NexusFile nexusFile, String instrumentName) throws NexusException {
        String detectorName = "detector";
        String localName = "detector";
        int x_pixel = 4;
        int y_pixel = 128;
        int eventsNumber = 1485;
        int clockPulses = 60;
        int[] dim;
        dim = new int[1];


        ArrayList<String> bankGroup = new ArrayList<String>();
        ArrayList<String> bankGroupName = new ArrayList<String>();
        if (instrumentName.equalsIgnoreCase("GPPD")) {
            for (int i=0;i<6;++i){
                bankGroup.add("bank"+i);
                bankGroupName.add("backward"+i);
            }
        }else if (instrumentName.equalsIgnoreCase("SANS")) {
                bankGroup.add("bank");
                bankGroupName.add("main");
        }else if (instrumentName.equalsIgnoreCase("MR")) {
                bankGroup.add("bank");
                bankGroupName.add("main");
        }

        for (int i=0;i<bankGroup.size();++i){
            nexusFile.makegroup(bankGroup.get(i), "NXdetector");
            nexusFile.opengroup(bankGroup.get(i), "NXdetector");
            
            // local name for the detector
            writeData(nexusFile, "local_name", NexusFile.NX_CHAR, 1, new int[]{bankGroupName.get(i).length()}, bankGroupName.get(i).getBytes());
            // distance of bank-to-sample
            //writeData(nexufFile, "distance",   NexusFile.NX_FLOAT32, 1, new int[]{1}, float(40.0+i));
            nexusFile.makedata("distance", NexusFile.NX_FLOAT32, 1, new int[]{1});
            nexusFile.opendata("distance");
            nexusFile.putdata(new float[]{40+i});
            nexusFile.putattr("units", NexusUnit.NX_LENGTH.getBytes(), NexusFile.NX_CHAR);
            nexusFile.closedata();
            // pixel id
            int[][] ids = genIntData2D(x_pixel, y_pixel, 0, 1);
            nexusFile.makedata("pixel_id", NexusFile.NX_INT32, 2, new int[]{x_pixel, y_pixel});
            nexusFile.opendata("pixel_id");
            nexusFile.putdata(ids);
            nexusFile.closedata();
            // spectrum index
            int[] spectrumIndex = genIntData1D(x_pixel * y_pixel, 0, 1);
            nexusFile.makedata("spectrum_index", NexusFile.NX_INT32, 1, new int[]{x_pixel * y_pixel});
            nexusFile.opendata("spectrum_index");
            nexusFile.putdata(spectrumIndex);
            nexusFile.closedata();
            // polar angle
            dim = new int[2];
            dim[0] = x_pixel;
            dim[1] = y_pixel;
            float[][] polar_angle = genFloatData2D(x_pixel, y_pixel, 100);
            nexusFile.makedata("polar_angle", NexusFile.NX_FLOAT32, 2, dim);
            nexusFile.opendata("polar_angle");
            nexusFile.putdata(polar_angle);
            nexusFile.putattr("units", NexusUnit.NX_ANGLE.getBytes(), NexusFile.NX_CHAR);
            nexusFile.closedata();
            // azimuthal angle
            float[][] azimuthal_angle = genFloatData2D(x_pixel, y_pixel, 100);
            nexusFile.makedata("azimuthal_angle", NexusFile.NX_FLOAT32, 2, dim);
            nexusFile.opendata("azimuthal_angle");
            nexusFile.putdata(azimuthal_angle);
            nexusFile.putattr("units", NexusUnit.NX_ANGLE.getBytes(), NexusFile.NX_CHAR);
            nexusFile.closedata();
            // x pixel offset
            dim = new int[1];
            dim[0] = x_pixel;
            float[] x_pixel_offset = genFloatData1D(x_pixel, 1);
            nexusFile.makedata("x_pixel_offset", NexusFile.NX_FLOAT32, 1, dim);
            nexusFile.opendata("x_pixel_offset");
            nexusFile.putdata(x_pixel_offset);
            nexusFile.putattr("axis", new int[]{1}, NexusFile.NX_INT32);
            nexusFile.putattr("primary", new int[]{1}, NexusFile.NX_INT32);
            nexusFile.putattr("long_name", "x pixel offset".getBytes(), NexusFile.NX_CHAR);
            nexusFile.putattr("units", NexusUnit.NX_LENGTH.getBytes(), NexusFile.NX_CHAR);
            nexusFile.closedata();
            // y pixel offset
            dim[0] = y_pixel;
            float[] y_pixel_offset = genFloatData1D(y_pixel, 1);
            nexusFile.makedata("y_pixel_offset", NexusFile.NX_FLOAT32, 1, dim);
            nexusFile.opendata("y_pixel_offset");
            nexusFile.putdata(y_pixel_offset);
            nexusFile.putattr("axis", new int[]{2}, NexusFile.NX_INT32);
            nexusFile.putattr("primary", new int[]{1}, NexusFile.NX_INT32);
            nexusFile.putattr("long_name", "y pixel offset".getBytes(), NexusFile.NX_CHAR);
            nexusFile.putattr("units", NexusUnit.NX_LENGTH.getBytes(), NexusFile.NX_CHAR);
            nexusFile.closedata();
            // z pixel offset
            dim[0] = 1;
            float[] z_pixel_offset = new float[]{0.0f};
            nexusFile.makedata("z_pixel_offset", NexusFile.NX_FLOAT32, 1, dim);
            nexusFile.opendata("z_pixel_offset");
            nexusFile.putdata(z_pixel_offset);
            nexusFile.putattr("axis", new int[]{3}, NexusFile.NX_INT32);
            nexusFile.putattr("primary", new int[]{1}, NexusFile.NX_INT32);
            nexusFile.putattr("long_name", "z pixel offset".getBytes(), NexusFile.NX_CHAR);
            nexusFile.putattr("units", NexusUnit.NX_LENGTH.getBytes(), NexusFile.NX_CHAR);
            nexusFile.closedata();
            // x pixel size
            dim = new int[2];
            dim[0] = x_pixel;
            dim[1] = y_pixel;
            float[][] x_pixel_size = genFloatData2D(x_pixel, y_pixel, 1);
            nexusFile.makedata("x_pixel_size", NexusFile.NX_FLOAT32, 2, dim);
            nexusFile.opendata("x_pixel_size");
            nexusFile.putdata(x_pixel_size);
            nexusFile.putattr("units", NexusUnit.NX_LENGTH.getBytes(), NexusFile.NX_CHAR);
            nexusFile.closedata();
            // y pixel size
            float[][] y_pixel_size = genFloatData2D(x_pixel, y_pixel, 1);
            nexusFile.makedata("y_pixel_size", NexusFile.NX_FLOAT32, 2, dim);
            nexusFile.opendata("y_pixel_size");
            nexusFile.putdata(y_pixel_size);
            nexusFile.putattr("units", NexusUnit.NX_LENGTH.getBytes(), NexusFile.NX_CHAR);
            nexusFile.closedata();
            // time of flight
            float[] timeOfFlight = genFloatData1D(eventsNumber, 1000);
            nexusFile.makedata("time_of_flight", NexusFile.NX_FLOAT32, 1, new int[]{eventsNumber});
            nexusFile.opendata("time_of_flight");
            nexusFile.putdata(timeOfFlight);
            nexusFile.putattr("units", NexusUnit.NX_TIME_OF_FLIGHT.getBytes(), NexusFile.NX_CHAR);
            nexusFile.putattr("axis", new int[]{3}, NexusFile.NX_INT32);
            nexusFile.putattr("primary", new int[]{1}, NexusFile.NX_INT32);
            nexusFile.putattr("long_name", "Time Of Flight (s)".getBytes(), NexusFile.NX_CHAR);
            nexusFile.closedata();

            // event pixel id
            int[] eventPixelId = genIntRandom1D(eventsNumber, 0, x_pixel * y_pixel);
            nexusFile.makedata("event_pixel_id", NexusFile.NX_INT32, 1, new int[]{eventsNumber});
            nexusFile.opendata("event_pixel_id");
            nexusFile.putdata(eventPixelId);
            nexusFile.closedata();

            // event time of flight
            int[] eventTimeOfFligh = genIntRandom1D(eventsNumber, 0, clockPulses);
            nexusFile.makedata("event_time_of_flight", NexusFile.NX_INT32, 1, new int[]{eventsNumber});
            nexusFile.opendata("event_time_of_flight");
            nexusFile.putdata(eventTimeOfFligh);
            nexusFile.putattr("units", NexusUnit.NX_TIME_OF_FLIGHT.getBytes(), NexusFile.NX_CHAR);
            nexusFile.closedata();

            // event pulse time with respect to the offset
            float[] eventPulseTime = genFloatPulseTime(eventsNumber, 0.04f);
            nexusFile.makedata("event_pulse_time", NexusFile.NX_FLOAT32, 1, new int[]{eventsNumber});
            nexusFile.opendata("event_pulse_time");
            nexusFile.putdata(eventPulseTime);
            nexusFile.putattr("units", NexusUnit.NX_TIME_OF_FLIGHT.getBytes(), NexusFile.NX_CHAR);
            nexusFile.closedata();

            nexusFile.closegroup();
        }
    }

    // See monitor as a kind of detector. 
    private static void genMonitor(NexusFile nexusFile) throws NexusException {
        String monitorName = "monitor1";
        String localName = "GEM";
        int x_pixel = 16;
        int y_pixel = 16;
        int eventsNumber = 785;
        int clockPulses = 60;
        int[] dim;
        dim = new int[1];

        nexusFile.makegroup(monitorName, "NXdetector");
        nexusFile.opengroup(monitorName, "NXdetector");

        // local name for the detector
        dim[0] = localName.length();
        nexusFile.makedata("local_name", NexusFile.NX_CHAR, 1, dim);
        nexusFile.opendata("local_name");
        nexusFile.putdata(localName.getBytes());
        nexusFile.closedata();

        // distance from the source
        nexusFile.makedata("distance", NexusFile.NX_FLOAT32, 1, new int[]{1});
        nexusFile.opendata("distance");
        nexusFile.putdata(new float[]{20.0f});
        nexusFile.putattr("units", NexusUnit.NX_LENGTH.getBytes(), NexusFile.NX_CHAR);
        nexusFile.closedata();

        // pixel id
        int[][] ids = genIntData2D(x_pixel, y_pixel, 0, 1);
        nexusFile.makedata("pixel_id", NexusFile.NX_INT32, 2, new int[]{x_pixel, y_pixel});
        nexusFile.opendata("pixel_id");
        nexusFile.putdata(ids);
        nexusFile.closedata();

        // spectrum index
        int[] spectrumIndex = genIntData1D(x_pixel * y_pixel, 0, 1);
        nexusFile.makedata("spectrum_index", NexusFile.NX_INT32, 1, new int[]{x_pixel * y_pixel});
        nexusFile.opendata("spectrum_index");
        nexusFile.putdata(spectrumIndex);
        //nexusFile.putattr("units", NexusUnit.NX_ANGLE.getBytes(), NexusFile.NX_CHAR);
        nexusFile.closedata();

        // polar angle
        dim = new int[2];
        dim[0] = x_pixel;
        dim[1] = y_pixel;
        float[][] polar_angle = genFloatData2D(x_pixel, y_pixel, 100);
        nexusFile.makedata("polar_angle", NexusFile.NX_FLOAT32, 2, dim);
        nexusFile.opendata("polar_angle");
        nexusFile.putdata(polar_angle);
        nexusFile.putattr("units", NexusUnit.NX_ANGLE.getBytes(), NexusFile.NX_CHAR);
        nexusFile.closedata();

        // polar angle
        float[][] azimuthal_angle = genFloatData2D(x_pixel, y_pixel, 100);
        nexusFile.makedata("azimuthal_angle", NexusFile.NX_FLOAT32, 2, dim);
        nexusFile.opendata("azimuthal_angle");
        nexusFile.putdata(azimuthal_angle);
        nexusFile.putattr("units", NexusUnit.NX_ANGLE.getBytes(), NexusFile.NX_CHAR);
        nexusFile.closedata();

        // x pixel offset
        dim = new int[1];
        dim[0] = x_pixel;
        float[] x_pixel_offset = genFloatData1D(x_pixel, 1);
        nexusFile.makedata("x_pixel_offset", NexusFile.NX_FLOAT32, 1, dim);
        nexusFile.opendata("x_pixel_offset");
        nexusFile.putdata(x_pixel_offset);
        nexusFile.putattr("axis", new int[]{1}, NexusFile.NX_INT32);
        nexusFile.putattr("primary", new int[]{1}, NexusFile.NX_INT32);
        nexusFile.putattr("long_name", "x pixel offset".getBytes(), NexusFile.NX_CHAR);
        nexusFile.putattr("units", NexusUnit.NX_LENGTH.getBytes(), NexusFile.NX_CHAR);
        nexusFile.closedata();

        // y pixel offset
        dim[0] = y_pixel;
        float[] y_pixel_offset = genFloatData1D(y_pixel, 1);
        nexusFile.makedata("y_pixel_offset", NexusFile.NX_FLOAT32, 1, dim);
        nexusFile.opendata("y_pixel_offset");
        nexusFile.putdata(y_pixel_offset);
        nexusFile.putattr("axis", new int[]{2}, NexusFile.NX_INT32);
        nexusFile.putattr("primary", new int[]{1}, NexusFile.NX_INT32);
        nexusFile.putattr("long_name", "y pixel offset".getBytes(), NexusFile.NX_CHAR);
        nexusFile.putattr("units", NexusUnit.NX_LENGTH.getBytes(), NexusFile.NX_CHAR);
        nexusFile.closedata();

        // z pixel offset
        dim[0] = 1;
        float[] z_pixel_offset = new float[]{0.0f};
        nexusFile.makedata("z_pixel_offset", NexusFile.NX_FLOAT32, 1, dim);
        nexusFile.opendata("z_pixel_offset");
        nexusFile.putdata(z_pixel_offset);
        nexusFile.putattr("axis", new int[]{3}, NexusFile.NX_INT32);
        nexusFile.putattr("primary", new int[]{1}, NexusFile.NX_INT32);
        nexusFile.putattr("long_name", "z pixel offset".getBytes(), NexusFile.NX_CHAR);
        nexusFile.putattr("units", NexusUnit.NX_LENGTH.getBytes(), NexusFile.NX_CHAR);
        nexusFile.closedata();

        // x pixel size
        dim = new int[2];
        dim[0] = x_pixel;
        dim[1] = y_pixel;
        float[][] x_pixel_size = genFloatData2D(x_pixel, y_pixel, 1);
        nexusFile.makedata("x_pixel_size", NexusFile.NX_FLOAT32, 2, dim);
        nexusFile.opendata("x_pixel_size");
        nexusFile.putdata(x_pixel_size);
        nexusFile.putattr("units", NexusUnit.NX_LENGTH.getBytes(), NexusFile.NX_CHAR);
        nexusFile.closedata();

        // y pixel size
        float[][] y_pixel_size = genFloatData2D(x_pixel, y_pixel, 1);
        nexusFile.makedata("y_pixel_size", NexusFile.NX_FLOAT32, 2, dim);
        nexusFile.opendata("y_pixel_size");
        nexusFile.putdata(y_pixel_size);
        nexusFile.putattr("units", NexusUnit.NX_LENGTH.getBytes(), NexusFile.NX_CHAR);
        nexusFile.closedata();

        // time of flight
        float[] timeOfFlight = genFloatData1D(eventsNumber, 1000);
        nexusFile.makedata("time_of_flight", NexusFile.NX_FLOAT32, 1, new int[]{eventsNumber});
        nexusFile.opendata("time_of_flight");
        nexusFile.putdata(timeOfFlight);
        nexusFile.putattr("units", NexusUnit.NX_TIME_OF_FLIGHT.getBytes(), NexusFile.NX_CHAR);
        nexusFile.putattr("axis", new int[]{3}, NexusFile.NX_INT32);
        nexusFile.putattr("primary", new int[]{1}, NexusFile.NX_INT32);
        nexusFile.putattr("long_name", "Time Of Flight (s)".getBytes(), NexusFile.NX_CHAR);
        nexusFile.closedata();

        // event pixel id
        int[] eventPixelId = genIntRandom1D(eventsNumber, 0, x_pixel * y_pixel);
        nexusFile.makedata("event_pixel_id", NexusFile.NX_INT32, 1, new int[]{eventsNumber});
        nexusFile.opendata("event_pixel_id");
        nexusFile.putdata(eventPixelId);
        //nexusFile.putattr("units", NexusUnit.NX_LENGTH.getBytes(), NexusFile.NX_CHAR);
        nexusFile.closedata();

        // event time of flight
        int[] eventTimeOfFlight = genIntRandom1D(eventsNumber, 0, clockPulses);
        nexusFile.makedata("event_time_of_flight", NexusFile.NX_INT32, 1, new int[]{eventsNumber});
        nexusFile.opendata("event_time_of_flight");
        nexusFile.putdata(eventTimeOfFlight);
        nexusFile.putattr("units", NexusUnit.NX_TIME_OF_FLIGHT.getBytes(), NexusFile.NX_CHAR);
        nexusFile.closedata();

        // event pulse time with respect to the offset
        float[] eventPulseTime = genFloatPulseTime(eventsNumber, 0.04f);
        nexusFile.makedata("event_pulse_time", NexusFile.NX_FLOAT32, 1, new int[]{eventsNumber});
        nexusFile.opendata("event_pulse_time");
        nexusFile.putdata(eventPulseTime);
        nexusFile.putattr("units", NexusUnit.NX_TIME_OF_FLIGHT.getBytes(), NexusFile.NX_CHAR);
        nexusFile.closedata();

        nexusFile.closegroup();
    }

    @Deprecated
    private static void genMonitor_Old(NexusFile nexusFile) throws NexusException {
        int[] dim;
        dim = new int[1];

        nexusFile.makegroup("monitor1", "NXmonitor");
        nexusFile.opengroup("monitor1", "NXmonitor");

        // run mode of the monitor
        // can be: monitor | timer 
        dim[0] = "monitor".length();
        nexusFile.makedata("mode", NexusFile.NX_CHAR, 1, dim);
        nexusFile.opendata("mode");
        nexusFile.putdata("monitor".getBytes());
        nexusFile.closedata();

        // start time
        String time = "2015-05-26T14:42:23+08:00";
        dim[0] = time.length();
        nexusFile.makedata("start_time", NexusFile.NX_CHAR, 1, dim);
        nexusFile.opendata("start_time");
        nexusFile.putdata(time.getBytes());
        nexusFile.closedata();

        // end time
        time = "2015-05-27T14:42:23+08:00";
        dim[0] = time.length();
        nexusFile.makedata("end_time", NexusFile.NX_CHAR, 1, dim);
        nexusFile.opendata("end_time");
        nexusFile.putdata(time.getBytes());
        nexusFile.closedata();

        // distance of monitor from sample
        nexusFile.makedata("distance", NexusFile.NX_FLOAT32, 1, new int[]{1});
        nexusFile.opendata("distance");
        nexusFile.putdata(new float[]{0.3f});
        nexusFile.putattr("units", NexusUnit.NX_LENGTH.getBytes(), NexusFile.NX_CHAR);
        nexusFile.closedata();

        nexusFile.closegroup();
    }

    private static void genHistogramData(NexusFile nexusFile, String instrumentName) throws NexusException {
        String groupName = "histogram_data";
        String XPixelOffsetPath;
        String YPixelOffsetPath;
        String TimeOfFlightPath;
        String XPixelOffsetLink;
        String YPixelOffsetLink;
        String TimeOfFlightLink;
        
        ArrayList<String> bankGroup = new ArrayList<String>();
        if (instrumentName.equalsIgnoreCase("GPPD")) {
            for (int i=0;i<6;++i){
                bankGroup.add("bank"+i);
            }
        }else if (instrumentName.equalsIgnoreCase("SANS")) {
                bankGroup.add("bank");
        }else if (instrumentName.equalsIgnoreCase("MR")) {
                bankGroup.add("bank");
        }

        //nexusFile.openpath("/entry");
        nexusFile.makegroup(groupName, "NXcollection");
        nexusFile.opengroup(groupName, "NXcollection");
        nexusFile.closegroup();
        
        for (int i=0;i<bankGroup.size();++i){
            //System.out.println(bankGroup.get(i)); 
           
            XPixelOffsetPath = "/entry/instrument/" + bankGroup.get(i) + "/x_pixel_offset";
            nexusFile.openpath(XPixelOffsetPath);
            XPixelOffsetLink = nexusFile.getdataID();
            
            YPixelOffsetPath = "/entry/instrument/" + bankGroup.get(i) + "/y_pixel_offset";
            nexusFile.openpath(YPixelOffsetPath);
            YPixelOffsetLink = nexusFile.getdataID();

            TimeOfFlightPath = "/entry/instrument/" + bankGroup.get(i) + "/time_of_flight";
            nexusFile.openpath(TimeOfFlightPath);
            TimeOfFlightLink = nexusFile.getdataID();
            
            nexusFile.openpath("/entry/"+groupName);

            nexusFile.makegroup(bankGroup.get(i), "NXevent_data");
            nexusFile.opengroup((bankGroup.get(i)), "NXevent_data");
            
            nexusFile.makelink(XPixelOffsetLink);
            nexusFile.makelink(YPixelOffsetLink);
            nexusFile.makelink(TimeOfFlightLink);
            
            nexusFile.closegroup();
            nexusFile.closegroup();
        }

        nexusFile.closegroup();
    }
    private static void genEventData(NexusFile nexusFile, String instrumentName) throws NexusException {
        String groupName = "event_data";
        String eventPixelIdPath;
        String eventTimeOfFlightPath;
        String eventPulseTimePath;
        NXlink eventPixelIdLink;
        NXlink eventTimeOfFlightLink;
        NXlink eventPulseTimeLink;
            
        ArrayList<String> bankGroup = new ArrayList<String>();
        if (instrumentName.equalsIgnoreCase("GPPD")) {
            for (int i=0;i<6;++i){
                bankGroup.add("bank"+i);
            }
        }else if (instrumentName.equalsIgnoreCase("SANS")) {
                bankGroup.add("bank");
        }else if (instrumentName.equalsIgnoreCase("MR")) {
                bankGroup.add("bank");
        }

        //nexusFile.openpath("/entry");
        nexusFile.makegroup(groupName, "NXcollection");
        nexusFile.opengroup(groupName, "NXcollection");
        nexusFile.closegroup();
        
        for (int i=0;i<bankGroup.size();++i){
            //System.out.println(bankGroup.get(i)); 
           
            eventPixelIdPath = "/entry/instrument/" + bankGroup.get(i) + "/event_pixel_id";
            nexusFile.openpath(eventPixelIdPath);
            eventPixelIdLink = nexusFile.getdataID();

            eventTimeOfFlightPath = "/entry/instrument/" + bankGroup.get(i) + "/event_time_of_flight";
            nexusFile.openpath(eventTimeOfFlightPath);
            eventTimeOfFlightLink = nexusFile.getdataID();

            eventPulseTimePath = "/entry/instrument/" + bankGroup.get(i) + "/event_pulse_time";
            nexusFile.openpath(eventPulseTimePath);
            eventPulseTimeLink = nexusFile.getdataID();
            
            nexusFile.openpath("/entry/"+groupName);

            nexusFile.makegroup(bankGroup.get(i), "NXevent_data");
            nexusFile.opengroup((bankGroup.get(i)), "NXevent_data");
            
            nexusFile.makelink(eventPixelIdLink);
            nexusFile.makelink(eventTimeOfFlightLink);
            nexusFile.makelink(eventPulseTimeLink);
            
            nexusFile.closegroup();
            nexusFile.closegroup();
        }

        nexusFile.closegroup();
    }

    private static void genHistogramData(NexusFile nexusFile, String instrumentName) throws NexusException {
        String groupName = "histogram1";
        int data1_size = 124;
        int data2_size = 56;

        nexusFile.makegroup(groupName, "NXdata");
        nexusFile.opengroup(groupName, "NXdata");

        // plot
        // the format is: axis1,axi2,..,axisN,histogramData:histogramDataError
        String plot = "data1,data2,data3:data3_error";  // for 3D
        nexusFile.makedata("plot", NexusFile.NX_CHAR, 1, new int[]{plot.length()});
        nexusFile.opendata("plot");
        nexusFile.putdata(plot.getBytes());
        //nexusFile.putattr("units", NexusUnit.NX_LENGTH.getBytes(), NexusFile.NX_CHAR);
        nexusFile.closedata();

        // type: the chart type
        // can be: line | bar| scatter
        String type = "line";
        nexusFile.makedata("type", NexusFile.NX_CHAR, 1, new int[]{type.length()});
        nexusFile.opendata("type");
        nexusFile.putdata(type.getBytes());
        //nexusFile.putattr("units", NexusUnit.NX_LENGTH.getBytes(), NexusFile.NX_CHAR);
        nexusFile.closedata();

        // title: the chart title
        String title = "Events Distribution";
        nexusFile.makedata("title", NexusFile.NX_CHAR, 1, new int[]{title.length()});
        nexusFile.opendata("title");
        nexusFile.putdata(title.getBytes());
        //nexusFile.putattr("units", NexusUnit.NX_LENGTH.getBytes(), NexusFile.NX_CHAR);
        nexusFile.closedata();

        float[] data1 = genFloatData1D(data1_size, 100);
        float[] data2 = genFloatData1D(data2_size, 50);
        float[] data3 = genFloatData1D(data1_size * data2_size, 30);
        float[] data3_error = genFloatData1D(data1_size * data2_size, 1);

        // data1
        nexusFile.makedata("data1", NexusFile.NX_FLOAT32, 1, new int[]{data1_size});
        nexusFile.opendata("data1");
        nexusFile.putdata(data1);
        nexusFile.putattr("units", NexusUnit.NX_LENGTH.getBytes(), NexusFile.NX_CHAR);
        nexusFile.putattr("long_name", "x".getBytes(), NexusFile.NX_CHAR);
        nexusFile.closedata();

        // data2
        nexusFile.makedata("data2", NexusFile.NX_FLOAT32, 1, new int[]{data2_size});
        nexusFile.opendata("data2");
        nexusFile.putdata(data2);
        nexusFile.putattr("units", NexusUnit.NX_LENGTH.getBytes(), NexusFile.NX_CHAR);
        nexusFile.putattr("long_name", "y".getBytes(), NexusFile.NX_CHAR);
        nexusFile.closedata();

        // data3
        nexusFile.makedata("data3", NexusFile.NX_FLOAT32, 1, new int[]{data1_size * data2_size});
        nexusFile.opendata("data3");
        nexusFile.putdata(data3);
        nexusFile.putattr("units", "counts".getBytes(), NexusFile.NX_CHAR);
        nexusFile.putattr("long_name", "Counts".getBytes(), NexusFile.NX_CHAR);
        nexusFile.closedata();

        // data3_error
        nexusFile.makedata("data3_error", NexusFile.NX_FLOAT32, 1, new int[]{data1_size * data2_size});
        nexusFile.opendata("data3_error");
        nexusFile.putdata(data3_error);
        //nexusFile.putattr("units", "counts".getBytes(), NexusFile.NX_CHAR);
        //nexusFile.putattr("long_name", "Counts".getBytes(), NexusFile.NX_CHAR);
        nexusFile.closedata();

        nexusFile.closegroup();
    }

    private static void genPublicFields(NexusFile nexusFile, String instrumentName) throws NexusException {
        String version = "1";
        String proposalId = "CSNS-00001";
        String acceleratorRunNo = "23";
        String runNo = "0000001"; // 7 digits
        String description = "This is the first beam from CSNS.";
        String beamline = null;
        String startTime = CalendarTool.getFormatedCurrentDate("yyyy-MM-dd'T'HH:mm:ss", true);
        String endTime = CalendarTool.getFormatedCurrentDate("yyyy-MM-dd'T'HH:mm:ss", true);

        if (instrumentName.equalsIgnoreCase("GPPD")){
            beamline="BL18";
        } else if (instrumentName.equalsIgnoreCase("MR")) {
            beamline="BL03";
        } else if (instrumentName.equalsIgnoreCase("SANS")) {
            beamline="BL02";
        } else{
            beamline="BL00";
        }

        String instrumentFile = "CSNS_BL01_Definition_2016_05_18.xml";
        
        String instrumentDefinition = null;
        //try(FileInputStream inputStream =new FileInputStream("/opt/shared/home/zhangjr/git/CSNSNeXusFormat/src/instrument/"+instrumentFile)) {
        try(FileInputStream inputStream =new FileInputStream("./"+instrumentFile)) {
            instrumentDefinition = IOUtils.toString(inputStream);
        } catch(FileNotFoundException ex) {
            System.out.println(ex.getMessage());
        }  catch(IOException ex) {
            System.out.println(ex.getMessage());
        }  catch(Exception ex) {
            System.out.println(ex.getMessage());
        }

        writeData(nexusFile, "version", NexusFile.NX_CHAR, 1, new int[]{version.length()}, version.toUpperCase().getBytes());
        writeData(nexusFile, "instrument_name", NexusFile.NX_CHAR, 1, new int[]{instrumentName.length()}, instrumentName.toUpperCase().getBytes());
        writeData(nexusFile, "beamline", NexusFile.NX_CHAR, 1, new int[]{beamline.length()}, beamline.toUpperCase().getBytes());
        writeData(nexusFile, "instrument_file", NexusFile.NX_CHAR, 1, new int[]{instrumentFile.length()}, instrumentFile.toUpperCase().getBytes());
        writeData(nexusFile, "instrument_definition", NexusFile.NX_CHAR, 1, new int[]{instrumentDefinition.length()}, instrumentDefinition.getBytes());
        writeData(nexusFile, "proposal_id", NexusFile.NX_CHAR, 1, new int[]{proposalId.length()}, proposalId.getBytes());
        writeData(nexusFile, "accelerator_run_no", NexusFile.NX_CHAR, 1, new int[]{acceleratorRunNo.length()}, acceleratorRunNo.getBytes());
        writeData(nexusFile, "run_no", NexusFile.NX_CHAR, 1, new int[]{runNo.length()}, runNo.getBytes());
        writeData(nexusFile, "description", NexusFile.NX_CHAR, 1, new int[]{description.length()}, description.getBytes());
        writeData(nexusFile, "start_time", NexusFile.NX_CHAR, 1, new int[]{startTime.length()}, startTime.getBytes());
        writeData(nexusFile, "end_time", NexusFile.NX_CHAR, 1, new int[]{endTime.length()}, endTime.getBytes());
    }

    private static void genFermiChopper(NexusFile nexusFile) throws NexusException {
        nexusFile.makegroup("fermi_chopper1", "NXfermi_chopper");
        nexusFile.opengroup("fermi_chopper1", "NXfermi_chopper");

        nexusFile.closegroup();
    }

    /**
     * Generate a 1D float array.
     *
     * @param size the size of the array
     * @param scale the scale for Random#nextFloat()
     * @return float[]
     */
    private static float[] genFloatData1D(final int size, final int scale) {
        float[] data;
        data = new float[size];
        for (int i = 0; i < size; i++) {
            data[i] = new Random().nextFloat() * scale;
        }
        return data;
    }

    /**
     * Generate a 2D float array.
     *
     * @param x size of the first dimension
     * @param y size of the second dimension
     * @param scale the scale for Random#nextFloat()
     * @return
     */
    private static float[][] genFloatData2D(final int x, final int y, final int scale) {
        float[][] data;
        data = new float[x][y];
        for (int i = 0; i < x; i++) {
            for (int j = 0; j < y; j++) {
                data[i][j] = new Random().nextFloat() * scale;
            }
        }
        return data;
    }

    /**
     * Generate a 1D integer array.
     *
     * @param size size of the array
     * @param start the start number
     * @param inc the incremental value for next number
     * @return int[]
     */
    private static int[] genIntData1D(final int size, final int start, final int inc) {
        int[] data;
        int current = start;
        data = new int[size];

        for (int i = 0; i < size; i++) {
            data[i] = current;
            current = current + inc;
        }
        return data;
    }

    /**
     * Generate a 2D integer array.
     *
     * @param x size of the first dimension
     * @param y size of the second dimension
     * @param start the start number
     * @param inc the incremental value for next number.
     * @return
     */
    private static int[][] genIntData2D(final int x, final int y, final int start, final int inc) {
        int[][] data;
        int current = start;
        data = new int[x][y];

        for (int i = 0; i < x; i++) {
            for (int j = 0; j < y; j++) {
                data[i][j] = current;
                current = current + inc;
            }
        }
        return data;
    }

    /**
     * Generate a random integer array between min and max.
     *
     * @param size the size of the result array
     * @param min the minumum value of the array
     * @param max the maxumum value of the array
     * @return a integer array
     */
    private static int[] genIntRandom1D(final int size, final int min, final int max) {
        int[] data = new int[size];
        for (int i = 0; i < size; i++) {
            data[i] = (new Random().nextInt(max)) % Math.abs(max - min + 1) + min;
        }
        return data;
    }

    private static float[] genFloatPulseTime(final int size, final float period) {
        float[] data = new float[size];
        float t0=0.0f;
        for (int i = 0; i < size; i++) {
            if (new Random().nextFloat()  < 0.1f){
                t0 = t0 + period;
            }
            data[i] = t0;
        }
        return data;
    }

    private static void writeData(NexusFile nexusFile, final String dataName, int dataType, int rank, int[] dim, Object data)
            throws NexusException {
        nexusFile.makedata(dataName, dataType, rank, dim);
        nexusFile.opendata(dataName);
        nexusFile.putdata(data);
        nexusFile.closedata();
    }

    private static void help(String[] supportedInstruments, String[] supportedFileFormats) {
        System.out.println("Usage:  java " + CsnsNexusTemplateGenerator.class.getCanonicalName() + " instrument_name [file_format]");
        System.out.println("\ninstrument_name can be one of these:");
        for (String supportedInstrument : supportedInstruments) {
            System.out.println(supportedInstrument);
        }
        System.out.println("\nfile_format can be one of these:");
        for (String supportedFileFormat : supportedFileFormats) {
            System.out.println(supportedFileFormat);
        }
        System.out.println("");
    }

    public static void main(String[] args) {
        String file = "csns-nexus-template.nxs";
        String fileSuffix = ".nxs";
        String instrumentName = "GPPD";
        String fileFormat = null;
        String[] supportedInstruments = new String[]{"GPPD", "SANS", "MR"};
        String[] supportedFileFormats = new String[]{"HDF5", "HDF4", "XML"};
        Boolean isSupported = false;

        if (args.length < 1) {
            help(supportedInstruments, supportedFileFormats);
            System.exit(0);
        }

        instrumentName = args[0].trim();
        for (String supportedInstrument : supportedInstruments) {
            if (instrumentName.equalsIgnoreCase(supportedInstrument)) {
                isSupported = true;
                break;
            }
        }
        if (!isSupported) {
            help(supportedInstruments, supportedFileFormats);
            System.exit(0);
        }

        if (args.length >= 2) {
            fileFormat = args[1].trim();
            isSupported = false;
            for (String supportedFileFormat : supportedFileFormats) {
                if (fileFormat.equalsIgnoreCase(supportedFileFormat)) {
                    isSupported = true;
                    break;
                }
            }
            if (!isSupported) {
                help(supportedInstruments, supportedFileFormats);
                System.exit(0);
            }

            if (fileFormat.equalsIgnoreCase("XML")) {
                fileSuffix = ".xml";
            }
        }

        if (instrumentName.equalsIgnoreCase("GPPD")) {
            file = "CSNS_GPPD_NeXus_Template" + fileSuffix;
        }
        if (instrumentName.equalsIgnoreCase("SANS")) {
            file = "CSNS_SANS_NeXus_Template" + fileSuffix;
        }
        if (instrumentName.equalsIgnoreCase("MR")) {
            file = "CSNS_MR_NeXus_Template" + fileSuffix;
        }

        genNexusTemplate(file, instrumentName, fileFormat);
    }

}

