package interactivefeaturelocator.model;

import java.util.HashSet;
import java.util.Set;

import org.ntlab.trace.MethodExecution;
import org.ntlab.trace.ThreadInstance;

public class DerivationSession implements IMethodListener {
	private ThreadInstance currentThreadInstance;
	private MethodExecution currentMethodExecution;
	private Set<MethodExecution> reflectedMethodExecutions; //ユーザの判断を反映したメソッド実行の集合
	Set<MethodExecution> unreflectedExecutions = new HashSet<>(); //ユーザの判断が反映できていないメソッド実行の集合
	
	public DerivationSession(MethodExecution seed) {
		currentMethodExecution = seed;
	}

	public MethodExecution getCurrentMethodExecution() {
		return currentMethodExecution;
	}

	/*
	 * カレントメソッド実行のメソッドの属性(新規追加, 修正あり, 修正なし)を一意に定める.
	 * @param property 一意に定められたメソッドの属性
	 * @return propertyを基に属性を一意に定められたカレントメソッド実行のメソッドの全てのメソッド実行
	 */
	public Set<MethodExecution> decideMethodProperty(MethodProperty property) {
		return ((MethodInfo)currentMethodExecution.getAugmentation()).discardOtherProperties(property);
	}
	
	public void moveCurrentMethodExecution(MethodExecution me) {
		if (me == null) return;
		MethodExecution parent = currentMethodExecution.getParent();
		if (parent != null) {
			((MethodInfo)parent.getAugmentation()).removeMethodListener(this);
		}
		((MethodInfo)currentMethodExecution.getAugmentation()).removeMethodListener(this);
		for (MethodExecution child : currentMethodExecution.getChildren()) {
			((MethodInfo)child.getAugmentation()).removeMethodListener(this);
		}
		this.currentMethodExecution = me;
		parent = currentMethodExecution.getParent();
		if (parent != null) {
			((MethodInfo)parent.getAugmentation()).addMethodListener(this);
		}
		((MethodInfo)currentMethodExecution.getAugmentation()).addMethodListener(this);
		for (MethodExecution child : currentMethodExecution.getChildren()) {
			((MethodInfo)child.getAugmentation()).addMethodListener(this);
		}
	}

	@Override
	public void notify(MethodInfo changedMethod) {
		reflectedMethodExecutions = new HashSet<>();
		Set<MethodProperty> newProperties = changedMethod.getProperties();
		if (currentMethodExecution.getAugmentation() == changedMethod) {
			// 推論 (消滅するかカレントをどこかに移すかを判断, 影響を受けるメソッドは存在しないか)
			
//			moveCurrentMethodExecution(null);
			reflectedMethodExecutions.add(currentMethodExecution);
		} else if (currentMethodExecution.getCallerMethodExecution().getAugmentation() == changedMethod
				&& newProperties.size() == 1 && newProperties.contains(MethodProperty.unmodified)) {
			// 命題9適用
			unreflectedExecutions = ((MethodInfo)currentMethodExecution.getAugmentation()).discardProperty(MethodProperty.created);
			reflectedMethodExecutions.add(currentMethodExecution.getParent());
		} else {
			for (MethodExecution me : currentMethodExecution.getChildren()) {
				if (me.getAugmentation() == changedMethod 
						&& newProperties.size() == 1 && newProperties.contains(MethodProperty.created)) {
					// 命題9適用
					unreflectedExecutions.addAll(((MethodInfo)currentMethodExecution.getAugmentation()).discardProperty(MethodProperty.unmodified));
					reflectedMethodExecutions.add(me);
				}
			}
			
		}
	}

	public Set<MethodExecution> getReflectedMethodExecutions() {
		return reflectedMethodExecutions;
	}

	public Set<MethodExecution> getUnreflectedMethodExecutions() {
		return unreflectedExecutions;
	}
}
