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

import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;


import edu.duke.donaldLab.jdshot.context.DistanceRestraintsContext;
import edu.duke.donaldLab.jdshot.context.SymmetryContext;
import edu.duke.donaldLab.jdshot.minimize.messages.ScoreReport;
import edu.duke.donaldLab.share.analysis.RestraintCalculator;
import edu.duke.donaldLab.share.io.FileUtil;
import edu.duke.donaldLab.share.mapping.NameMapper;
import edu.duke.donaldLab.share.mapping.NameScheme;
import edu.duke.donaldLab.share.nmr.DistanceRestraint;
import edu.duke.donaldLab.share.nmr.DistanceRestraintMapper;
import edu.duke.donaldLab.share.pdb.ProteinReader;
import edu.duke.donaldLab.share.pdb.ProteinWriter;
import edu.duke.donaldLab.share.protein.AtomAddressInternal;
import edu.duke.donaldLab.share.protein.AtomAddressReadable;
import edu.duke.donaldLab.share.protein.Protein;
import edu.duke.donaldLab.share.pseudoatoms.PseudoatomBuilder;
import edu.duke.donaldLab.share.xplor.EnergyCalculator;
import edu.duke.donaldLab.share.xplor.StructureMinimizer;
import edu.duke.donaldLab.share.xplor.XplorException;

public class EvaluatingMinimizer
{
	/**************************
	 *   Definitions
	 **************************/
	
	private static final String LocalOutPdb = "out.pdb";
	

	/**************************
	 *   Data Members
	 **************************/
	
	private File m_workDir;
	private EnergyCalculator m_energyCalculator;
	private RestraintCalculator m_restraintCalculator;
	private ProteinReader m_pdbReader;
	private ProteinWriter m_pdbWriter;
	private StructureMinimizer m_minimizer;
	private List<DistanceRestraint<AtomAddressReadable>> m_distanceRestraints;
	
	
	/**************************
	 *   Constructors
	 **************************/
	
	public EvaluatingMinimizer( File workDir, SymmetryContext symmetryContext, DistanceRestraintsContext distanceRestraintsContext )
	{
		this(
			workDir,
			DistanceRestraintMapper.mapInternalToReadable(
				symmetryContext.getSymmetry().newNoeReplicator().replicate(
					distanceRestraintsContext.getInternalRestraints(),
					symmetryContext.getNumSubunits()
				),
				symmetryContext.getClonedMonomer()
			)
		);
	}
	
	public EvaluatingMinimizer( File workDir, List<DistanceRestraint<AtomAddressReadable>> distanceRestraints )
	{
		// save parameters
		m_workDir = workDir;
		m_distanceRestraints = distanceRestraints;
		
		// cache some instances
		m_energyCalculator = new EnergyCalculator( m_workDir );
		m_restraintCalculator = new RestraintCalculator();
		m_pdbReader = new ProteinReader();
		m_pdbWriter = new ProteinWriter();
		
		// init the minimizer
		m_minimizer = new StructureMinimizer( m_workDir );
		m_minimizer.setDistanceRestraints( m_distanceRestraints );
	}
	
	
	/**************************
	 *   Methods
	 **************************/
	
	public void setNumSteps( int numSteps )
	{
		m_minimizer.setNumSteps( numSteps );
	}
	
	public ScoreReport minimize( String outPdbPath, Protein protein )
	throws IOException, XplorException
	{
		return minimize( new File( outPdbPath ), protein );
	}
	
	public ScoreReport minimize( File outPdbFile, Protein protein )
	throws IOException, XplorException
	{
		// compute before scores
		ScoreReport scoreReport = new ScoreReport();
		scoreReport.before.vdwEnergy = m_energyCalculator.getEnergy( protein );
		evaluate( scoreReport.before, protein );
		
		// minimize
		File localOutPdbFile = new File( m_workDir, LocalOutPdb );
		m_minimizer.minimize( localOutPdbFile, protein );
		
		// caluclate the after scores
		if( localOutPdbFile.exists() )
		{
			Protein minimizedProtein = m_pdbReader.read( localOutPdbFile );
			
			// map the names back to the new format
			NameMapper.ensureProtein( minimizedProtein, NameScheme.New );
			m_pdbWriter.write( minimizedProtein, localOutPdbFile );
			
			evaluate( scoreReport.after, minimizedProtein );
		}
		
		// copy the local out file to the final dir
		FileUtil.copyFile( localOutPdbFile, outPdbFile );
		
		return scoreReport;
	}
	
	
	/**************************
	 *   Functions
	 **************************/
	
	private void evaluate( ScoreReport.Sample scores, Protein protein )
	throws IOException, XplorException
	{
		// compute energy score
		scores.vdwEnergy = m_energyCalculator.getEnergy( protein );
		
		// copy the protein so we can add pseudoatoms
		Protein pseudoProtein = new Protein( protein );
		PseudoatomBuilder.getInstance().build( pseudoProtein );
		
		// compute satisfaction scores
		ArrayList<DistanceRestraint<AtomAddressInternal>> internalRestraints = DistanceRestraintMapper.mapReadableToInternal( m_distanceRestraints, pseudoProtein );
		scores.restraintRmsd = m_restraintCalculator.getRmsd( pseudoProtein, internalRestraints );
		scores.numSatisfied = m_restraintCalculator.getNumSatisfied( pseudoProtein, internalRestraints );
	}
}
