/*******************************************************************************
 * 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.FileWriter;
import java.io.IOException;

import mpi.MPIException;

import edu.duke.donaldLab.jdshot.grid.PointIterator;
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.ClientState;
import edu.duke.donaldLab.share.mpi.MPIEndpoint;
import edu.duke.donaldLab.share.mpi.Server;
import edu.duke.donaldLab.share.pdb.ProteinReader;
import edu.duke.donaldLab.share.perf.Progress;

public class MinimizerServer extends Server
{
	/**************************
	 *   Definitions
	 **************************/
	
	private enum Mode
	{
		Points
		{
			@Override
			public long getNumIds( MinimizerServer server )
			{
				return server.m_iterPoint.getNumPoints();
			}
			
			@Override
			public boolean hasNext( MinimizerServer server )
			{
				return server.m_iterPoint.hasNext();
			}
		},
		Structures
		{
			@Override
			public long getNumIds( MinimizerServer server )
			{
				try
				{
					return new ProteinReader().readNumProteins( server.m_structuresFile );
				}
				catch( IOException ex )
				{
					ex.printStackTrace();
				}
				return -1;
			}
			
			@Override
			public boolean hasNext( MinimizerServer server )
			{
				return server.m_nextId < server.m_progress.getTotalWork();
			}
		};
		
		public abstract long getNumIds( MinimizerServer server );
		public abstract boolean hasNext( MinimizerServer minimizerServer );
	}
	
	
	/**************************
	 *   Data Members
	 **************************/
	
	private PointIterator m_iterPoint;
	private File m_structuresFile;
	private File m_scoresFile;
	private Progress m_progress;
	private int m_nextId;
	private FileWriter m_scoreWriter;
	private Mode m_mode;
	private boolean m_isAborting;
	
	
	/**************************
	 *   Constructors
	 **************************/
	
	public MinimizerServer( PointIterator iterPoint, File scoresFile )
	{
		// save parameters
		m_iterPoint = iterPoint;
		m_structuresFile = null;
		m_scoresFile = scoresFile;
		
		// init defaults
		m_progress = null;
		m_nextId = 0;
		m_mode = Mode.Points;
		m_isAborting = false;
	}
	
	public MinimizerServer( File structuresFile, File scoresFile )
	{
		// save parameters
		m_iterPoint = null;
		m_structuresFile = structuresFile;
		m_scoresFile = scoresFile;
		
		// init defaults
		m_progress = null;
		m_nextId = 0;
		m_mode = Mode.Structures;
	}
	
	
	/**************************
	 *   Events
	 **************************/
	
	public void handleMessage( PointRequest request )
	throws MPIException
	{
		// get the next point if applicable
		if( !m_isAborting && m_iterPoint != null && m_iterPoint.hasNext() )
		{
			PointResponse response = new PointResponse();
			response.point = m_iterPoint.next();
			response.id = m_nextId++;
			
			MPIEndpoint.sendMessage( response, request.getSourceRank() );
		}
		else
		{
			// otherwise, tell the client to shut down
			MPIEndpoint.sendMessage( new PointResponse(), request.getSourceRank() );
			setClientState( request.getSourceRank(), ClientState.Complete );
		}
	}
	
	public void handleMessage( StructureRequest request )
	throws MPIException
	{
		// get the next structure if applicable
		if( !m_isAborting && m_mode.hasNext( this ) )
		{
			StructureResponse response = new StructureResponse();
			response.id = m_nextId++;
			
			MPIEndpoint.sendMessage( response, request.getSourceRank() );
		}
		else
		{
			// otherwise, tell the client to shut down
			MPIEndpoint.sendMessage( new StructureResponse(), request.getSourceRank() );
			setClientState( request.getSourceRank(), ClientState.Complete );
		}
	}
	
	public void handleMessage( ScoreReport report )
	{
		// write the scores
		try
		{
			m_scoreWriter.write( String.format(
				"%d\t%.3f\t%.3f\t%d\t%.3f\t%.3f\t%d\n",
				report.id,
				report.before.vdwEnergy,
				report.before.restraintRmsd,
				report.before.numSatisfied,
				report.after.vdwEnergy,
				report.after.restraintRmsd,
				report.after.numSatisfied
			) );
		}
		catch( IOException ex )
		{
			MPIEndpoint.reportException( ex );
			abort();
		}
		
		m_progress.incrementProgress();
	}
	
	
	/**************************
	 *   Functions
	 **************************/
	
	@Override
	protected void init( )
	throws Exception
	{
		m_scoreWriter = new FileWriter( m_scoresFile );
		
		// setup the progress bar
		long numIds = m_mode.getNumIds( this );
		MPIEndpoint.log( "waiting for " + numIds + " " + m_mode + "..." );
		m_progress = new Progress( numIds, 30000 );
		m_progress.setMessageListener( this );
	}
	
	@Override
	protected void shutdown( )
	throws Exception
	{
		m_scoreWriter.close();
		MPIEndpoint.log( "Processed " + m_progress.getNumWorkDone() + " " + m_mode + "." );
	}
	
	private void abort( )
	{
		MPIEndpoint.log( "Aborting..." );
		m_isAborting = true;
	}
}
