package org.ntlab.traceanalyzer;

import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;

import org.ntlab.trace.ClassInfo;
import org.ntlab.trace.MethodExecution;
import org.ntlab.trace.ThreadInstance;
import org.ntlab.trace.Trace;
import org.ntlab.trace.TraceJSON;

public class MarkedTrace {
	private Trace trace;
	private long markStart = -1L;
	private long markEnd = -1L;
	private ArrayList<MarkedThread> markedThreads = null;
	private boolean bHighlightUniqueMethods = false;
	/**
	 * マーク外で実行された全メソッド実行
	 */
	private HashSet<String> unmarkedMethodSignatures = null;
	
	public MarkedTrace(String traceFilePath) {
		this.trace = new TraceJSON(traceFilePath);
		markFull();
	}

	public void setTrace(Trace trace) {
		this.trace = trace;
		markFull();
	}
	
	public ClassInfo getClassInfo(String className) {
		if (trace instanceof TraceJSON) {
			return ((TraceJSON)trace).getClassInfo(className);
		}
		return null;
	}

	public void markFull() {
		Collection<ThreadInstance> threads = trace.getAllThreads().values();
		long start = -1L;
		long end = -1L;
		for (ThreadInstance thread: threads) {
			ArrayList<MethodExecution> roots = thread.getRoot();
			for (MethodExecution m: roots) {
				if (start == -1L || m.getEntryTime() < start) {
					start = m.getEntryTime();
				}
				if (end == -1L || m.getExitTime() > end) {
					end = m.getExitTime();		// JSONトレースのみ対応
				}
			}
		}
		if (end < start) {
			end = start + 1;
		}
		mark(start, end);
	}
	
	public void mark(long start, long end) {
		this.markStart = start;
		this.markEnd = end;
		updateMarkedThreads();
	}

	public long getMarkStart() {
		return markStart;
	}

	public long getMarkEnd() {
		return markEnd;
	}

	public ArrayList<MarkedThread> getMarkedThreads() {
		if (markedThreads == null) {
			updateMarkedThreads();
		}
		return markedThreads;
	}

	private void updateMarkedThreads() {
		MarkedMethodExecutionVisitor visitor = new MarkedMethodExecutionVisitor();
		trace.traverseMarkedMethodExecutions(visitor, markStart, markEnd);
		markedThreads = visitor.getMarkedThreads();
	}

	/**
	 * マーク固有のメソッドを強調する
	 * @param bHighlightUniqueMethods マーク固有のメソッドを強調するか?
	 */
	public void setHighlightUniqueMethods(boolean bHighlightUniqueMethods) {
		this.bHighlightUniqueMethods = bHighlightUniqueMethods;
		if (bHighlightUniqueMethods) {
			if (unmarkedMethodSignatures == null) unmarkedMethodSignatures = trace.getUnmarkedMethodSignatures(markStart, markEnd);
			for (int i = 0; i < markedThreads.size(); i++) {
				markedThreads.get(i).searchUniqueMethodInvocation(unmarkedMethodSignatures);
			}
		}
	}

	/**
	 * マーク固有のメソッドを強調しているか?
	 * @return
	 */
	public boolean isbHighlightUniqueMethods() {
		return bHighlightUniqueMethods;
	}
	
	/**
	 * マーク固有のメソッド実行をすべて取得する
	 * @return　マーク固有の全メソッド実行
	 */
	public ArrayList<MarkedMethodExecution> getUniqueMethodExecutions() {
		ArrayList<MarkedMethodExecution> uniqueMethodExecutions = new ArrayList<MarkedMethodExecution>();
		if (markedThreads == null) {
			updateMarkedThreads();
		}
		if (unmarkedMethodSignatures == null) unmarkedMethodSignatures = trace.getUnmarkedMethodSignatures(markStart, markEnd);
		for (int i = 0; i < markedThreads.size(); i++) {
			uniqueMethodExecutions.addAll(markedThreads.get(i).getUniqueMethodExecutions(unmarkedMethodSignatures));
		}
		return uniqueMethodExecutions;
	}
}
