package lpv.simulation.propagator;

import javax.vecmath.Matrix4f;
import javax.vecmath.Point3i;
import javax.vecmath.Vector4f;

import lpv.LPV;

public class SSLPVPropagator implements Propagator {
	// Original Author: Jesper Børlum, Brian Bunch Christensen, Thomas Kim Kjeldsen, Peter Trier Mikkelsen, 
	//					Karsten Østergaard Noe, Jens Rimestad, Jesper Mosegaard
	// /home/dt07jo1/Documents/SSLPV/Data/Shaders/PropagateFS.glsl
	// http://cg.alexandra.dk/publications/sslpv/
	
	
	
	private final static Matrix4f px = new Matrix4f(0.166666666660684f, 0.0f, 0.0f, -0.036816795777334f,
            0.0f, 0.069828636319029f, 0.0f, 0.0f,
            0.0f, 0.0f, 0.069828636319029f, 0.0f,
            -0.239943780012009f, 0.0f, 0.0f, 0.061658794702436f);

	private final static Matrix4f mx = new Matrix4f(0.166666666660684f, 0.0f, 0.0f, 0.036816795777334f,
	            0.0f, 0.069828636319029f, 0.0f, 0.0f,
	            0.0f, 0.0f, 0.069828636319029f, 0.0f,
	            0.239943780012009f, 0.0f, 0.0f, 0.061658794702436f);
	
	private final static Matrix4f py = new Matrix4f(0.166666666660684f, -0.036816795777334f, 0.0f, 0.0f,
	            -0.239943780012009f, 0.061658794702436f, 0.0f, 0.0f, 
	            0.0f, 0.0f, 0.069828636319029f, 0.0f,
	            0.0f, 0.0f, 0.0f, 0.069828636319029f);
	
	private final static Matrix4f my = new Matrix4f(0.166666666660684f, 0.036816795777334f, 0.0f, 0.0f,
	            0.239943780012009f, 0.061658794702436f, 0.0f, 0.0f, 
	            0.0f, 0.0f, 0.069828636319029f, 0.0f,
	            0.0f, 0.0f, 0.0f, 0.069828636319029f);
	
	private final static Matrix4f pz = new Matrix4f(0.166666666660684f, 0.0f, -0.036816795777334f, 0.0f,
	            0.0f, 0.069828636319029f, 0.0f, 0.0f, 
	            -0.239943780012009f, 0.0f, 0.061658794702436f, 0.0f,
	            0.0f, 0.0f, 0.0f, 0.069828636319029f);
	
	private final static Matrix4f mz = new Matrix4f(0.166666666660684f, 0.0f, 0.036816795777334f, 0.0f,
	            0.0f, 0.069828636319029f, 0.0f, 0.0f, 
	            0.239943780012009f, 0.0f, 0.061658794702436f, 0.0f,
	            0.0f, 0.0f, 0.0f, 0.069828636319029f);

	private float factor;
	
	public SSLPVPropagator(float factor) {
		this.factor = factor;
	}
	
	@Override
	public void propagate(LPV in, LPV out, LPV acc, LPV geometry, Point3i pos, int iteration) {
		// Find sample offsets
		Point3i kUp		= new Point3i(pos.x, 		pos.y + 1, 	pos.z);
		Point3i kDown	= new Point3i(pos.x, 		pos.y - 1, 	pos.z);
		Point3i kFront	= new Point3i(pos.x, 		pos.y, 		pos.z + 1);
		Point3i kBack	= new Point3i(pos.x, 		pos.y, 		pos.z - 1);
		Point3i kRight	= new Point3i(pos.x + 1, 	pos.y, 		pos.z);
		Point3i kLeft	= new Point3i(pos.x - 1, 	pos.y, 		pos.z);

		// Sample intensity
		Vector4f kUpSH;
		Vector4f kDownSH;
		Vector4f kFrontSH;
		Vector4f kBackSH;
		Vector4f kRightSH;
		Vector4f kLeftSH;
		
		kUpSH		= texture(in, kUp);
		kDownSH		= texture(in, kDown);
		kRightSH	= texture(in, kRight);
		kLeftSH		= texture(in, kLeft);
		kFrontSH	= texture(in, kFront);
		kBackSH		= texture(in, kBack);
		
		// Perform propagation
		Vector4f kRes = new Vector4f(0.0f, 0.0f, 0.0f, 0.0f);
		
		// RIGHT (+x)
		px.transform(kRightSH);
		kRes.add(kRightSH);

		// LEFT (-x)
		mx.transform(kLeftSH);
		kRes.add(kLeftSH);

		// UP (+y)
		py.transform(kUpSH);
		kRes.add(kUpSH);

		// DOWN (-y)
		my.transform(kDownSH);
		kRes.add(kDownSH);

		// FRONT (+z)
		pz.transform(kFrontSH);
		kRes.add(kFrontSH);

		// BACK (-z)
		mz.transform(kBackSH);
		kRes.add(kBackSH);
		
		// Inject some energy - For artist directability
		kRes.scale(factor);

		// Output result
		out.get(pos.x, pos.y, pos.z).set(kRes);
		acc.get(pos.x, pos.y, pos.z).add(kRes);
	}

	private Vector4f texture(LPV in, Point3i p) {
		if(p.x >= 0 && p.x < in.getWidth() && 
				p.y >= 0 && p.y < in.getHeight() && 
				p.z >= 0 && p.z < in.getDepth()) {
			return new Vector4f(in.get(p.x, p.y, p.z));
		} else {
			return new Vector4f(0.0f, 0.0f, 0.0f, 0.0f);
		}
	}
}
