package lpv.simulation.propagator;

import javax.vecmath.Point3i;
import javax.vecmath.Vector2f;
import javax.vecmath.Vector4f;

import lpv.LPV;

public class LeeSalzmanPropagator implements Propagator {
	private float factor;

	// Original Author: Lee Salzman
	// /home/dt07jo1/Documents/lpv/propgrid3d.ps
	// http://lee.fov120.com/
	
	public LeeSalzmanPropagator(float factor) {
		this.factor = factor;
	}
	
	@Override
	public void propagate(LPV in, LPV out, LPV acc, LPV geometry, Point3i pos, int iteration) {
		Vector2f coeffsmh = new Vector2f(0.25f, -0.375f);
		Vector2f coeffsph = new Vector2f(0.25f, 0.375f);
		
		Vector2f coeffsm2 = new Vector2f(1.0f, -1.5f);
		Vector2f coeffsp2 = new Vector2f(1.0f, 1.5f);
		
		Point3i tc;
		
		float f;
		Vector4f i;
		Vector4f o = out.get(pos.x, pos.y, pos.z);
		o.set(0.0f, 0.0f, 0.0f, 0.0f);
		
		tc = new Point3i(pos.x, 	pos.y + 1, 	pos.z);
		if(tc.x >= 0 && tc.x < in.getWidth() && tc.y >= 0 && tc.y < in.getHeight() && tc.z >= 0 && tc.z < in.getDepth()) {
			i = in.get(tc.x, tc.y, tc.z); i = new Vector4f(i.x+0.5f, i.y+0.5f, i.z+0.5f, i.w+0.5f);
			f = Math.max(0.0f, coeffsm2.dot(new Vector2f(i.x, i.y)) + 0.25f);
			o.add(new Vector4f(coeffsmh.x * f, coeffsmh.y * f, 0.0f, 0.0f));
		}
		
		tc = new Point3i(pos.x, 	pos.y - 1, 	pos.z);
		if(tc.x >= 0 && tc.x < in.getWidth() && tc.y >= 0 && tc.y < in.getHeight() && tc.z >= 0 && tc.z < in.getDepth()) {
			i = in.get(tc.x, tc.y, tc.z); i = new Vector4f(i.x+0.5f, i.y+0.5f, i.z+0.5f, i.w+0.5f);
			f = Math.max(0.0f, coeffsp2.dot(new Vector2f(i.x, i.y)) - 1.25f);
			o.add(new Vector4f(coeffsph.x * f, coeffsph.y * f, 0.0f, 0.0f));
		}
		
		tc = new Point3i(pos.x + 1,	pos.y, 		pos.z);
		if(tc.x >= 0 && tc.x < in.getWidth() && tc.y >= 0 && tc.y < in.getHeight() && tc.z >= 0 && tc.z < in.getDepth()) {
			i = in.get(tc.x, tc.y, tc.z); i = new Vector4f(i.x+0.5f, i.y+0.5f, i.z+0.5f, i.w+0.5f);
			f = Math.max(0.0f, coeffsm2.dot(new Vector2f(i.x, i.w)) + 0.25f);
			o.add(new Vector4f(coeffsmh.x * f, 0.0f, 0.0f, coeffsmh.y * f));
		}
		
		tc = new Point3i(pos.x - 1,	pos.y, 		pos.z);
		if(tc.x >= 0 && tc.x < in.getWidth() && tc.y >= 0 && tc.y < in.getHeight() && tc.z >= 0 && tc.z < in.getDepth()) {
			i = in.get(tc.x, tc.y, tc.z); i = new Vector4f(i.x+0.5f, i.y+0.5f, i.z+0.5f, i.w+0.5f);
			f = Math.max(0.0f, coeffsp2.dot(new Vector2f(i.x, i.w)) - 1.25f);
			o.add(new Vector4f(coeffsph.x * f, 0.0f, 0.0f, coeffsph.y * f));
		}
		
		tc = new Point3i(pos.x, 	pos.y, 		pos.z + 1);
		if(tc.x >= 0 && tc.x < in.getWidth() && tc.y >= 0 && tc.y < in.getHeight() && tc.z >= 0 && tc.z < in.getDepth()) {
			i = in.get(tc.x, tc.y, tc.z); i = new Vector4f(i.x+0.5f, i.y+0.5f, i.z+0.5f, i.w+0.5f);
			f = Math.max(0.0f, coeffsp2.dot(new Vector2f(i.x, i.z)) - 1.25f);
			o.add(new Vector4f(coeffsph.x * f, 0.0f, coeffsph.y * f, 0.0f));
		}
		
		tc = new Point3i(pos.x, 	pos.y, 		pos.z - 1);
		if(tc.x >= 0 && tc.x < in.getWidth() && tc.y >= 0 && tc.y < in.getHeight() && tc.z >= 0 && tc.z < in.getDepth()) {
			i = in.get(tc.x, tc.y, tc.z); i = new Vector4f(i.x+0.5f, i.y+0.5f, i.z+0.5f, i.w+0.5f);
			f = Math.max(0.0f, coeffsm2.dot(new Vector2f(i.x, i.z)) + 0.25f);
			o.add(new Vector4f(coeffsmh.x * f, 0.0f, coeffsmh.y * f, 0.0f));
		}
		
		o.scale(factor);
		if(iteration == 0) {
			o.add(in.get(pos.x, pos.y, pos.z));
		}
		acc.get(pos.x, pos.y, pos.z).add(o);
	}
}
