package railo.runtime.engine;

import java.util.Collections;
import java.util.HashMap;
import java.util.Map;

import railo.commons.lang.StringUtil;
import railo.runtime.PageContext;
import railo.runtime.op.Caster;

public abstract class ExecutionLogSupport implements ExecutionLog {
	
	protected static final short UNIT_NANO=1;
	protected static final short UNIT_MICRO=2;
	protected static final short UNIT_MILLI=4;
	protected static final short UNIT_UNDEFINED=0;
	
	private Map<String,Pair> map=Collections.synchronizedMap(new HashMap<String,Pair>());
	protected long min=Long.MIN_VALUE;
	protected short unit=UNIT_UNDEFINED;
	
	public void init(PageContext pc,Map<String,String> arguments) {
		// min
		if(min==Long.MIN_VALUE) {
			min=toNanos(arguments.get("min-time"),0);
		}
		// unit
		if(UNIT_UNDEFINED==unit){
			unit=UNIT_NANO;
			// unit
			String _unit=arguments.get("unit");
			if(_unit!=null) {
				_unit=_unit.trim();
				if(_unit.equalsIgnoreCase("micro"))		unit=UNIT_MICRO;
				else if(_unit.equalsIgnoreCase("µs"))	unit=UNIT_MICRO;
				else if(_unit.equalsIgnoreCase("milli"))unit=UNIT_MILLI;
				else if(_unit.equalsIgnoreCase("ms"))	unit=UNIT_MILLI;
			}
		}
		_init(pc,arguments);
	}

	private static long toNanos(String str, int defaultValue) {
		if(StringUtil.isEmpty(str)) return defaultValue;
		str=str.trim().toLowerCase();
		long l = Caster.toLongValue(str,Long.MIN_VALUE);
		if(l!=Long.MIN_VALUE) return l;
		
		if(str.endsWith("ns")) {
			String sub=str.substring(0,str.length()-2);
			l = Caster.toLongValue(sub.trim(),Long.MIN_VALUE);
			if(l!=Long.MIN_VALUE) return l;
		}
		else if(str.endsWith("µs")) {
			String sub=str.substring(0,str.length()-2);
			double d = Caster.toDoubleValue(sub.trim(),Double.NaN);
			if(!Double.isNaN(d)) return (long)(d*1000);
		}
		else if(str.endsWith("ms")) {
			String sub=str.substring(0,str.length()-2);
			double d = Caster.toDoubleValue(sub.trim(),Double.NaN);
			if(!Double.isNaN(d)) return (long)(d*1000*1000);
		}
		else if(str.endsWith("s")) {
			String sub=str.substring(0,str.length()-1);
			double d = Caster.toDoubleValue(sub.trim(),Double.NaN);
			if(!Double.isNaN(d)) return (long)(d*1000*1000);
		}
		
		return defaultValue;
	}
	
	/*public static void main(String[] args) {
		print.e(toNanos("123456"));
		print.e(toNanos("123456ns"));
		print.e(toNanos("123.456µs"));
		print.e(toNanos("0.123456 ms"));
		print.e(toNanos("123456 ns"));
	}*/
	

	public final void release() {
		map.clear();
		_release();
	}
	
	public final void start(int pos,String id) {
		long current = System.nanoTime();
		map.put(id,new Pair(current,pos));
	}

	public final void end(int pos,String id) {
		long current = System.nanoTime();
		Pair pair=map.remove(id);
		if(pair!=null) {
			if((current-pair.time)>=min)
				_log(pair.pos,pos,pair.time,current);
		}
	}
	protected abstract void _init(PageContext pc, Map<String, String> arguments);
	protected abstract void _log(int startPos, int endPos, long startTime, long endTime);
	protected abstract void _release();


	protected String timeLongToString(long current) {
		if(unit==UNIT_MICRO) return (current/1000L)+" µs";
		if(unit==UNIT_MILLI) return (current/1000000L)+" ms";
		return current+" ns";
	}
	protected static String unitShortToString(short unit) {
		if(unit==UNIT_MICRO) return "µs";
		if(unit==UNIT_MILLI) return "ms";
		return "ns";
	}
	
	private final static class Pair {
		private final long time;
		private final int pos;
		public Pair(long time, int pos) {
			this.time=time;
			this.pos=pos;
		}
	}
}
