import ij.*;
import ij.process.*;
import ij.plugin.*; 

import java.lang.*; 
import java.text.DecimalFormat; 
import java.awt.*; 
import java.awt.image.*; 

/** Splits a FluoviewTiff_ stack into multiple substacks. This class
    uses Fluoview image description information to determine the
    number of substacks. Each such substack contains all images
    associated with a specific channel. It also ensures that this
    image description information is stored with the generated
    substacks.  This plugin is the preferred method of splitting
    FluoviewTiff_ stacks. To split non-FluoviewTiff_ stacks into
    substacks use the ij.plugin.SplitStack plugin.

@see plugins.FluoviewTiff_
@see plugins.SplitStack

@author Patrick Kelly <phkelly@ucsd.edu> */


public class FVSplitStack_ implements PlugIn {

    // --------------------------------------------------
    public void run(String arg) {
	ImagePlus imp = WindowManager.getCurrentImage(); 

	// Make sure valid image.
	if(imp==null) {
	    IJ.noImage(); 
	    return ; 
	}

	// Make sure we have a stack.
	if(imp.getStackSize()==1) {
	    IJ.error("FVSplitStack_: Must call this plugin on image stack."); 
	    return; 
	}

	// Make sure this is a valid Fluoview stack.
	if(!FluoviewTiff_.isFluoviewImage(imp)) {
	    IJ.error("FVSplitStack_: Image is FluoviewTiff_ type but"+
		     " does not have appropriate image information."+
		     "\n\nInspect with ij.plugin.ExtendedInfo."+
		     "\n\nTry using ij.plugin.SplitStack instead."); 
	    return; 
	}

	if(!imp.lock()) 
	    return;    // exit if in use

	FluoviewTiff_ fimp = (FluoviewTiff_)imp; 
	// Split stacks based on number of channels.
	int numSubStacks = fimp.getNumberChannels(); 
	processStack(fimp,numSubStacks); 

	imp.unlock(); 
    }

    // --------------------------------------------------
    /** Provide processing interface, alternate to run, that supports
	directly spliting the input FluoviewTiff_ stack.  */
    public void splitStack(FluoviewTiff_ fvimp) {
	if(!fvimp.lock())
	    return;  // exit if in use.

	// Split stacks based on number of channels.
	int numSubStacks = fvimp.getNumberChannels();
	processStack(fvimp,numSubStacks); 
	
	fvimp.unlock(); 
    }



    // --------------------------------------------------
    protected void processStack(FluoviewTiff_ imp, int numSubStacks) {
	// Determine number of images in each stack.
	int numImages = imp.getStackSize()/numSubStacks; 

	if(numImages==1)
	    stack2images(imp); // special case 1 image per substack
	else
	    stack2stacks(imp,numImages,numSubStacks); 
    }


    // --------------------------------------------------
    /** Handle special case where each substack consists of a single
	image only. In this case, we generate new images, not new
	stacks.  */
    protected void stack2images(FluoviewTiff_ imp) { 
	String sLabel =  imp.getTitle();

	// Distribute acquistion parameters to new images.
	FluoviewTiff_.AcqParams ap = 
	    imp.getAcqParams(); 

	ImageStack stack = imp.getStack(); 
	
	int sz = stack.getSize(); 
	
	// Get an appropriate color model for output images. This
	// should be grayscale so that image creation works correctly
	// in non-color mode display.
	ColorModel cm = LookUpTable.createGrayscaleColorModel(false); 

	DecimalFormat df = new DecimalFormat("0000");    // for title

	for(int n=1;n<=sz;++n) {
	    // Get current image processor from stack.  Whatever
	    // approach is used here should COPY pixels from old
	    // processor to new. For instance, ImageProcessor.crop()
	    // returns (cropped) copy.
	    ImageProcessor ip = stack.getProcessor(n); 
	    ImageProcessor newip = ip.createProcessor(ip.getWidth(),
						      ip.getHeight());

	    // Make sure processor has appropriate color model. 
	    newip.setColorModel(cm); 
	    // Do the actual pixel copy.
	    newip.setPixels(ip.getPixelsCopy()); 


	    FluoviewTiff_ im = new FluoviewTiff_("slice"+
					       df.format(n)+"_"+
					       sLabel,
					       newip); 

	    // Set image description information for this file. 
	    FluoviewTiff_.CProps cp = imp.getChannelProps(n-1); 
	    im.setChannelProps(cp); 
	    im.setAcqParams(ap); 
	    im.initImageDescription(); 

	    // Propagate scale information.
	    im.copyScale(imp);

	    // Show this image.
	    im.show(); 

	    // Propagate composite status. Do this after show() call
	    // so that window is updated along w/ FluoviewTiff_ image
	    // itself.
	    im.setComposite(imp.isFVComposite(),true); 

	    IJ.showStatus("Writing image: "+n+"/"+sz); 
	}

	IJ.showStatus(""); 

	//if(imp.isProcessor()) {
	//    ImageProcessor ip = imp.getProcessor(); 
	//    ip.setPixels(stack.getPixels()); 
	//}
    }


    // --------------------------------------------------
    /** General case where each substack is a stack.  */
    protected void stack2stacks(FluoviewTiff_ imp, 
			      int numImages, int numSubStacks) {

	FluoviewTiff_.AcqParams ap = 
	    imp.getAcqParams(); 

	ImageStack stack = imp.getStack(); 

	// Create appropriate color model here. This should be
	// grayscale so that image creation works correctly in
	// non-color mode display.
	ColorModel cm = LookUpTable.createGrayscaleColorModel(false); 

	DecimalFormat df = new DecimalFormat("0000");   // for title
	String sBaseName = imp.getTitle(); 

	for(int nss=1,index=0; nss<=numSubStacks; ++nss) {
	    // Create new image stack consistent w/ input image.
	    ImageStack ims = new ImageStack(stack.getWidth(),
					    stack.getHeight(),cm); 

	    // Populate this stack.
	    String sSliceName = "slice_"; 
	    for(int n=1; n<=numImages; ++n) {
		index++;    // activate next slice

		// Get pixels from associated processor. Use of crop
		// to select only those pixels w/in roi if specified.
		ims.addSlice(sSliceName+df.format(n), 
			     stack.getProcessor(index).getPixelsCopy()); 

		IJ.showStatus("Writing image: "+n+"/"+numImages
			      +" in substack: "+nss+"/"+numSubStacks); 
	    }

	    // Stack name.
	    String sStackName = "stk_"+df.format(nss)+"_"+sBaseName; 

	    // Create new FluoviewTiff_ image using this substack.
	    FluoviewTiff_ nimp = new FluoviewTiff_(sStackName,ims);

	    // Set up image description for this stack. Note, we
	    // retrieve channel property object using channel index,
	    // not array index.
	    FluoviewTiff_.CProps cp = imp.getChannelProps(nss-1); 
	    nimp.setChannelProps(cp); 
	    nimp.setAcqParams(ap); 
	    nimp.initImageDescription(); 

	    // Propagate scale information.
	    nimp.copyScale(imp); 

	    // Display this new stack. 
	    nimp.show();

	    // Propagate composite status. Do this after show() call
	    // so that window is updated along w/ FluoviewTiff_ image
	    // itself.
	    nimp.setComposite(imp.isFVComposite(),true); 
	}

	IJ.showStatus(""); 

	//if(imp.isProcessor()) {
	//    ImageProcessor ip = imp.getProcessor(); 
	//    ip.setPixels(stack.getPixels()); 
	//}
    }

}   // FVSplitStack_ class



