package com.eactive.eai.rms.common.logging;

import java.text.NumberFormat;
import java.util.LinkedHashMap;
import java.util.Map;

import org.apache.commons.lang3.time.StopWatch;

import lombok.Getter;
import lombok.Synchronized;

public class RequestReportStopWatch {
	private static final long NANO_2_MILLIS = 1000000L;
	@Getter
	private String id;
	@Getter
	private Map<String, TaskInfo> taskInfoMap = new LinkedHashMap<>();

	private long startTimeNanos;
	private long totalTimeNanos;
	private boolean started;

	public RequestReportStopWatch(String id) {
		this.id = id;
	}

	public Object getTime() {
		return (System.nanoTime() - startTimeNanos) / NANO_2_MILLIS;
	}

	public void start() {
		startTimeNanos = System.nanoTime();
		started = true;
	}

	public void stop() {
		long lastTime = System.nanoTime() - this.startTimeNanos;
		this.totalTimeNanos += lastTime;
	}

	@Synchronized
	public void startTask(String taskName) {
		if (!started) {
			throw new IllegalStateException("start() 를 먼저 실행 하세요.");
		}

		TaskInfo taskInfo = taskInfoMap.get(taskName);
		if (taskInfo == null) {
			taskInfo = new TaskInfo(taskName);
			taskInfoMap.put(taskName, taskInfo);
			taskInfo.getStopWatch().start();
		} else {
			if (taskInfo.getStopWatch().isSuspended()) {
				taskInfo.getStopWatch().resume();
			}
		}

		taskInfo.startCount = taskInfo.startCount + 1;
	}

	@Synchronized
	public void stopTask(TaskInfo taskInfo) {
		taskInfo.stopCount = taskInfo.stopCount + 1;

		if (taskInfo.startCount == taskInfo.stopCount && !taskInfo.stopWatch.isSuspended()) {
			taskInfo.stopWatch.suspend();
		}
	}

	public String print() {
		long totalTimeMillis = totalTimeNanos / NANO_2_MILLIS;
		StringBuilder sb = new StringBuilder();
		sb
				.append("RequestReportStopWatch '")
				.append(getId())
				.append("': running time = ")
				.append(totalTimeNanos)
				.append("(")
				.append(totalTimeMillis)
				.append(" ms)");
		sb.append('\n');
		sb.append("---------------------------------------------\n");
		sb.append("ms         %     c    Task name\n");
		sb.append("---------------------------------------------\n");
		NumberFormat nf = NumberFormat.getNumberInstance();
		nf.setMinimumIntegerDigits(9);
		nf.setGroupingUsed(false);

		NumberFormat pf = NumberFormat.getPercentInstance();
		pf.setMinimumIntegerDigits(3);
		pf.setGroupingUsed(false);

		NumberFormat cf = NumberFormat.getNumberInstance();
		cf.setMinimumIntegerDigits(3);
		cf.setGroupingUsed(false);

		for (TaskInfo task : taskInfoMap.values()) {
			sb.append(nf.format(task.stopWatch.getTime())).append("  ");
			sb.append(pf.format((double) task.stopWatch.getTime() / totalTimeMillis)).append("  ");
			sb.append(cf.format(task.startCount)).append("  ");
			sb.append(task.getStopWatch().getMessage()).append("\n");
		}

		return sb.toString();
	}

	@Getter
	public static class TaskInfo {
		private StopWatch stopWatch;
		private int startCount;
		private int stopCount;

		public TaskInfo(String taskName) {
			stopWatch = new StopWatch(taskName);
		}
	}

}
