
package org.das2.qds.buffer;

import java.nio.ByteBuffer;
import java.text.ParseException;
import org.das2.datum.TimeUtil;
import org.das2.datum.Units;
import org.das2.qds.QDataSet;

/**
 * Often we see where time is left decoded in binary streams, occupying ~21 
 * bytes (ASCII characters) instead of 8 bytes to represent them as a double.  This extension
 * allows this sort of data to be read in as well, making the data available 
 * as Units.us2000.
 * 
 * @author jbf
 */
public class TimeDataSet extends BufferDataSet {

    int lenBytes;
    double fill= -1e38;
    private final Units UNITS= Units.us2000;

    /**
     * Like the other constructors, but the type is needed as well to get the 
     * number of bytes.  
     * @param rank
     * @param reclen
     * @param recoffs
     * @param len0
     * @param len1
     * @param len2
     * @param len3
     * @param back
     * @param type string "timeXX" where XX is number of bytes, between 20 and 24.
     */
    public TimeDataSet(int rank, int reclen, int recoffs, int len0, int len1, int len2, int len3, ByteBuffer back, Object type ) {
        super(rank, reclen, recoffs, len0, len1, len2, len3, type, back );
        this.lenBytes= Integer.parseInt( type.toString().substring(4) );
        if ( this.lenBytes<16 ) throw new IllegalArgumentException("time16 is the shortest time supported.");
        if ( this.lenBytes>24 ) throw new IllegalArgumentException("time24 is the longest time supported.");
        putProperty( QDataSet.UNITS, UNITS );
        putProperty( QDataSet.FILL_VALUE, fill );
    }
    
    public void setLengthBytes( int length ) {
        this.lenBytes= length;
    }
    
    private double parseTime( ByteBuffer back, int offset ) {
        byte[] buff= new byte[lenBytes];
        for ( int i=0; i<lenBytes; i++ ) buff[i]= back.get(i+offset);
        String s= new String(buff);
        try {
            return TimeUtil.create( s ).doubleValue( UNITS );
        } catch (ParseException ex) {
            return fill;
        }
    }
    
    @Override
    public double value() {
        return parseTime(back, offset());
    }

    @Override
    public double value(int i0) {
        return parseTime(back, offset(i0));
    }

    @Override
    public double value(int i0, int i1) {
        return parseTime(back, offset(i0, i1));
    }

    @Override
    public double value(int i0, int i1, int i2) {
        return parseTime(back, offset(i0, i1, i2));
    }

    @Override
    public double value(int i0, int i1, int i2, int i3) {
        return parseTime(back, offset(i0, i1, i2, i3));
    }

    private byte[] getBytes( double d ) {
        String s= UNITS.createDatum(d).toString(); //TODO: 24 byte limit is here.
        return s.substring(0,lenBytes).getBytes();
    }
    
    @Override
    public void putValue(double d) {
        ensureWritable();
        int offs= offset();
        byte[] bb= getBytes( d );
        for ( int i=0; i<lenBytes; i++ ) back.put( offs+i, bb[i] );
    }

    @Override
    public void putValue(int i0, double d) {
        ensureWritable();
        int offs= offset(i0);
        byte[] bb= getBytes( d );
        for ( int i=0; i<lenBytes; i++ ) back.put( offs+i, bb[i] );
    }

    @Override
    public void putValue(int i0, int i1, double d) {
        ensureWritable();
        int offs= offset(i0,i1);
        byte[] bb= getBytes( d );
        for ( int i=0; i<lenBytes; i++ ) back.put( offs+i, bb[i] );
    }

    @Override
    public void putValue(int i0, int i1, int i2, double d) {
        ensureWritable();
        int offs= offset(i0,i1,i2);
        byte[] bb= getBytes( d );
        for ( int i=0; i<lenBytes; i++ ) back.put( offs+i, bb[i] );
    }

    @Override
    public void putValue(int i0, int i1, int i2, int i3, double d) {
        ensureWritable();
        int offs= offset(i0,i1,i2,i3);
        byte[] bb= getBytes( d );
        for ( int i=0; i<lenBytes; i++ ) back.put( offs+i, bb[i] );
    }
    
}
