/*-
 * #%L
 * Fiji distribution of ImageJ for the life sciences.
 * %%
 * Copyright (C) 2007 - 2021 Fiji 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 spim.process.fusion.deconvolution;

import java.util.concurrent.Callable;

import net.imglib2.Cursor;
import net.imglib2.RandomAccessibleInterval;
import net.imglib2.RealRandomAccess;
import net.imglib2.interpolation.randomaccess.NLinearInterpolatorFactory;
import net.imglib2.realtransform.AffineTransform3D;
import net.imglib2.type.numeric.real.FloatType;
import net.imglib2.view.Views;
import spim.process.fusion.FusionHelper;
import spim.process.fusion.ImagePortion;

/**
 * Fuse one portion of a paralell fusion, supports no weights
 * 
 * @author Stephan Preibisch (stephan.preibisch@gmx.de)
 */
public class TransformInput implements Callable< String >
{
	final ImagePortion portion;
	final RandomAccessibleInterval< FloatType > img;
	final AffineTransform3D transform;
	final RandomAccessibleInterval< FloatType > transformedImg;
	final int offsetX, offsetY, offsetZ, imgSizeX, imgSizeY, imgSizeZ;

	public TransformInput(
			final ImagePortion portion,
			final RandomAccessibleInterval< FloatType > img,
			final AffineTransform3D transform,
			final RandomAccessibleInterval< FloatType > transformedImg,
			final long[] offset )
	{
		this.portion = portion;
		this.img = img;
		this.transform = transform;
		this.transformedImg = transformedImg;

		this.offsetX = (int)offset[ 0 ];
		this.offsetY = (int)offset[ 1 ];
		this.offsetZ = (int)offset[ 2 ];

		this.imgSizeX = (int)img.dimension( 0 );
		this.imgSizeY = (int)img.dimension( 1 );
		this.imgSizeZ = (int)img.dimension( 2 );
	}
	
	@Override
	public String call() throws Exception 
	{
		final NLinearInterpolatorFactory< FloatType > f = new NLinearInterpolatorFactory< FloatType >();
		
		// make the interpolators and get the transformations
		final RealRandomAccess< FloatType > ir = Views.interpolate( Views.extendMirrorSingle( img ), f ).realRandomAccess();
		final Cursor< FloatType > cursor = Views.iterable( transformedImg ).localizingCursor();

		final float[] s = new float[ 3 ];
		final float[] t = new float[ 3 ];
		
		cursor.jumpFwd( portion.getStartPosition() );
		
		for ( int j = 0; j < portion.getLoopSize(); ++j )
			loop( cursor, ir, transform, s, t, offsetX, offsetY, offsetZ, imgSizeX, imgSizeY, imgSizeZ );
		
		return portion + " finished successfully (transform input & no weights).";
	}

	private static final void loop(
			final Cursor< FloatType > cursor,
			final RealRandomAccess< FloatType > ir,
			final AffineTransform3D transform,
			final float[] s, final float[] t,
			final int offsetX, final int offsetY, final int offsetZ,
			final int imgSizeX, final int imgSizeY, final int imgSizeZ )
	{
		// move img cursor forward any get the value (saves one access)
		final FloatType v = cursor.next();
		cursor.localize( s );

		s[ 0 ] += offsetX;
		s[ 1 ] += offsetY;
		s[ 2 ] += offsetZ;

		transform.applyInverse( t, s );

		if ( FusionHelper.intersects( t[ 0 ], t[ 1 ], t[ 2 ], imgSizeX, imgSizeY, imgSizeZ ) )
		{
			ir.setPosition( t );

			// do not accept 0 values in the data where image data is present, 0 means no image data is available
			// (used in MVDeconvolution.computeQuotient)
			v.set( Math.max( MVDeconvolution.minValue, ir.get().get() ) );
		}
	}
}
