/*
Copyright (C) 2011  Diego Darriba, David Posada

This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 3 of the License, or
(at your option) any later version.

This program 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 General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
*/
package es.uvigo.darwin.jmodeltest.observer;

import java.io.BufferedOutputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectOutput;
import java.io.ObjectOutputStream;
import java.io.OutputStream;
import java.util.Locale;
import java.util.Observable;
import java.util.Observer;

import es.uvigo.darwin.jmodeltest.ApplicationOptions;
import es.uvigo.darwin.jmodeltest.ModelTest;
import es.uvigo.darwin.jmodeltest.ModelTestConfiguration;
import es.uvigo.darwin.jmodeltest.io.TextOutputStream;
import es.uvigo.darwin.jmodeltest.model.Model;
import es.uvigo.darwin.jmodeltest.utilities.Utilities;

public class ConsoleProgressObserver implements Observer {

	private TextOutputStream stream;
	/** Timer for calculate the elapsed time **/
	private long startTime;
	private int totalModels;
	private int completedModels = 0;
	private boolean threadScheduling;
	private ApplicationOptions options;

	public ConsoleProgressObserver(ApplicationOptions options) {
		this.startTime = System.currentTimeMillis();
		this.stream = ModelTest.getMainConsole();
		this.threadScheduling = options.threadScheduling;
		this.options=options;
	}

	@Override
	@SuppressWarnings("fallthrough")
	public synchronized void update(Observable o, Object arg) {

		if (arg != null) {
			ProgressInfo info = (ProgressInfo) arg;

			switch (info.getType()) {

			case ProgressInfo.BASE_TREE_INIT:
				stream.println(" ");
				stream.println("Estimating a BIONJ-JC tree ... ");
				break;

			case ProgressInfo.BASE_TREE_COMPUTED:
				stream.println("OK");
				stream.println(info.getModel().getName() + " tree: "
						+ info.getModel().getTreeString());
				break;

			case ProgressInfo.OPTIMIZATION_INIT:
				this.totalModels = options.getNumModels();
				stream.println(" ");stream.println(" ");
				stream.println("::Progress::");
				stream.println(" ");
				stream.println("Model \t\t Exec. Time \t Total Time \t -lnL");
				stream.println("-------------------------------------------------------------------------");
				break;
			
			case ProgressInfo.GTR_OPTIMIZATION_INIT:
				stream.println("[Heuristic search] Optimizing " + info.getModel().getName() + " model");
				break;
				
			case ProgressInfo.GTR_OPTIMIZATION_COMPLETED:
				stream.println("[Heuristic search] Starting model filtering");
				break;
				
			case ProgressInfo.SINGLE_OPTIMIZATION_INIT:
				break;

			case ProgressInfo.SINGLE_OPTIMIZATION_COMPLETED:
				completedModels++;
				stream.print(info.getModel().getName() + "\t");
				double modelLnL;
				if (info.getValue() == ProgressInfo.VALUE_REGULAR_OPTIMIZATION) {
					modelLnL = info.getModel().getLnL(); 
				} else {
					modelLnL = info.getModel().getLnLIgnoringGaps();
				}
				if (info.getModel().getName().length()<8)
					stream.print("\t");
				stream.print(info.getMessage() + "\t" 
						+ Utilities.calculateRuntime(startTime, System.currentTimeMillis()) + "\t" 
						+ String.format(Locale.ENGLISH, "%13.4f", modelLnL));
				if (ModelTest.MPJ_RUN && threadScheduling) {
					stream.println(" ");
				} else {
					if (info.isHeuristicSearch() && info.getValue() == ProgressInfo.VALUE_REGULAR_OPTIMIZATION) {
						stream.println("\t ["+info.getHeuristicStage()+"/6] (" + completedModels + "/" + info.getNumModelsInStage() + ")");
						if (completedModels == info.getNumModelsInStage()) {
							completedModels = 0;
						}
					} else {
						stream.println("\t (" + completedModels + "/" + totalModels + ")");
					}
				}
				
				if (ModelTestConfiguration.isCkpEnabled()) {
					try {
						OutputStream file = new FileOutputStream(
								options.getCkpFile());
						OutputStream buffer = new BufferedOutputStream(file);
						ObjectOutput output = new ObjectOutputStream(buffer);
						output.writeObject(ModelTest.getCandidateModels());
						output.close();
					} catch (IOException ex) {
						System.err.println("Cannot perform output.");
					}
				}
				
				break;
				
			case ProgressInfo.REOPTIMIZATION_INIT:
				this.totalModels = info.getValue();
				this.completedModels = 0;
				this.startTime = System.currentTimeMillis();
				stream.println(" ");stream.println(" ");
				stream.println("Some models should be reoptimized for checking lnL against the unconstrained likelihood");
				stream.println(" ");
				stream.println("Model \t\t Exec. Time \t Total Time\t-lnL w/o gaps");
				stream.println("-------------------------------------------------------------------------");
				break;
				
			case ProgressInfo.REOPTIMIZATION_COMPLETED:

				stream.println(" ");

				stream.println("  Unconstrained -lnL       = " + options.getUnconstrainedLnL());
				stream.println("  Number of patterns found = " + options.getNumPatterns());

				stream.println(" ");
				break;
				
			case ProgressInfo.INTERRUPTED:
				stream.println(" ");
				stream.println("Computation of likelihood scores discontinued ...");
				break;

			case ProgressInfo.ERROR:
				stream.println(info.getMessage());
				stream.println(" ");
				stream.println("Computation of likelihood scores discontinued ...");
				System.exit(-1);
				break;
			case ProgressInfo.ERROR_BINARY_NOEXISTS:
				stream.println("");
				stream.println("ERROR: PhyML binary does not exists: " + info.getMessage());
				stream.println("");
				ModelTest.finalize(ProgressInfo.ERROR_BINARY_NOEXISTS);
				break;
			case ProgressInfo.ERROR_BINARY_NOEXECUTE:
				stream.println("");
				stream.println("ERROR: PhyML binary does not have execution permission: " + info.getMessage());
				stream.println("");
				ModelTest.finalize(ProgressInfo.ERROR_BINARY_NOEXECUTE);
				break;
			case ProgressInfo.OPTIMIZATION_COMPLETED_OK:

				stream.println(" ");
				for (Model model : ModelTest.getCandidateModels()) {
					model.print(ModelTest.getMainConsole());
					ModelTest.getMainConsole().println(" ");
				}

				if (options.isAmbiguous()) {
				stream.println("  Best-fit models should be reoptimized for comparison with unconstrained likelihood");
				} else {
					stream.println("  Unconstrained -lnL       = " + options.getUnconstrainedLnL());
					stream.println("  Number of patterns found = " + options.getNumPatterns());
				}
				stream.println(" ");
				stream.println("Computation of likelihood scores completed. It took "
						+ Utilities.calculateRuntime(startTime,
								System.currentTimeMillis()) + ".");
				stream.println(" ");
				// continue

			case ProgressInfo.OPTIMIZATION_COMPLETED_INTERRUPTED:
				// dispose
				break;
			}
		}

	}

}
