package gr.uom.java.ast.inheritance;

import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.ListIterator;
import java.util.Map;
import java.util.Set;

import gr.uom.java.ast.ClassObject;
import gr.uom.java.ast.SystemObject;
import gr.uom.java.ast.TypeObject;

public class CompleteInheritanceDetection {
	private Map<String, LinkedHashSet<String>> subclassMap;
	
	public CompleteInheritanceDetection(SystemObject system) {
		this.subclassMap = new LinkedHashMap<String, LinkedHashSet<String>>();
		generateInheritanceHierarchies(system);
	}
	
	private void addSubclassToSuperclass(String superclass, String subclass) {
		if(subclassMap.containsKey(superclass)) {
			LinkedHashSet<String> subclasses = subclassMap.get(superclass);
			subclasses.add(subclass);
		}
		else {
			LinkedHashSet<String> subclasses = new LinkedHashSet<String>();
			subclasses.add(subclass);
			subclassMap.put(superclass, subclasses);
		}
	}
	
	private void generateInheritanceHierarchies(SystemObject system) {
		ListIterator<ClassObject> classIterator = system.getClassListIterator();
        while(classIterator.hasNext()) {
            ClassObject classObject = classIterator.next();
            TypeObject superclassType = classObject.getSuperclass();
            if(superclassType != null) {
            	String superclass = superclassType.getClassType();
            	if(system.getClassObject(superclass) != null) {
            		addSubclassToSuperclass(superclass, classObject.getName());
            	}
            }
            ListIterator<TypeObject> interfaceIterator = classObject.getInterfaceIterator();
            while(interfaceIterator.hasNext()) {
            	TypeObject superInterface = interfaceIterator.next();
            	if(system.getClassObject(superInterface.getClassType()) != null) {
                	addSubclassToSuperclass(superInterface.getClassType(), classObject.getName());
                }
            }
        }
	}
	
	public InheritanceTree getTree(String className) {
		if(subclassMap.containsKey(className)) {
			InheritanceTree tree = new InheritanceTree();
			recursivelyConstructTree(tree, className);
			return tree;
		}
		else {
			return null;
		}
	}
	
	private void recursivelyConstructTree(InheritanceTree tree, String className) {
		if(subclassMap.containsKey(className)) {
			LinkedHashSet<String> subclasses = subclassMap.get(className);
			for(String subclass : subclasses) {
				tree.addChildToParent(subclass, className);
				recursivelyConstructTree(tree, subclass);
			}
		}
	}
	
	public Set<String> getRoots() {
		return subclassMap.keySet();
	}
	
	public Set<InheritanceTree> getMatchingTrees(String subclassName) {
		Set<InheritanceTree> inheritanceTrees = new LinkedHashSet<InheritanceTree>();
		for(String superclass : subclassMap.keySet()) {
			LinkedHashSet<String> subclasses = subclassMap.get(superclass);
			boolean matchingInheritanceHierarchy = false;
			for(String subclass : subclasses) {
				if((subclass.contains(".") && subclass.endsWith("." + subclassName)) || subclass.equals(subclassName)) {
					matchingInheritanceHierarchy = true;
					break;
				}
			}
			if(matchingInheritanceHierarchy)
				inheritanceTrees.add(getTree(superclass));
		}
		return inheritanceTrees;
	}
}
