/*  Monte-Carlo simulation code for statistical physics
    Copyright (C) 2001-2005  Sylvain Reynal
    
    Département de Physique
    Ecole Nationale Supérieure de l'Electronique et de ses Applications (ENSEA)
    6, avenue du Ponceau,    
    F-95014 CERGY CEDEX

    et
    
    Laboratoire de Physique Théorique et Modélisation (LPTM)
    Université de Cergy-Pontoise - Site de Neuville
    F-95031 CERGY CEDEX
    
	
    Tel : 00 +33 130 736 245
    Fax : 00 +33 130 736 667
    e-mail : reynal@ensea.fr
    web page : http://www.ensea.fr/staff/reynal/
*/

package fr.ensea.montecarlo.canonical;

import java.util.*;
import java.awt.*; import javax.swing.*;
import fr.ensea.Localizer;
import fr.ensea.montecarlo.*;
import fr.ensea.montecarlo.event.*;

/**
 * This class manages the (canonical) algorithm thread (since the algorithm and the UI are executed 
 * in separate threads). In particular, this class allows one to configure either a temperature sweep or
 * a single-temperature simulation, to pause/stop/restart the simulation, to set the "sleep" delay between
 * MC steps, and arranges to repaint the UI asynchronously at appropriate times. 
 * Any particular algorithm (e.g. Metropolis, Wolff, ...) may be plugged into it.
 */
public class MCSimulationThread extends AbstractSimulationThread {

	private Montecarlo algorithm;
	private double[] kTList;
	private final Montecarlo metropolis, wolffcluster;
	private int kTidx; // current index in kTList
	
	public final static String METROPOLIS="metropolis";
	public final static String WOLFF="wolff";

	public MCSimulationThread(MCApplet controller, double defKT){
		super(controller);
		this.metropolis = controller.getLattice().createMetropolis(controller.getSamplesBag(), defKT);// par défaut
		this.wolffcluster = controller.getLattice().createWolffCluster(controller.getSamplesBag(), defKT);// may be null
		this.algorithm = metropolis;
		setSingleTemperature(defKT);
	}

	public MCSimulationThread(MCApplet controller, double kTstart, double kTend, int kTsteps){
		super(controller);
		this.metropolis = controller.getLattice().createMetropolis(controller.getSamplesBag(), kTstart);// par défaut
		this.wolffcluster = controller.getLattice().createWolffCluster(controller.getSamplesBag(), kTstart);// may be null
		this.algorithm = metropolis;
		setTemperatureRange(kTstart, kTend, kTsteps);
	}

	
	public String getCountersRateJLabel(){
		return algorithm.getCountersRateJLabel();
	}

	public void reinitCounters(){
		algorithm.reinitCounters();
	}

	/**
	 * Plugs a new algorithm into this class.
	 * @param algorithm METROPOLIS or WOLFF
	 * @return true iff the algorithm was successfully plugged
	 */
	public boolean setAlgorithm(String algo){
		boolean wasRunning  = isRunning();
		if (algo==WOLFF){
			if (this.algorithm==wolffcluster) return false;
			if (wolffcluster==null) return false;
			kill();
			this.algorithm = wolffcluster;
		}
		else if (algo==METROPOLIS){
			if (this.algorithm==metropolis) return false;
			if (metropolis==null) return false;
			kill();
			this.algorithm = metropolis;
		}
		else return false;

		if (wasRunning){ // restart automatically
			((MCApplet)controller).getThermalAveragesSet().addNewAveragesSet();
			//new Thread(this).start();
		}
		return true;
	}

	public Montecarlo getAlgorithm(){
		return algorithm;
	}

	public void activateRG(){
		if (metropolis!=null && metropolis instanceof PottsMetropolis)  ((PottsMetropolis)metropolis).activateRG();
		if (wolffcluster!=null && wolffcluster instanceof PottsWolffCluster)  ((PottsWolffCluster)wolffcluster).activateRG();
	}
	
	public void setFactorRG(int rgFactor){
		if (metropolis!=null && metropolis instanceof PottsMetropolis)   ((PottsMetropolis)metropolis).setFactorRG(rgFactor);
		if (wolffcluster!=null && wolffcluster instanceof PottsWolffCluster)   ((PottsWolffCluster)wolffcluster).setFactorRG(rgFactor);
	}
		
		
	/**
	 * Configures a temperature sweep.
	 */
	public void setTemperatureRange(double kTstart, double kTend, int kTsteps){
		if (kTsteps<2) kTsteps=2;
		kTList = new double[kTsteps];
		double deltakT = (kTend-kTstart)/(kTsteps-1);
		for (int i=0; i<kTsteps; i++){
			kTList[i] = kTstart + i * deltakT;
		}
		//System.out.println(this);
		if (alive){
			if (paused) kill(); // if we don't kill it, and kTList.length has changed, the "for" loop in the run() method will sure get into the mess
			else start();
		}
	}

	/**
	 * Switch the algorithm to single-temperature mode.
	 */
	public void setSingleTemperature(double kT){
		kTList = new double[1];
		kTList[0]=kT;
		if (alive){
			if (paused) kill();
			else start();
		}
	}

	public double getSingleTemperature(){
		return kTList[0];
	}

	public double getKTStart(){
		return kTList[0];
	}

	public double getKTEnd(){
		return kTList[kTList.length-1];
	}

	public int getKTSteps(){
		return kTList.length;
	}

	public boolean isTemperatureSweep(){
		return (kTList.length > 1);
	}

	public double[] getKTList(){
		return kTList;
	}

	public String toString(){
		String s = "MCSimulationThread:\n";
		s += "kTList:\n";
		for (int i=0; i<kTList.length; i++){
			s+= kTList[i]+",";
		}
		s+="\n";
		return s;
	}

	protected boolean configureNextRun(){
		if (kTidx >= kTList.length) {
			kTidx=0;
			return false;
		}
		algorithm.setTemperature(kTList[kTidx]);
		controller.getSamplesBag().resetSamplesGraphs();
		((MCApplet)controller).getControlPane().setRunName("T="+Double.toString(kTList[kTidx]));
		return true;
	}
	
	protected void sweep(){
		algorithm.sweep();
	}

	protected boolean finalizeRun(){
		if (controller.getSamplesBag().size() < nbMCSteps) return false;
		if (isTemperatureSweep()) {
			((MCApplet)controller).getThermalAveragesSet().storeSimulationResults(algorithm.getTemperature()); // if in temp-sweep mode only (otherwise it's to be done manually)
			controller.showStatus(Localizer.get("NewPointAdded")+"=" + algorithm.getTemperature());
		}
		kTidx++;
		return true;
	}
	

	// --------------- UI -----------------
	public JComboBox createAlgoChoice(){
		JComboBox algoChooserCH=new JComboBox();
		if (metropolis != null)  algoChooserCH.addItem("Metropolis");
		if (wolffcluster != null)  algoChooserCH.addItem("Wolff cluster");
		if (algorithm==metropolis) algoChooserCH.setSelectedIndex(0);
		else algoChooserCH.setSelectedIndex(1);
		return algoChooserCH;
	}
	
}

