package org.autoplot.imagedatasource;

import java.awt.Color;
import java.awt.image.BufferedImage;
import org.das2.qds.AbstractDataSet;
import org.das2.qds.QDataSet;
import org.das2.qds.ops.Ops;

/**
 * Adapt a BufferedImage to a rank 2 or rank 3 QDataSet, using
 * ColorOp to extract red, green, or blue channels.  When mask
 * is null and op is null, then a rank 3 dataset [w,h,3] is returned, 
 * a bundle of red, green, blue channels.
 * @author jbf
 */
public class ImageDataSet extends AbstractDataSet {

    BufferedImage image;
    ColorOp op;
    int w,h;
    private int rank;
    
    public interface ColorOp {
        double value( int rgb );
    }

    private static class ChannelColorOp implements ColorOp {
        int mask;
        int rot;
        ChannelColorOp( int mask, int rot ) {
            this.mask= mask;
            this.rot= rot;
        }
        @Override
        public double value( int rgb ) {
            return ( rgb & mask ) >> rot;
        }
    }
    
    private double log2( double d ) {
        return Math.log(d)/Math.log(2);
    }
    
    
    /**
     * create a dataset from the image,
     * returning a rank 3 dataset ds[w,h,3].
     * @param image the image
     */
    public ImageDataSet( BufferedImage image ) {
        this( image, null, null );
    }
    
    /**
     * create a dataset from the image.  When mask and op are null,
     * then a rank 3 dataset ds[w,h,3] is returned.
     * @param image the image
     * @param mask null or the mask, where the image is ANDed with this color, and shifted by the lowest one bit location.
     * @param op null or an operation, such as grayscale, to convert the data to rank 2.
     */
    public ImageDataSet( BufferedImage image, Color mask, ColorOp op ) {
        this.image= image;
        this.h= image.getHeight();
        this.w= image.getWidth();
        this.rank= 2;
        if ( mask==null ) {
            if ( op==null ) {
                rank= 3;
                putProperty( QDataSet.DEPEND_2, Ops.labelsDataset(new String[] { "red", "green", "blue" } ) );
            } else {
                this.op= op;
            }
        } else {
            this.op= new ChannelColorOp( mask.getRGB() & 0xFFFFFF, (int)log2( Integer.lowestOneBit( mask.getRGB() ) ) );
        }
        putProperty( QDataSet.QUBE, Boolean.TRUE );
        putProperty( QDataSet.RENDER_TYPE, QDataSet.VALUE_RENDER_TYPE_COMPOSITE_IMAGE );
    }
    
    @Override
    public int rank() {
        return rank;
    }

    @Override
    public int length() {
        return w;
    }

    @Override
    public int length(int i) {
        return h;
    }

    @Override
    public int length(int i, int j) {
        return 3;
    }

    
    @Override
    public double value(int i0, int i1) {
        return op.value( image.getRGB( i0, h-i1-1 ) );
    }

    @Override
    public double value(int i0, int i1, int i2) {
        int rgb= image.getRGB( i0, h-i1-1 );
        switch (i2) {
            case 0: return ( rgb & 0xff0000 ) >> 16; 
            case 1: return ( rgb & 0x00ff00 ) >> 8; 
            case 2: return ( rgb & 0x0000ff ); 
            default: throw new IndexOutOfBoundsException( "i2=3" );
        }
    }
    
    
    

}