/*
 *                    BioJava development code
 *
 * This code may be freely distributed and modified under the
 * terms of the GNU Lesser General Public Licence.  This should
 * be distributed with the code.  If you do not have a copy,
 * see:
 *
 *      http://www.gnu.org/copyleft/lesser.html
 *
 * Copyright for this code is held jointly by the individual
 * authors.  These should be listed in @author doc comments.
 *
 * For more information on the BioJava project and its aims,
 * or to join the biojava-l mailing list, visit the home page
 * at:
 *
 *      http://www.biojava.org/
 *
 */
package org.biojava.nbio.structure;

import static org.junit.Assert.*;

import javax.vecmath.Matrix4d;
import javax.vecmath.Point3d;

import org.biojava.nbio.structure.geometry.Matrices;
import org.biojava.nbio.structure.jama.Matrix;
import org.junit.Test;

public class TestCalc {

	@Test
	public void testAngle() {
		Atom a = getAtom(1,1,1);

		Atom b = getAtom(2,2,2);

		// 2 parallel vectors should return 0
		assertEquals(0.0, Calc.angle(a, b),0.00001);

		// range should be [0,180]
		Atom ref = getAtom(1,0,0);
		Atom a1 = getAtom(1,1,0);
		Atom a2 = getAtom(0,1,0);
		Atom a3 = getAtom(-1,1,0);
		Atom a4 = getAtom(-1,0,0);
		Atom a5 = getAtom(-1,-1,0);
		Atom a6 = getAtom(0,-1,0);
		Atom a7 = getAtom(1,-1,0);

		assertEquals(180.0,Calc.angle(ref, a4),0.00001);

		assertTrue(Calc.angle(ref, a1)>=0 && Calc.angle(ref,a1)<=180.0);
		assertTrue(Calc.angle(ref, a2)>=0 && Calc.angle(ref,a2)<=180.0);
		assertTrue(Calc.angle(ref, a3)>=0 && Calc.angle(ref,a3)<=180.0);
		assertTrue(Calc.angle(ref, a4)>=0 && Calc.angle(ref,a4)<=180.0);
		assertTrue(Calc.angle(ref, a5)>=0 && Calc.angle(ref,a5)<=180.0);
		assertTrue(Calc.angle(ref, a6)>=0 && Calc.angle(ref,a6)<=180.0);
		assertTrue(Calc.angle(ref, a7)>=0 && Calc.angle(ref,a7)<=180.0);

		Atom c = getAtom(0,0,0);
		Atom d = getAtom(0,0,0);

		assertEquals(Double.NaN, Calc.angle(a,c),0.00001);
		assertEquals(Double.NaN, Calc.angle(c,d),0.00001);
	}

	@Test
	public void testTorsionAngle() {
		Atom a = getAtom(0,0,0);

		Atom b = getAtom(1,0,0);

		Atom c = getAtom(2,0,0);

		Atom d = getAtom(3,0,0);

		// all 4 points colinear

		assertEquals(Double.NaN, Calc.torsionAngle(a, b, c, d),0.00001);

		// first 3 colinear
		d = getAtom(3,1,0);
		assertEquals(Double.NaN, Calc.torsionAngle(a, b, c, d),0.00001);

		// second 3 colinear
		d = getAtom(3,0,0);
		a = getAtom(1,1,0);
		assertEquals(Double.NaN, Calc.torsionAngle(a, b, c, d),0.00001);

		// coplanar vectors
		a = getAtom(0,0,0);
		b = getAtom(1,0,0);
		c = getAtom(1, 1, 0);
		d = getAtom(2, 1, 0);

		assertEquals(180, Calc.torsionAngle(a, b, c, d),0.00001);

		c = getAtom(-1,-1, 0);
		d = getAtom(-2,-1, 0);

		assertEquals(0, Calc.torsionAngle(a, b, c, d),0.00001);
	}

	@Test
	public void testJamaTransformation() {

		Atom atom = getAtom(1.0, 1.0, 1.0);

		//Identity transform
		Matrix identR = Matrix.identity(3, 3);
		Atom identT = getAtom(0, 0, 0);
		Calc.rotate(atom, identR);
		Calc.shift(atom, identT);

		Point3d expected = new Point3d(1.0, 1.0, 1.0);
		Point3d actual = atom.getCoordsAsPoint3d();

		assertEquals(expected, actual);

		//Sample transform: calc transposes automatically the matrix
		//because it is a pre-multiplication rotation matrix
		Matrix sampleR = Matrices.getRotationJAMA(getSampleTransform());
		Atom sampleT = Calc.getTranslationVector(getSampleTransform());
		Calc.rotate(atom, sampleR);
		Calc.shift(atom, sampleT);

		expected = new Point3d(2.0, 7.0, -1.3);
		actual = atom.getCoordsAsPoint3d();

		assertEquals(expected, actual);
	}

	@Test
	public void testVecmathTransformation() {

		Atom atom = getAtom(1.0, 1.0, 1.0);

		//Identity transform
		Matrix4d ident = new Matrix4d();
		ident.setIdentity();
		Calc.transform(atom, ident);

		Point3d expected = new Point3d(1.0, 1.0, 1.0);
		Point3d actual = atom.getCoordsAsPoint3d();

		assertEquals(expected, actual);

		//Sample transform
		Matrix4d sample = getSampleTransform();
		Calc.transform(atom, sample);

		expected = new Point3d(2.0, 7.0, -1.3);
		actual = atom.getCoordsAsPoint3d();

		assertEquals(expected, actual);
	}

	private static Atom getAtom(double x, double y, double z) {
		Atom a = new AtomImpl();
		a.setX(x);
		a.setY(y);
		a.setZ(z);
		return a;
	}

	private static Matrix4d getSampleTransform(){

		Matrix4d sample = new Matrix4d(new double[] {1.0,1.5,0.5,-1.0,
		                                0.5,0.5,1.0,5.0,
		                                0.3,0.4,0.5,-2.5,
		                                0.0,0.0,0.0,1.0});
		return sample;
	}

}
