package inference.rewrite;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

import lombok.Getter;
import models.terms.DependencyTerm;
import models.terms.EvaluatableTerm;
import models.terms.Resource;

public class ResourceTree {
	
	@Getter
	private Resource root;
	private Map<Position, List<Position>> tree;
	private Map<Position, Resource> resourceMap;
	
	public ResourceTree(EvaluatableTerm term) {
		tree = new HashMap<>();
		resourceMap = new HashMap<>();
		constructResourceTree(term, new Position(List.of(0)));
		root = resourceMap.get(new Position(List.of(0)));
	}
	
	private List<Position> constructResourceTree(EvaluatableTerm term, Position top) {
		if (term instanceof Resource resource) {
			resourceMap.put(top, resource);
			return List.of(top);
		} else if (term instanceof DependencyTerm depTerm) {
			EvaluatableTerm dependingTerm = depTerm.getDependingTerm();
			List<Resource> dependedResources = depTerm.getDependedResources(); 
			List<EvaluatableTerm> argumentTerms = depTerm.getArgumentTerms();
			List<Position> dependingTermPositions = constructResourceTree(dependingTerm, top);
			List<Position> resultPositions = new ArrayList<>();
			for (Position pos: dependingTermPositions) {
				tree.put(pos, new ArrayList<>());
			}
			for (int i = 0; i < dependedResources.size(); i++) {
				for (Position pos : dependingTermPositions) {
					Position nextPos = pos.addPath(i);
					tree.get(pos).add(nextPos);
					Position dependedResourcePosition= constructResourceTree(dependedResources.get(i), nextPos).get(0);
					tree.put(dependedResourcePosition, new ArrayList<>());
					Position argumentTermPosition = dependedResourcePosition.addPath(0);
					tree.get(dependedResourcePosition).add(argumentTermPosition);
					tree.put(argumentTermPosition, new ArrayList<>());
					resultPositions.addAll(constructResourceTree(argumentTerms.get(i), argumentTermPosition));
				}
			}
			return resultPositions;
		} else {
			return null;
		}
	}
	
	@Override
	public String toString() {
		return tree.toString();
	}
	
	public void debug(Position pos) {
		System.out.println(pos + ", " + resourceMap.get(pos));
		if (tree.containsKey(pos)) {
			for (Position nextPos : tree.get(pos)) {
				debug(nextPos);
			}
		}
	}
	
	public void debugAllPath() {
		debugAllPath(new Position(), new ArrayList<>());
	}
	
	private void debugAllPath(Position pos, List<Resource> curPath) {
		curPath.add(resourceMap.get(pos));
		if (tree.get(pos).size() == 0) {
			System.out.println(curPath.stream().map(Resource::toString).collect(Collectors.joining("-")));
		} else {
			for (Position nextPos: tree.get(pos)) {
				debugAllPath(nextPos, curPath);
			}
		}
		curPath.remove(curPath.size() - 1);
	}
	
}

