package otherSupport;

import jafama.FastMath;

import java.text.DecimalFormat;


/** A little common status outputter, for classes that don't know if they're going to be taking a 
 * long time or not.
 * Construct with the maximum count of the loop and call .doStatus() on each iteration.
 * Nothing is outputted until the 'user attention span' limit is reached, at which point
 * the name will be outputted, followed by a % complete status every update period. 
 * At crude attempt at estimated time left is made. 
 */
public final class StatusOutput {
	
	private static DecimalFormat fmt;  
		
	private long initTime;
	private long n;
	private String firstText;
	private long lastTime;
	private boolean active;
	private int charsThisLine;
	private int maxCharsOnLine = 160;
	private double lastPC;
	
	private int attentionSpan, updatePeriod;
	
	private void defaultTiming(){
		attentionSpan =  Integer.parseInt(SettingsManager.defaultGlobal().getProperty("minerva.user.attentionSpanMilisecs", "5000"));
		updatePeriod = Integer.parseInt(SettingsManager.defaultGlobal().getProperty("minerva.user.updatePeriod", "1000"));
		fmt = new DecimalFormat("##.##");
	}
	
	public StatusOutput(Class caller, long n) {
		this.firstText = caller.getCanonicalName();
		this.n = n;
		this.initTime = System.currentTimeMillis();
		this.active = false;
		defaultTiming();
	}
	
	public StatusOutput(String firstText, long n) {
		this.firstText = firstText;
		this.n = n;
		this.initTime = System.currentTimeMillis();
		this.active = false;
		defaultTiming();
	}
	
	public StatusOutput(Class caller, long n, int attentionSpan, int updatePeriod) {
		this.firstText = caller.getCanonicalName();
		this.n = n;
		this.initTime = System.currentTimeMillis();
		this.active = false;
		this.attentionSpan = attentionSpan;
		this.updatePeriod = updatePeriod;
	}
		
	public StatusOutput(String firstText, long n, int attentionSpan, int updatePeriod) {
		this.firstText = firstText;
		this.n = n;
		this.initTime = System.currentTimeMillis();
		this.active = false;
		this.attentionSpan = attentionSpan;
		this.updatePeriod = updatePeriod;
	}
	
	/** @returns true if it actually outputs something */
	public final boolean doStatus(int i){
		long now = System.currentTimeMillis();
		if(!active && (now - initTime) > attentionSpan){
			//activate
			active = true;
			System.out.print(firstText + ": ");
			charsThisLine += firstText.length() + 2;
			lastTime = 0;
			lastPC = -1;
		}
		if(active && (now - lastTime) > updatePeriod){
			double nowPC =  i*100.0 / n;
			
			//make sure we're outputting with enough precision that it actually changes
			int reqDigits = -((int)FastMath.log10(lastPC - nowPC)) + 2;
			fmt.setMinimumFractionDigits(reqDigits);
			fmt.setMaximumFractionDigits(reqDigits);
			
			String outText = fmt.format(nowPC) + "% ";
			System.out.print(outText);
			charsThisLine += outText.length();
			if(charsThisLine > maxCharsOnLine){
				//estimate ETA
				long estTimeSecs = (n - i) * (now - initTime) / i / 1000;
				long estTimeMin = estTimeSecs / 60;
				estTimeSecs -= estTimeMin * 60;				
				outText = firstText + "[" + (estTimeMin > 0 ? estTimeMin + "m " : "") + estTimeSecs + "s]: ";
				
				charsThisLine += outText.length();
				System.out.print("\n" + outText);
				charsThisLine = 0;
			}
			lastTime = System.currentTimeMillis();
			lastPC = nowPC;
			
			return true;
		}
		return false;
	}

	public void done() {
		if(active)
			System.out.println(firstText + ": Done (" + (System.currentTimeMillis() - initTime)*1000 + "s)");
	}

}
