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

import edu.duke.donaldLab.jdshot.context.DistanceRestraintsContext;
import edu.duke.donaldLab.jdshot.context.SymmetryContext;
import edu.duke.donaldLab.jdshot.minimize.messages.PointRequest;
import edu.duke.donaldLab.jdshot.minimize.messages.PointResponse;
import edu.duke.donaldLab.jdshot.minimize.messages.ScoreReport;
import edu.duke.donaldLab.jdshot.minimize.messages.StructureRequest;
import edu.duke.donaldLab.jdshot.minimize.messages.StructureResponse;
import edu.duke.donaldLab.share.mpi.Client;
import edu.duke.donaldLab.share.mpi.MPIEndpoint;
import edu.duke.donaldLab.share.nmr.DistanceRestraint;
import edu.duke.donaldLab.share.nmr.DistanceRestraintReader;
import edu.duke.donaldLab.share.pdb.ProteinReader;
import edu.duke.donaldLab.share.protein.AtomAddressReadable;
import edu.duke.donaldLab.share.protein.Protein;
import edu.duke.donaldLab.share.pseudoatoms.PseudoatomBuilder;

public class MinimizerClient extends Client
{
	/**************************
	 *   Definitions
	 **************************/
	
	private enum Mode
	{
		Points
		{
			@Override
			public void run( MinimizerClient client )
			throws Exception
			{
				while( true )
				{
					// ask the server for a point
					MPIEndpoint.sendMessageToServer( new PointRequest() );
					PointResponse response = (PointResponse)MPIEndpoint.waitForMessage();
					
					// if we didn't get a point, we're done
					if( response.point == null )
					{
						break;
					}
					
					// process the point
					File outFile = new File( client.m_structuresDir, String.format( "structure.%06d.pdb", response.id ) );
					Protein oligomer = client.m_symmetryContext.getOligomer( response.point );
					ScoreReport scoreReport = client.m_minimizer.minimize( outFile, oligomer );
					scoreReport.id = response.id;
					MPIEndpoint.sendMessageToServer( scoreReport );
				}
			}
		},
		Structures
		{
			@Override
			public void run( MinimizerClient client )
			throws Exception
			{
				while( true )
				{
					// ask the server for a point
					MPIEndpoint.sendMessageToServer( new StructureRequest() );
					StructureResponse response = (StructureResponse)MPIEndpoint.waitForMessage();
					
					// if we didn't get a point, we're done
					if( response.id == -1 )
					{
						break;
					}
					
					// process the point
					File outFile = new File( client.m_structuresDir, String.format( "structure.%06d.pdb", response.id ) );
					Protein structure = new ProteinReader().read( client.m_structuresFile, (int)response.id );
					ScoreReport scoreReport = client.m_minimizer.minimize( outFile, structure );
					scoreReport.id = response.id;
					MPIEndpoint.sendMessageToServer( scoreReport );
				}
			}
		};

		public abstract void run( MinimizerClient client ) throws Exception;
	}
	
	
	/**************************
	 *   Data Members
	 **************************/
	
	private File m_workDir;
	private File m_structuresDir;
	private SymmetryContext m_symmetryContext;
	private File m_structuresFile;
	private EvaluatingMinimizer m_minimizer;
	private Mode m_mode;
	
	
	/**************************
	 *   Constructors
	 **************************/
	
	public MinimizerClient( File workDir, File structuresDir, SymmetryContext symmetryContext, DistanceRestraintsContext noesContext )
	throws IOException
	{
		// save parameters
		m_workDir = new File( workDir, MPIEndpoint.getNodeName() );
		m_structuresDir = structuresDir;
		m_symmetryContext = symmetryContext;
		m_structuresFile = null;
		
		// prep the minimizer
		m_minimizer = new EvaluatingMinimizer( m_workDir, m_symmetryContext, noesContext );
		m_mode = Mode.Points;
	}
	
	public MinimizerClient( File workDir, File structuresDir, File structuresFile, File distanceRestraintsFile )
	throws IOException
	{
		// save parameters
		m_workDir = new File( workDir, MPIEndpoint.getNodeName() );
		m_structuresDir = structuresDir;
		m_symmetryContext = null;
		m_structuresFile = structuresFile;
		
		// read the restraints and prep the minimizer
		List<DistanceRestraint<AtomAddressReadable>> distanceRestraints = new DistanceRestraintReader().read( distanceRestraintsFile );
		PseudoatomBuilder.getInstance().buildDistanceRestraints( new ProteinReader().read( structuresFile, 0 ), distanceRestraints );
		m_minimizer = new EvaluatingMinimizer( m_workDir, distanceRestraints );
		m_mode = Mode.Structures;
	}
	
	
	/**************************
	 *   Methods
	 **************************/
	
	public void run( )
	throws Exception
	{
		MPIEndpoint.log( "client started!" );
		makeWorkDirectory();
		
		m_mode.run( this );
		
		cleanupWorkDirectory();
		MPIEndpoint.log( "client complete!" );
	}
	
	
	/**************************
	 *   Functions
	 **************************/
	
	private void makeWorkDirectory( )
	throws IOException
	{
		// make sure the folder exists
		m_workDir.mkdirs();
	}
	
	private void cleanupWorkDirectory( )
	throws IOException
	{
		// we can only delete empty directories
		nukeWorkDirectory();
		
		m_workDir.delete();
	}
	
	private void nukeWorkDirectory( )
	throws IOException
	{
		// empty the directory of any files
		String[] files = m_workDir.list();
		for( int i=0; i<files.length; i++ )
		{
			new File( m_workDir, files[i] ).delete();
		}
	}
}
