package imagej.core.plugins;

import imagej.model.Dataset;
import imagej.plugin.ImageJPlugin;
import imagej.plugin.Parameter;

import imglib.ops.function.RealFunction;
import imglib.ops.operation.AssignOperation;

import java.util.ArrayList;
import java.util.List;

import mpicbg.imglib.container.planar.PlanarContainerFactory;
import mpicbg.imglib.cursor.Cursor;
import mpicbg.imglib.image.Image;
import mpicbg.imglib.image.ImageFactory;
import mpicbg.imglib.type.numeric.RealType;
import mpicbg.imglib.type.numeric.integer.UnsignedShortType;

// TODO - class was abstract - not sure that was necessary - removed for now

/**
 * TODO
 *
 * @author Barry DeZonia
 * @author Curtis Rueden
 */
public class NAryOperation<T extends RealType<T>> implements ImageJPlugin {

	// TODO - SwingInputHarvester must prompt user for list of images
	//  + need N-list widget with plus and minus buttons
	//  + need Dataset support -- JComboBox

	// TODO - eventually, need to resolve raw type warnings

	// ***************  instance variables ***************************************************************
	
	@Parameter
	protected List<Dataset> in;

	@Parameter(output=true)
	protected Dataset out;

	/** The imglib-ops function to execute. */
	private RealFunction<T> function;

	// ***************  public/protected interface ***************************************************************

	/** this constructor required for plugins that create a function that uses one of it's @Parameter values in construction */
	protected NAryOperation()
	{
		this.function = null;
	}
	
	/** preferred constructor */
	protected NAryOperation(RealFunction<T> function)
	{
		setFunc(function);
	}

	/** helper method that allows the function of an operation to be changed */
	public void setFunction(RealFunction<T> function)
	{
		setFunc(function);
	}
	
	/** runs the plugin applying the operation's function to the input and assigning it to the output */
	@Override
	public void run()
	{
		if (in == null)  // TODO - temporary code to test these until IJ2 plugins can correctly fill a List<Dataset> @Parameter
		{
			Image<UnsignedShortType> junkImage = createImage(new UnsignedShortType(), new int[]{200,200});
			Cursor<UnsignedShortType> cursor = junkImage.createCursor();
			int index = 0;
			for (UnsignedShortType pixRef : cursor)
				pixRef.set(index++);
			cursor.close();
			in = new ArrayList<Dataset>();
			in.add(new Dataset(junkImage));
		}
		
		if (function == null)
			throw new IllegalStateException("function reference is null: function must be set via constructor or setFunction() before calling NAryOperation::run()");
			
		//@SuppressWarnings("unchecked")
		final Image<T>[] inputs = new Image[in.size()];
		
		for (int i = 0; i < inputs.length; i++) {
			inputs[i] = imageFromDataset(in.get(i));
		}
		
		final Image<T> output;
		if (out != null)
			output = imageFromDataset(out);
		else
			output = zeroDataImageWithSameAttributes(inputs[0]);  // TODO - must be given at least one input image or this will be unhappy
		
		final AssignOperation<T> operation = new AssignOperation<T>(inputs, output, function);

		operation.execute();
		
		out = datasetFromImage(output);
	}

	// ***************  private interface ***************************************************************

	/** this exists so that anyone overriding setFunction() does not break NAryOperation(function) constructor */
	private void setFunc(RealFunction<T> func)
	{
		this.function = func;
	}
	
	/** create an image of given type and dimensions using specified container type */
	private static <K extends RealType<K>> Image<K> createImage(RealType<K> type, int[] dimensions)
	{
		PlanarContainerFactory cFact = new PlanarContainerFactory();
		ImageFactory<K> factory = new ImageFactory<K>((K)type, cFact);
		return factory.createImage(dimensions);
	}
	
	/** make an image that has same type and dimensions as Dataset (but in a planar container) */
	private Image<T> imageFromDataset(Dataset dataset)
	{
		//@SuppressWarnings("unchecked")
		return (Image<T>) dataset.getImage();
	}

	/** make an image that has same type, container, and dimensions as refImage */
	private Image<T> zeroDataImageWithSameAttributes(Image<T> refImage)
	{
		return refImage.createNewImage(refImage.getDimensions());
	}

	/** make a Dataset from an Image */
	private Dataset datasetFromImage(Image<T> image)
	{
		return new Dataset(image);
	}

}

@@ -263,70 +263,8 @@
 t;%0A%0A
-import mpicbg.imglib.container.planar.PlanarContainerFactory;%0A
 impo
@@ -333,49 +333,8 @@
 ge;%0A
-import mpicbg.imglib.image.ImageFactory;%0A
 impo
@@ -655,19 +655,18 @@
 eJPlugin
- %7B
 %0A
+%7B
 %0A%09// TOD
@@ -1393,32 +1393,29 @@
 uction */%0A%09p
-rotected
+ublic
  NAryOperati
@@ -1483,24 +1483,21 @@
 or */%0A%09p
-rotected
+ublic
  NAryOpe
@@ -1534,32 +1534,40 @@
 n)%0A%09%7B%0A%09%09
-setFunc(
+this.function = 
 function
 );%0A%09%7D%0A%0A%09
@@ -1554,25 +1554,24 @@
 n = function
-)
 ;%0A%09%7D%0A%0A%09/** h
@@ -1701,24 +1701,32 @@
 %7B%0A%09%09
-setFunc(
+this.function = 
 function
 );%0A%09
@@ -1721,17 +1721,16 @@
 function
-)
 ;%0A%09%7D%0A%09%0A%09
@@ -2029,28 +2029,46 @@
 Image = 
-create
+Dataset.createPlanar
 Image(
+%22%22, 
 new Unsi
@@ -3199,554 +3199,8 @@
 **%0A%0A
-%09/** this exists so that anyone overriding setFunction() does not break NAryOperation(function) constructor */%0A%09private void setFunc(RealFunction%3CT%3E func)%0A%09%7B%0A%09%09this.function = func;%0A%09%7D%0A%09%0A%09/** create an image of given type and dimensions using specified container type */%0A%09private static %3CK extends RealType%3CK%3E%3E Image%3CK%3E createImage(RealType%3CK%3E type, int%5B%5D dimensions)%0A%09%7B%0A%09%09PlanarContainerFactory cFact = new PlanarContainerFactory();%0A%09%09ImageFactory%3CK%3E factory = new ImageFactory%3CK%3E((K)type, cFact);%0A%09%09return factory.createImage(dimensions);%0A%09%7D%0A%09%0A
 %09/**

change constructors from protected to public allowing better reuse plus did some minor internal changes