
package org.das2.qds;

/**
 * return a rank N-1 dataset from a rank N dataset by slicing on the fourth
 * dimension.  (Rank 4 supported.)
 * 
 * plane datasets are sliced as well, when they have rank 4 or greater.
 * 
 * @author jbf
 */
public final class Slice3DataSet extends AbstractDataSet {

    QDataSet ds;
    int index;

    public Slice3DataSet(QDataSet ds, int index) {
        this( ds, index, true );
    }

    Slice3DataSet( QDataSet ds, int index, boolean addContext ) {
        if (ds.rank() > 4 ) {
            throw new IllegalArgumentException("rank limit > 4");
        }
        if ( ds.rank() < 4 ) {
            throw new IllegalArgumentException("rank limit < 4");
        }
        this.ds = ds;
        this.index = index;

        if ( ds.length()==0 ) {
            int[] qube= DataSetUtil.qubeDims(ds);
            if ( qube!=null ) {
                if ( index>=qube[3] ) {
                    throw new IndexOutOfBoundsException("slice3 index is out of bounds");
                }
            } else {
                // do nothing as this is what the last implementation did.
                //throw new IndexOutOfBoundsException("dataset is empty and slice3 index is out of bounds");
            }
        } else {        
            if ( index>= ds.length(0,0,0) ) throw new IndexOutOfBoundsException("slice3 index is out of bounds");
        }
 
        if ( addContext ) {
            QDataSet bundle= (QDataSet) ds.property( QDataSet.BUNDLE_3 );
            QDataSet dep3= (QDataSet) ds.property(QDataSet.DEPEND_3);
            if ( bundle!=null && dep3==null ) {
                QDataSet context=null;
                if ( addContext ) {
                    context= DataSetOps.getContextForUnbundle( bundle, index );
                    DataSetUtil.addContext( this, context );
                    putProperty( QDataSet.NAME, bundle.property(QDataSet.NAME,index) );
                }
            } else {
                if ( dep3!=null ) {
                    if ( dep3.rank()==1 ) {
                        DataSetUtil.addContext( this, new Slice0DataSet(dep3,index,false) );
                    } else if ( dep3.rank()==2 ) {
                        DataSetUtil.addContext( this, new Slice1DataSet(dep3,index,false) );
                    } else {
                        System.err.println( "slice on non-qube, dep3 has rank="+dep3.rank() );
                    }
                } else {
                    DRank0DataSet context= DataSetUtil.asDataSet(index);
                    context.putProperty( QDataSet.NAME, "slice3" );
                    DataSetUtil.addContext( this, context );
                }
            }
        }

        putProperty( QDataSet.DEPEND_0, ds.property(QDataSet.DEPEND_0) );
        putProperty( QDataSet.DEPEND_1, ds.property(QDataSet.DEPEND_1) );
        putProperty( QDataSet.DEPEND_2, ds.property(QDataSet.DEPEND_2) );
        putProperty( QDataSet.DEPEND_3, null );

        putProperty( QDataSet.BUNDLE_0, ds.property( QDataSet.BUNDLE_0 ) );
        putProperty( QDataSet.BUNDLE_1, ds.property( QDataSet.BUNDLE_1 ) );
        putProperty( QDataSet.BUNDLE_2, ds.property( QDataSet.BUNDLE_2 ) );
        putProperty( QDataSet.BINS_0, ds.property( QDataSet.BINS_0 ) );
        putProperty( QDataSet.BINS_1, ds.property( QDataSet.BINS_1 ) );
        
        for ( int i=0; i<QDataSet.MAX_PLANE_COUNT; i++ ) {
            String prop= "PLANE_"+i;
            QDataSet plane0= (QDataSet) ds.property( prop );
            if ( plane0!=null ) {
                if ( plane0.rank()<4 ) {
                    putProperty( prop, plane0 );
                } else {
                    putProperty( prop, new Slice3DataSet( plane0, index ) );
                }
            } else {
                break;
            }
        }
        
        String[] props= DataSetUtil.correlativeProperties();
        for (String prop : props) {
            QDataSet s = (QDataSet) ds.property( prop );
            if (s != null) {
                if ( s.rank()<4 ) {
                    putProperty( prop, s );
                } else {
                    putProperty( prop, new Slice3DataSet( s, index, addContext ) );
                }
            }
        }
        
        putProperty( QDataSet.WEIGHTS, null );

        DataSetUtil.copyDimensionProperties( ds, this );
        DataSetUtil.maybeCopyRenderType( ds, this );

    }

    public int rank() {
        return ds.rank() - 1;
    }

    public double value(int i0, int i1, int i2) {
        return ds.value( i0, i1, i2, index );
    }

    public Object property(String name) {
        if (properties.containsKey(name)) {
            return properties.get(name);
        } else {
            if ( DataSetUtil.isInheritedProperty(name) ) {
                return ds.property(name);
            } else {
                return null;
            }
            
        }
    }

    public int length() {
        return ds.length();
    }

    @Override
    public int length(int i) {
        return ds.length( i );
    }
    
    public int length(int i0,int i1) { //TODO: really? interesting...
        return ds.length( i0, i1 );
    }
    
    @Override
    public boolean equals(Object obj) {
        if ( obj==null ) return false;
        if ( obj instanceof Slice3DataSet ) {
            Slice3DataSet that= ((Slice3DataSet)obj);
            return that.ds.equals(this.ds) && that.index==this.index;
        } else {
            return false;
        }
    }

    @Override
    public int hashCode() {
        return ds.hashCode() + index;
    }
}
