/*******************************************************************************
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 * 
 * This library 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 Lesser General Public License for more details.
 * 
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
 * USA
 * 
 * Contact Info:
 * 	Bruce Donald
 * 	Duke University
 * 	Department of Computer Science
 * 	Levine Science Research Center (LSRC)
 * 	Durham
 * 	NC 27708-0129 
 * 	USA
 * 	brd@cs.duke.edu
 * 
 * Copyright (C) 2011 Jeffrey W. Martin and Bruce R. Donald
 * 
 * <signature of Bruce Donald>, April 2011
 * Bruce Donald, Professor of Computer Science
 ******************************************************************************/


package edu.duke.donaldLab.jdshot.grid.dn;

import edu.duke.donaldLab.jdshot.grid.GridCell;
import edu.duke.donaldLab.jdshot.grid.SearchSpace;
import edu.duke.donaldLab.jdshot.search.CellSplitter;

public class DnCellSplitter implements CellSplitter
{
	/**************************
	 *   Definitions
	 **************************/
	
	public enum Subspace
	{
		Translations,
		Rotations;
	}
	
	
	/**************************
	 *   Data Members
	 **************************/
	
	private DnGridPoint m_primalDeltas;
	
	
	/**************************
	 *   Constructors
	 **************************/
	
	public DnCellSplitter( SearchSpace searchSpace )
	{
		m_primalDeltas = (DnGridPoint)searchSpace.getInitialCells().get( 0 ).getDeltas();
	}
	

	/**************************
	 *   Methods
	 **************************/
	
	@Override
	public void split( GridCell left, GridCell right, GridCell source )
	{
		// make sure the casts we're about to make are safe
		assert( left instanceof DnGridCell );
		assert( right instanceof DnGridCell );
		assert( source instanceof DnGridCell );
		
		split( (DnGridCell)left, (DnGridCell)right, (DnGridCell)source );
	}
	
	public void split( DnGridCell left, DnGridCell right, DnGridCell source )
	{
		// split using primal deltas
		// NOTE: we could use fancier heuristics here
		splitDeltas( left, right, source, m_primalDeltas );
	}
	
	private void splitDeltas( DnGridCell leftCell, DnGridCell rightCell, DnGridCell source, DnGridPoint primalDeltas )
	{
		// just in case...
		assert( leftCell != null );
		assert( rightCell != null );
		assert( source != null );
		
		// scale the lengths by the primal deltas to unbias searching certain dimensions first
		DnGridPoint scaled = source.getDeltas();
		scaled.x /= primalDeltas.x;
		scaled.y /= primalDeltas.y;
		scaled.z /= primalDeltas.z;
		scaled.phi /= primalDeltas.phi;
		scaled.theta /= primalDeltas.theta;
		scaled.rho /= primalDeltas.rho;
		
		// decide which dimension to split
		int dimension = -1;
		if( gta( scaled.x, scaled.y, scaled.z, scaled.phi, scaled.theta, scaled.rho ) )
		{
			dimension = 0;
		}
		else if( gta( scaled.y, scaled.x, scaled.z, scaled.phi, scaled.theta, scaled.rho ) )
		{
			dimension = 1;
		}
		else if( gta( scaled.z, scaled.x, scaled.y, scaled.phi, scaled.theta, scaled.rho ) )
		{
			dimension = 2;
		}
		else if( gta( scaled.phi, scaled.x, scaled.y, scaled.z, scaled.theta, scaled.rho ) )
		{
			dimension = 3;
		}
		else if( gta( scaled.theta, scaled.x, scaled.y, scaled.z, scaled.phi, scaled.rho ) )
		{
			dimension = 4;
		}
		else if( gta( scaled.rho, scaled.x, scaled.y, scaled.z, scaled.phi, scaled.theta ) )
		{
			dimension = 5;
		}
		
		assert( dimension != -1 ) : "Didn't choose a valid dimension to split!";
		
		// split along the selected dimension
		source.split( leftCell, rightCell, dimension );
	}
	
	public void splitSubspace( DnGridCell leftCell, DnGridCell rightCell, DnGridCell source, Subspace subspace )
	{
		// just in case...
		assert( leftCell != null );
		assert( rightCell != null );
		
		// decide which dimension to split (without scalings) in the desired subspace
		int dimension = -1;
		DnGridPoint deltas = source.getDeltas();
		switch( subspace )
		{
			case Translations:
				if( gta( deltas.x, deltas.y, deltas.z ) )
				{
					dimension = 0;
				}
				else if( gta( deltas.y, deltas.x, deltas.z ) )
				{
					dimension = 1;
				}
				else if( gta( deltas.z, deltas.x, deltas.y ) )
				{
					dimension = 2;
				}
			break;
			
			case Rotations:
				if( gta( deltas.phi, deltas.theta, deltas.rho ) )
				{
					dimension = 3;
				}
				else if( gta( deltas.theta, deltas.phi, deltas.rho ) )
				{
					dimension = 4;
				}
				else if( gta( deltas.rho, deltas.phi, deltas.theta ) )
				{
					dimension = 5;
				}
			break;
		}
		
		assert( dimension != -1 ) : "Didn't choose a valid dimension to split!";
		
		// split along the selected dimension
		source.split( leftCell, rightCell, dimension );
	}
	
	
	/**************************
	 *   Functions
	 **************************/
	
	private boolean gta( double a, double b, double c )
	{
		return a >= b && a >= c;
	}
	
	private boolean gta( double a, double b, double c, double d, double e, double f )
	{
		return a >= b && a >= c && a >= d && a >= e && a >= f;
	}
}
