package au.gov.ansto.bragg.nexus.base;

import java.beans.PropertyVetoException;
import java.lang.reflect.Array;
import java.lang.reflect.Field;

import static neutron.nexus.NexusFile.*;

/**
 * 
 * 
 * @author dke (Darren Kelly), with contributions by student hrz (Hugh Rayner)
 *
 * <p>
 * POLICY: getters MAY return null, means not defined, not read, and not written.
 * This policy may be overridden in next release with strong exceptions
 * and strong NULL placeholder !
 * </p>
 * 
 */

/*
 * <p>Candidate for strong units:<br/>
 * http://www.unidata.ucar.edu/software/udunits/
 * UDUNITS is a library for manipulating units_ of physical qualities.
 * </p>
 */

// Issues:
//
// 2005-12-08 ! dke: NB: does not usually write "name" property (although eventually may extend something that has a "name")
//
// 2005-12-08 ? dke: what about extra data item attributes like "Signal" (cf. Mark Koennike triple-axis model)
//
// 2005-12-08 ? dke: enforce write-as-attribute vs. write-as-content policy by inheritance from NXattribute
// 2005-12-13 ! dke: prefer attribute registration through hashmap in NXobject.
//
// 20005-12-13 ! dke: decoupled UDUNITS veto
//
// 2005-12-DD ?prefer Enum of possible units_ 
// 2005-12-13 ! no, use UDUNITS
//
// 2005-12-13 ? dke: how handle 'type="NX_FLOAT[:...]|NX_INT[:...]"' (entry/NXdata.xml)

public class NXdataItem_
        extends NXtyped_ 
        implements NXdataItem
{

    public NXdataItem_() {
    }
    
    public NXdataItem_(Object content) {
        setContent(content);
    }

    /* 2005-12-13 dke: approach discarded after tests with hrz: leave as record
    final static public String $signal = "signal";
    private NXattribute<Integer> signal;
    public NXattribute<Integer> getSignal() {
        //if (signal==null)
        return signal;
    }
    public void setSignal(NXattribute<Integer> signal) {
        this.signal = signal;
    }
     * */
    
        
    /** To be written in XML as attribute of an element */
    private String units_;
    /**  @inheritDoc */
    public String getUnits()
    //?throws InvalidUnitException
    {
        if (units_==null) return $UNDEF;
        return units_;
    }
    public void setUnits(String units) {
        //?fire().veto
        //?fire().change
        this.units_ = units;
    }
    
    
    private String type_;
    public String getType(){
        return type_;
    }
    public void setType(final String type){
        type_ = type;
    }
    
    static {
        setNXtypeFor($type,NX_CHAR);
        //registerNeXusField($type, NXdataItem_.class);
    }    
    
    static {
        setNXtypeFor($checksum,NX_INT32);
        //registerNeXusField($checksum, NXdataItem_.class);
    }
    
    /** NX_INT32: Sum of data array acting as a check on data integrity */
    public Integer checksum_ = null;
    final public Integer getChecksum() {
        return checksum_;
    }    
    final public void setChecksum(Integer checksum) throws PropertyVetoException 
    //TODO throws PropertyVetoException
    {
            checksum_ = checksum;
    }

    static {
        setNXtypeFor($url,NX_CHAR);
        //TODO registerXMLattribute($url, NXdataItem_.class);
    }
    /** The URL of the XML DTD file or schema on which the NeXus file is based. Should only be used with the "analysis" data item in an NXentry group. */
    public String url_;
    //final NO, makes managing auto-generation harder
    public String getURL() {
        if (url_==null) return $UNDEF;
        return url_;
    }
    public void setURL(String url) throws PropertyVetoException {
        this.url_ = url;
    }

    static {
        setNXtypeFor($version,NX_CHAR);
        //TODO registerXMLattribute($version, NXdataItem_.class);
    }
    /** The Version of the XML DTD file or schema on which the NeXus file is based. Should only be used with the "analysis" data item in an NXentry group. */
    public String version_;
    //transient public String version0;
    //final 
    public String getVersion() {
        if (version_==null) return $UNDEF;
        return version_;
    }
    //final
    public void setVersion(String version) throws PropertyVetoException {
        this.version_ = version;
    }
    
    
    //TODO setNXtypeFor($version,..)
        
    static {
        setNXtypeFor($link,NX_CHAR);
        //TODO registerXMLattribute($link, NXdataItem_.class);
    }

    public String link_;
    /** @inheritDoc */
    public String getLink() {
        if (link_==null) return $UNDEF;
        return link_;
    }
    public void setLink(String link) throws PropertyVetoException {
        this.link_ = link;
    }            
    
    public static void main(String[] args) {
        NXdataItem_ d = new NXdataItem_();
    }
        
    
    /**
     * <p>Author: hrz</p>
     * @param typeWithDimensions (dimensions actually optional)
     */
    public void initContent(String typeWithDimensions) {
        Class clazz = Integer.class;
        Integer type = NXtypes.NX_INT;
        String[] split = typeWithDimensions.split("\\[");
        if(split[0].contains("FLOAT")) {
            clazz = Float.class;
            type = NXtypes.NX_FLOAT;
        } else if(split[0].contains("CHAR")) {
            clazz = String.class;
            type = NXtypes.NX_CHAR;
        }
        if(split.length == 1) {
            return;
        }
        String[] dimensions = split[1].substring(0, split[1].length()-1).split(",");
        int[] sizes = new int[dimensions.length];
        for(int i = 0; i < sizes.length; i++) {
            sizes[i] = Integer.parseInt(dimensions[i].trim());
        }
        setContent(Array.newInstance(clazz, sizes));
        try {
            setNXtype(type);
        }
        catch (Exception e) {error(e);}
    }
    /** <b>author:</b> hrz */
    private static int[] getDims(Object o) {
        if(o == null || o.getClass().getComponentType() == null)
            return new int[0];
        else {
            int[] sub = getDims(Array.get(o, 0));
            int[] out = new int[sub.length+1];
            for(int i = 0; i < sub.length; i++) {
                out[i+1] = sub[i];
            }
            out[0] = Array.getLength(o);
            return out;
        }
    }
    /** <b>author:</b> hrz */
    private static String showArray(int[] in) {
        if(in.length == 0 || in.length == 1 && in[0] == 1)
            return "";
        String rv = "[";
        for(int i = 0; i < in.length; i++) {
            if(i == in.length-1)
                rv = rv + in[i];
            else
                rv = rv + in[i]+", ";
        }
        
        return rv + "]";
    }
            
    /** <b>author:</b> hrz */
    public String typeAsString() {
        return super.typeAsString() + showArray(getDims(getContent()));
    }
    
    //static {
        //registerXMLattribute($units,NXdataItem_.class);        
        //registerXMLattribute($NXtype, NXdataItem_.class);
        
        //registerNeXusField($units,NXdataItem_.class);
        //.registerNeXusField($NXtype, NXdataItem_.class);
    //}
    
}
