package ij.process;
import java.awt.*;
import ij.*;
import ij.process.*;
import ij.macro.Interpreter;

/** This class processes stacks. */
public class StackProcessor {
    private ImageStack stack;
    private ImageProcessor ip;
    int nSlices;
    double xScale, yScale;
    int[] table;
    double fillValue;
        
    /* Constructs a StackProcessor from a stack. */
    //public StackProcessor(ImageStack stack) {
    //  this.StackProcessor(stack, stack.getProcessor());
   //}
    
    /** Constructs a StackProcessor from a stack. 'ip' is the
        processor that will be used to process the slices. 
        'ip' can be null when using crop(). */
    public StackProcessor(ImageStack stack, ImageProcessor ip) {
        this.stack = stack;
        this.ip = ip;
        nSlices = stack.getSize();
        if (nSlices>1 && ip!=null)
            ip.setProgressBar(null);
   }
    
    static final int FLIPH=0, FLIPV=1, SCALE=2, INVERT=3, APPLY_TABLE=4, SCALE_WITH_FILL=5;
    
    void process(int command) {
        String s = "";
        ImageProcessor ip2 = stack.getProcessor(1);
        switch (command) {
            case FLIPH: case FLIPV: s="Flip: "; break;
            case SCALE: s="Scale: "; break;
            case SCALE_WITH_FILL: s="Scale: "; ip2.setBackgroundValue(fillValue); break;
            case INVERT: s="Invert: "; break;
            case APPLY_TABLE: s="Apply: "; break;
        }
        ip2.setRoi(this.ip.getRoi());
        ip2.setInterpolate(this.ip.getInterpolate());
        for (int i=1; i<=nSlices; i++) {
            showStatus(s,i,nSlices);
            ip2.setPixels(stack.getPixels(i));
            if (nSlices==1 && i==1 && command==SCALE)
                ip2.snapshot();
            switch (command) {
                case FLIPH: ip2.flipHorizontal(); break;
                case FLIPV: ip2.flipVertical(); break;
                case SCALE: case SCALE_WITH_FILL: ip2.scale(xScale, yScale); break;
                case INVERT: ip2.invert(); break;
                case APPLY_TABLE: ip2.applyTable(table); break;
            }
            IJ.showProgress((double)i/nSlices);
        }
        IJ.showProgress(1.0);
    }

    public void invert() {
        process(INVERT);
    }
    
    public void flipHorizontal() {
        process(FLIPH);
    }
    
    public void flipVertical() {
        process(FLIPV);
    }
    
    public void applyTable(int[] table) {
        this.table = table;
        process(APPLY_TABLE);
    }

    public void scale(double xScale, double yScale) {
        this.xScale = xScale;
        this.yScale = yScale;
        process(SCALE);
    }

    public void scale(double xScale, double yScale, double fillValue) {
        this.xScale = xScale;
        this.yScale = yScale;
        this.fillValue = fillValue;
        process(SCALE_WITH_FILL);
    }

    /** Creates a new stack with dimensions 'newWidth' x 'newHeight'.
        To reduce memory requirements, the orginal stack is deleted
        as the new stack is created. */
    public ImageStack resize(int newWidth, int newHeight) {
        ImageStack stack2 = new ImageStack(newWidth, newHeight);
        ImageProcessor ip2;
        try {
            for (int i=1; i<=nSlices; i++) {
                showStatus("Resize: ",i,nSlices);
                ip.setPixels(stack.getPixels(1));
                String label = stack.getSliceLabel(1);
                stack.deleteSlice(1);
                ip2 = ip.resize(newWidth, newHeight);
                if (ip2!=null)
                    stack2.addSlice(label, ip2);
                IJ.showProgress((double)i/nSlices);
            }
            IJ.showProgress(1.0);
        } catch(OutOfMemoryError o) {
            while(stack.getSize()>1)
                stack.deleteLastSlice();
            IJ.outOfMemory("StackProcessor.resize");
            IJ.showProgress(1.0);
        }
        return stack2;
    }

    /** Crops the stack to the specified rectangle. */
    public ImageStack crop(int x, int y, int width, int height) {
        ImageStack stack2 = new ImageStack(width, height);
        ImageProcessor ip2;
        for (int i=1; i<=nSlices; i++) {
            ImageProcessor ip1 = stack.getProcessor(1);
            ip1.setRoi(x, y, width, height);
            String label = stack.getSliceLabel(1);
            stack.deleteSlice(1);
            ip2 = ip1.crop();
            stack2.addSlice(label, ip2);
            IJ.showProgress((double)i/nSlices);
        }
        IJ.showProgress(1.0);
        return stack2;
    }

    ImageStack rotate90Degrees(boolean clockwise) {
        ImageStack stack2 = new ImageStack(stack.getHeight(), stack.getWidth());
        ImageProcessor ip2;
        for (int i=1; i<=nSlices; i++) {
            showStatus("Rotate: ",i,nSlices);
            ip.setPixels(stack.getPixels(1));
            String label = stack.getSliceLabel(1);
            stack.deleteSlice(1);
            if (clockwise)
                ip2 = ip.rotateRight();
            else
                ip2 = ip.rotateLeft();
            if (ip2!=null)
                stack2.addSlice(label, ip2);
            if (!Interpreter.isBatchMode())
                IJ.showProgress((double)i/nSlices);
        }
        if (!Interpreter.isBatchMode())
            IJ.showProgress(1.0);
        return stack2;
    }
    
    public ImageStack rotateRight() {
        return rotate90Degrees(true);
    }
    
    public ImageStack rotateLeft() {
        return rotate90Degrees(false);
    }
    
    public void copyBits(ImageProcessor src, int xloc, int yloc, int mode) {
        copyBits(src, null, xloc, yloc, mode);
    }

    public void copyBits(ImageStack src, int xloc, int yloc, int mode) {
        copyBits(null, src, xloc, yloc, mode);
    }

    private void copyBits(ImageProcessor srcIp, ImageStack srcStack, int xloc, int yloc, int mode) {
        int inc = nSlices/20;
        if (inc<1) inc = 1;
        boolean stackSource = srcIp==null;
        for (int i=1; i<=nSlices; i++) {
            if (stackSource)
                srcIp = srcStack.getProcessor(i);
            ImageProcessor dstIp = stack.getProcessor(i);
            dstIp.copyBits(srcIp, xloc, yloc, mode);
            if ((i%inc) == 0) IJ.showProgress((double)i/nSlices);
        }
        IJ.showProgress(1.0);
    }
    
    void showStatus(String s, int n, int total) {
        IJ.showStatus(s+n+"/"+total);
    }   
    
}