package imagej.core.plugins.assign;

import imagej.ImageJ;
import imagej.data.Dataset;
import imagej.data.Position;
import imagej.display.Display;
import imagej.display.DisplayService;
import imagej.ext.plugin.ImageJPlugin;
import imagej.ext.plugin.PreviewPlugin;
import imagej.util.IntRect;
import net.imglib2.RandomAccess;
import net.imglib2.img.Axes;
import net.imglib2.ops.operation.RegionIterator;
import net.imglib2.ops.operator.UnaryOperator;
import net.imglib2.type.numeric.RealType;


public abstract class AbstractPreviewPlugin implements ImageJPlugin, PreviewPlugin {

	// -- instance variables that are Parameters --

	private Dataset dataset;
	
	private double[] dataBackup;

	private long[] planeSpan;
	
	private RegionIterator iter;
	
	@Override
	public void run() {
		if (previewOn())
			restoreViewedPlane();
		transformDataset();
	}

	@Override
	public void preview() {
		if (dataBackup == null)
			saveViewedPlane();
		else
			restoreViewedPlane();
		if (previewOn())
			transformViewedPlane();
	}

	@Override
	public void cancel() {
		if (previewOn())
			restoreViewedPlane();
	}

	public abstract Display getDisplay();
	public abstract boolean previewOn();
	public abstract UnaryOperator getOperator();
	
	// -- private helpers --
	
	// NB
	// - It is possible to design an elegant plane cloner but can only assume
	// certain types. And since dataset.setPlane() does not copy data if not
	// PlanarAccess backed it is not helpful to have exact data.
	// - we could make Dataset.copyOfPlane() visible but it assumes things
	// about all types being native types. And again can't set data.
	// - so we opt for copying to an array of doubles. However this could
	// cause precision loss for long data
	
	private void saveViewedPlane() {

		// get Dataset we'll be transforming
		dataset = ImageJ.get(DisplayService.class).getActiveDataset(getDisplay());
		
		// check dimensions of Dataset
		int xIndex = dataset.getAxisIndex(Axes.X);
		int yIndex = dataset.getAxisIndex(Axes.Y);
		if ((xIndex != 0) || (yIndex != 1))
			throw new IllegalArgumentException("display is not ordered with X axis 1st and Y axis 2nd.");
		long[] dims = dataset.getDims();
		long w = dims[0];
		long h = dims[1];
		if (w*h > Integer.MAX_VALUE)
			throw new IllegalArgumentException("plane too large to copy into memory");
		
		// calc origin
		Position planePos = getDisplay().getActiveView().getPlanePosition();
		long[] planeOrigin = new long[dims.length];
		planeOrigin[0] = 0;
		planeOrigin[1] = 0;
		for (int i = 2; i < planeOrigin.length; i++)
			planeOrigin[i] = planePos.getLongPosition(i-2);
		
		// calc span
		planeSpan = new long[dims.length];
		planeSpan[0] = w;
		planeSpan[1] = h;
		for (int i = 2; i < planeSpan.length; i++)
			planeSpan[i] = 1;
		
		// setup region iterator
		RandomAccess<? extends RealType<?>> accessor = dataset.getImgPlus().randomAccess();
		iter = new RegionIterator(accessor, planeOrigin, planeSpan);

		// copy data to a double[]
		dataBackup = new double[(int)(w*h)];
		int index = 0;
		while (iter.hasNext()) {
			iter.next();
			dataBackup[index++] = iter.getValue();
		}
	}
	
	private void restoreViewedPlane() {
		
		// restore data from our double[]
		int index = 0;
		iter.reset();
		while (iter.hasNext()) {
			iter.next();
			iter.setValue(dataBackup[index++]);
		}
		dataset.update();
	}
	
	private void transformDataset() {
		UnaryOperator op = getOperator();
		InplaceUnaryTransform transform = new InplaceUnaryTransform(dataset, op);
		transform.run();
	}

	// TODO - transforms whole plane. Only transform the current selection?
	
	private void transformViewedPlane() {
		UnaryOperator op = getOperator();
		InplaceUnaryTransform transform = new InplaceUnaryTransform(dataset, op);
		transform.setRegion(planeSpan, new IntRect(0,0,(int)planeSpan[0],(int)planeSpan[1]));
		transform.run();
	}

}

@@ -261,36 +261,8 @@
 in;%0A
-import imagej.util.IntRect;%0A
 impo
@@ -654,16 +654,47 @@
 ackup;%0A%0A
+%09private long%5B%5D planeOrigin;%0A%09%0A
 %09private
@@ -712,17 +712,16 @@
 neSpan;%0A
-%09
 %0A%09privat
@@ -2070,16 +2070,21 @@
 ception(
+%0A%09%09%09%09
 %22display
@@ -2411,23 +2411,16 @@
 on();%0A%09%09
-long%5B%5D 
 planeOri
@@ -2829,17 +2829,20 @@
 cessor =
- 
+%0A%09%09%09
 dataset.
@@ -3329,24 +3329,104 @@
 ate();%0A%09%7D%0A%09%0A
+%09// TODO - transforms all data. Should only transform current selection planes%0A%0A
 %09private voi
@@ -3609,21 +3609,20 @@
 rms 
-whole
+full
  plane. 
 Only
@@ -3617,17 +3617,25 @@
  plane. 
-O
+Make it o
 nly tran
@@ -3644,12 +3644,8 @@
 orm 
-the 
 curr
@@ -3657,17 +3657,17 @@
 election
-?
+.
 %0A%09%0A%09priv
@@ -3843,66 +3843,25 @@
 lane
-Span, new IntRect(0,0,(int)planeSpan%5B0%5D,(int)planeSpan%5B1%5D)
+Origin, planeSpan
 );%0A%09

fix bug that caused datasets of 3 or more dimensions to preview the incorrect plane