/*******************************************************************************
 * 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.analyze;

import java.io.File;

import edu.duke.donaldLab.jdshot.grid.CellIterator;
import edu.duke.donaldLab.jdshot.grid.CellReader;
import edu.duke.donaldLab.jdshot.grid.GridPointAdapterCenter;
import edu.duke.donaldLab.jdshot.grid.PointIterator;
import edu.duke.donaldLab.jdshot.grid.PointReader;
import edu.duke.donaldLab.jdshot.search.SearchContext;
import edu.duke.donaldLab.share.perf.Progress;
import edu.duke.donaldLab.share.protein.Protein;

public class Analyzer
{
	/**************************
	 *   Data Members
	 **************************/
	
	private SearchContext m_searchContext;
	private AnalysisContext m_analysisContext;
	private File m_inFile;
	
	
	/**************************
	 *   Methods
	 **************************/
	
	public void init( SearchContext searchContext, AnalysisContext analysisContext, File inFile )
	{
		// save parameters
		m_searchContext = searchContext;
		m_analysisContext = analysisContext;
		m_inFile = inFile;
	}
	
	public int analyze( int numThreads )
	throws Exception
	{
		// determine the analysis mode
		AnalysisMode mode = null;
		String extension = m_inFile.getName().substring( m_inFile.getName().lastIndexOf( '.' ) + 1 );
		if( extension.equals( "cells" ) )
		{
			mode = AnalysisMode.Cells;
		}
		else if( extension.equals( "points" ) )
		{
			mode = AnalysisMode.Points;
		}
		else
		{
			throw new Exception( "Invalid input cells!" );
		}
		
		// get the appropriate iterators
		CellIterator iterCell = null;
		PointIterator iterPoint = null;
		Progress progress = null;
		switch( mode )
		{
			case Cells:
				iterCell = new CellIterator( new CellReader( m_inFile, m_searchContext.getSymmetry() ) );
				progress = new Progress( iterCell.getNumCells(), 15000 );
			break;
			
			case Points:
				iterPoint = new PointIterator( new PointReader( m_inFile, m_searchContext.getSymmetry() ) );
				progress = new Progress( iterPoint.getNumPoints(), 15000 );
			break;
		}
		
		// get a cell analyzer
		SCSAnalyzer scsAnalyzer = new SCSAnalyzer( m_searchContext, m_analysisContext );
		
		// get the reference backbone if needed
		Protein referenceBackbone = null;
		if( m_analysisContext.getOligomer() != null )
		{
			referenceBackbone = m_analysisContext.getOligomer().getBackbone();
		}
		
		// stats vars
		double totalVolume = 0.0;
		double minReferenceRmsd = Double.POSITIVE_INFINITY;
		double maxReferenceRmsd = Double.NEGATIVE_INFINITY;
		double sumReferenceRmsd = 0.0;
		double minNoeRmsd = Double.POSITIVE_INFINITY;
		double maxNoeRmsd = Double.NEGATIVE_INFINITY;
		double sumNoeRmsd = 0.0;
		
		// start some search processors
		AnalysisProcessorSynchronizer synch = new AnalysisProcessorSynchronizer(
			mode,
			iterCell,
			iterPoint,
			new GridPointAdapterCenter(),
			progress
		);
		AnalysisProcessor[] processors = new AnalysisProcessor[numThreads];
		Thread[] threads = new Thread[numThreads];
		for( int i=0; i<numThreads; i++ )
		{
			processors[i] = new AnalysisProcessor( scsAnalyzer, synch, referenceBackbone );
			threads[i] = new Thread( processors[i] );
			threads[i].start();
		}
		
		// wait for thread to finish
		for( int i=0; i<numThreads; i++ )
		{
			threads[i].join();
		}
		
		// compile the stats
		for( int i=0; i<numThreads; i++ )
		{
			totalVolume += processors[i].getTotalVolume();
			sumReferenceRmsd += processors[i].getSumReferenceRmsd();
			if( processors[i].getMinReferenceRmsd() < minReferenceRmsd )
			{
				minReferenceRmsd = processors[i].getMinReferenceRmsd();
			}
			if( processors[i].getMaxReferenceRmsd() > maxReferenceRmsd )
			{
				maxReferenceRmsd = processors[i].getMaxReferenceRmsd();
			}
			sumNoeRmsd += processors[i].getSumNoeRmsd();
			if( processors[i].getMinNoeRmsd() < minNoeRmsd )
			{
				minNoeRmsd = processors[i].getMinNoeRmsd();
			}
			if( processors[i].getMaxNoeRmsd() > maxNoeRmsd )
			{
				maxNoeRmsd = processors[i].getMaxNoeRmsd();
			}
		}
		
		// get the number of poiints/cells processed
		int numProcessed = 0;
		switch( mode )
		{
			case Cells:
				numProcessed = iterCell.getNumServed();
			break;
			
			case Points:
				numProcessed = iterPoint.getNumServed();
			break;
		}
		
		// output the stats
		System.out.println( "Total Volume: " + totalVolume );
		if( m_analysisContext.getOligomer() != null )
		{
			System.out.println( "Min Reference RMSD: " + minReferenceRmsd );
			System.out.println( "Max Reference RMSD: " + maxReferenceRmsd );
			System.out.println( "Avg Reference RMSD: " + ( sumReferenceRmsd / (double)numProcessed ) );
		}
		System.out.println( "Min NOE RMSD: " + minNoeRmsd );
		System.out.println( "Max NOE RMSD: " + maxNoeRmsd );
		System.out.println( "Avg NOE RMSD: " + ( sumNoeRmsd / (double)numProcessed ) ); 
		
		return numProcessed;
	}
}
