/*-
 * #%L
 * Multiview stitching of large datasets.
 * %%
 * Copyright (C) 2016 - 2017 Big Stitcher developers.
 * %%
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as
 * published by the Free Software Foundation, either version 2 of the
 * License, or (at your option) any later version.
 * 
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public
 * License along with this program.  If not, see
 * <http://www.gnu.org/licenses/gpl-2.0.html>.
 * #L%
 */
package net.imglib2.algorithm.phasecorrelation;

import java.io.File;

import ij.ImageJ;
import net.imglib2.Cursor;
import net.imglib2.FinalInterval;
import net.imglib2.Interval;
import net.imglib2.Localizable;
import net.imglib2.Point;
import net.imglib2.RandomAccess;
import net.imglib2.RandomAccessible;
import net.imglib2.RandomAccessibleInterval;
import net.imglib2.RealRandomAccess;
import net.imglib2.Sampler;
import net.imglib2.algorithm.phasecorrelation.deprecated.Blending;
import net.imglib2.img.Img;
import net.imglib2.img.array.ArrayImgs;
import net.imglib2.img.display.imagej.ImageJFunctions;
import net.imglib2.type.numeric.RealType;
import net.imglib2.type.numeric.real.FloatType;
import net.imglib2.util.Intervals;
import net.imglib2.view.Views;
import net.preibisch.mvrecon.process.fusion.transformed.weights.BlendingRealRandomAccessible;


public class BlendedExtendedMirroredRandomAccesible2 <T extends RealType<T>>implements RandomAccessible<T> {

	private RandomAccessibleInterval<T> img;
	BlendingRealRandomAccessible blending;
	private int numDimensions;
	private FinalInterval extDims;
	
	public BlendedExtendedMirroredRandomAccesible2(RandomAccessibleInterval<T> img, int[] border) {
		this.img = img;
		this.numDimensions = img.numDimensions();
		
		float[] blendingBorder = new float[numDimensions];
		float[] border2 = new float[numDimensions];
		
		this.extDims = new FinalInterval(img);		
		for (int i = 0; i < numDimensions; i++)
		{
			extDims = Intervals.expand(extDims, border[i], i);
			blendingBorder[i] = border[i];
			border2[i] = 0.0f;
		}
		
		this.blending = new BlendingRealRandomAccessible(extDims, border2, blendingBorder);
	}
	
	
	@Override
	public int numDimensions() {
		return numDimensions;
	}
	
	public FinalInterval getExtInterval() {
		return extDims;
	}

	/**
	 * TODO: For efficiency reasons we should implement it as a RandomAccess that actually updates the underlying
	 * imgRA for every move. This way, the outofbounds can work very efficiently when it is iterated through Views.iterable().cursor()
	 */
	public class BlendedRandomAccess extends Point implements RandomAccess<T>
	{
		public BlendedRandomAccess() {
			super(img.numDimensions());
		}

		RandomAccess<T> imgRA = Views.extendMirrorSingle(img).randomAccess();
		RealRandomAccess<FloatType> blendRA = blending.realRandomAccess();
		T val = imgRA.get().createVariable();

		@Override
		public T get() {
			val.setReal(imgRA.get().getRealFloat() * blendRA.get().getRealFloat());
			return val;
		}

		@Override
		public void fwd(int d) {
			super.fwd(d);
			imgRA.fwd(d);
			blendRA.fwd(d);
		}

		@Override
		public void bck(int d) {
			super.bck(d);
			imgRA.bck(d);
			blendRA.bck(d);
		}

		@Override
		public void move(int distance, int d) {
			super.move(distance, d);
			imgRA.move(distance, d);
			blendRA.move(distance, d);
		}

		@Override
		public void move(long distance, int d) {
			super.move(distance, d);
			imgRA.move(distance, d);
			blendRA.move(distance, d);
		}

		@Override
		public void move(Localizable localizable) {
			super.move(localizable);
			imgRA.move(localizable);
			blendRA.move(localizable);
		}

		@Override
		public void move(int[] distance) {
			super.move(distance);
			imgRA.move(distance);
			blendRA.move(distance);
		}

		@Override
		public void move(long[] distance) {
			super.move(distance);
			imgRA.move(distance);
			blendRA.move(distance);
		}

		@Override
		public void setPosition(Localizable localizable) {
			super.setPosition(localizable);
			imgRA.setPosition(localizable);
			blendRA.setPosition(localizable);
		}

		@Override
		public void setPosition(int[] position) {
			super.setPosition(position);
			imgRA.setPosition(position);
			blendRA.setPosition(position);
		}

		@Override
		public void setPosition(long[] position) {
			super.setPosition(position);
			imgRA.setPosition(position);
			blendRA.setPosition(position);
		}

		@Override
		public void setPosition(int position, int d) {
			super.setPosition(position, d);
			imgRA.setPosition(position, d);
			blendRA.setPosition(position, d);
		}

		@Override
		public void setPosition(long position, int d) {
			super.setPosition(position, d);
			imgRA.setPosition(position, d);
			blendRA.setPosition(position, d);
		}

		@Override
		public Sampler<T> copy() {
			return copyRandomAccess();
		}

		@Override
		public RandomAccess<T> copyRandomAccess() {
			BlendedRandomAccess a = new BlendedRandomAccess();
			a.move(this);
			return a;
		}
		
	}
	
	@Override
	public RandomAccess<T> randomAccess() {
		return new BlendedRandomAccess();
	}

	@Override
	public RandomAccess<T> randomAccess(Interval interval) {
		return randomAccess();
	}
	

	public static void main(String[] args) {
		
		new ImageJ();
		Img<FloatType> img1 = ImgLib2Util.openAs32Bit(new File("src/main/resources/img1.tif"));
		long[] dims = new long[img1.numDimensions()];

		BlendedExtendedMirroredRandomAccesible2<FloatType> ext = new BlendedExtendedMirroredRandomAccesible2<FloatType>(img1, new int[]{100, 100, 10});
		
		ext.getExtInterval().dimensions(dims);		
		Img<FloatType> img2 = ArrayImgs.floats(dims);

		// TODO: For efficiency reasons we should now also iterate the BlendedExtendedMirroredRandomAccesible and not the image
		long start = System.currentTimeMillis();
		
		Cursor<FloatType> c = img2.cursor();
		for (FloatType e : Views.iterable(Views.interval(ext, ext.getExtInterval())))
		{
			c.fwd();
			c.get().set(e);
		}
		
		long end = System.currentTimeMillis();		
		System.out.println(end-start);
		
		ImageJFunctions.show(img2);	
		
		//RandomAccessibleInterval<FloatType> img3 = Views.interval(ext, ext.getExtInterval());		
		//ImageJFunctions.show(img3);
		

		

	}
}