package gr.uom.java.ast;

import gr.uom.java.ast.decomposition.CatchClauseObject;
import gr.uom.java.ast.decomposition.TryStatementObject;

import java.util.ArrayList;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.ListIterator;
import java.util.Set;

import org.eclipse.core.resources.IFile;
import org.eclipse.jdt.core.ITypeRoot;

public abstract class ClassDeclarationObject {
	protected String name;
	protected List<MethodObject> methodList;
	protected List<FieldObject> fieldList;
	protected List<CommentObject> commentList;
	
	public ClassDeclarationObject() {
		this.methodList = new ArrayList<MethodObject>();
		this.fieldList = new ArrayList<FieldObject>();
		this.commentList = new ArrayList<CommentObject>();
	}

	public abstract ITypeRoot getITypeRoot();
	public abstract ClassObject getClassObject();
	public abstract IFile getIFile();
	public abstract TypeObject getSuperclass();

	public boolean addMethod(MethodObject method) {
		return methodList.add(method);
	}

	public boolean addField(FieldObject f) {
		return fieldList.add(f);
	}

	public boolean addComment(CommentObject comment) {
		return commentList.add(comment);
	}

	public boolean addComments(List<CommentObject> comments) {
		return commentList.addAll(comments);
	}

	public List<MethodObject> getMethodList() {
		return methodList;
	}

	public ListIterator<MethodObject> getMethodIterator() {
		return methodList.listIterator();
	}

	public ListIterator<FieldObject> getFieldIterator() {
		return fieldList.listIterator();
	}

	public ListIterator<CommentObject> getCommentIterator() {
		return commentList.listIterator();
	}

	public int getNumberOfMethods() {
		return methodList.size();
	}

	public void setName(String name) {
		this.name = name;
	}

	public String getName() {
		return name;
	}

	public boolean isGeneratedByParserGenenator() {
		for(CommentObject comment : commentList) {
			if(comment.getText().contains("Generated By:JavaCC") ||
					comment.getText().contains("Generated By:JJTree") ||
					(comment.getText().contains("Generated from") && comment.getText().contains("by ANTLR")) ||
					comment.getText().contains("generated by JFlex") ||
					comment.getText().contains("generated by CUP") ||
					comment.getText().contains("created by jay")) {
				return true;
			}
		}
		return false;
	}

	public boolean containsMethodWithTestAnnotation() {
    	for(MethodObject method : methodList) {
    		if(method.hasTestAnnotation())
    			return true;
    	}
    	return false;
    }

    public boolean extendsTestCase() {
    	TypeObject superclass = this.getSuperclass();
    	if(superclass == null)
    		return false;
    	else if(superclass.getClassType().equals("junit.framework.TestCase"))
    		return true;
    	else {
    		ClassObject superClassObject = ASTReader.getSystemObject().getClassObject(superclass.getClassType());
    		if(superClassObject != null)
    			return superClassObject.extendsTestCase();
    	}
    	return false;
    }

    public MethodObject getMethod(MethodInvocationObject mio) {
        ListIterator<MethodObject> mi = getMethodIterator();
        while(mi.hasNext()) {
            MethodObject mo = mi.next();
            if(mo.equals(mio))
                return mo;
        }
        return null;
    }

    public MethodObject getMethod(SuperMethodInvocationObject smio) {
        ListIterator<MethodObject> mi = getMethodIterator();
        while(mi.hasNext()) {
            MethodObject mo = mi.next();
            if(mo.equals(smio))
                return mo;
        }
        return null;
    }

    public boolean containsMethodInvocation(MethodInvocationObject methodInvocation) {
    	for(MethodObject method : methodList) {
    		if(method.containsMethodInvocation(methodInvocation))
    			return true;
    	}
    	return false;
    }

    public boolean containsFieldInstruction(FieldInstructionObject fieldInstruction) {
    	for(MethodObject method : methodList) {
    		if(method.containsFieldInstruction(fieldInstruction))
    			return true;
    	}
    	return false;
    }

    public boolean containsMethodInvocation(MethodInvocationObject methodInvocation, MethodObject excludedMethod) {
    	for(MethodObject method : methodList) {
    		if(!method.equals(excludedMethod) && method.containsMethodInvocation(methodInvocation))
    			return true;
    	}
    	return false;
    }

    public boolean containsSuperMethodInvocation(SuperMethodInvocationObject superMethodInvocation) {
    	for(MethodObject method : methodList) {
    		if(method.containsSuperMethodInvocation(superMethodInvocation))
    			return true;
    	}
    	return false;
    }

    public boolean hasFieldType(String className) {
        ListIterator<FieldObject> fi = getFieldIterator();
        while(fi.hasNext()) {
            FieldObject fo = fi.next();
            if(fo.getType().getClassType().equals(className))
                return true;
        }
        return false;
    }

	public Set<FieldObject> getFieldsAccessedInsideMethod(AbstractMethodDeclaration method) {
		Set<FieldObject> fields = new LinkedHashSet<FieldObject>();
		for(FieldInstructionObject fieldInstruction : method.getFieldInstructions()) {
			FieldObject accessedFieldFromThisClass = findField(fieldInstruction);
			if(accessedFieldFromThisClass != null) {
				fields.add(accessedFieldFromThisClass);
			}
		}
		if(method.getMethodBody() != null) {
			List<TryStatementObject> tryStatements = method.getMethodBody().getTryStatements();
			for(TryStatementObject tryStatement : tryStatements) {
				for(CatchClauseObject catchClause : tryStatement.getCatchClauses()) {
					for(FieldInstructionObject fieldInstruction : catchClause.getBody().getFieldInstructions()) {
						FieldObject accessedFieldFromThisClass = findField(fieldInstruction);
						if(accessedFieldFromThisClass != null) {
							fields.add(accessedFieldFromThisClass);
						}
					}
				}
				if(tryStatement.getFinallyClause() != null) {
					for(FieldInstructionObject fieldInstruction : tryStatement.getFinallyClause().getFieldInstructions()) {
						FieldObject accessedFieldFromThisClass = findField(fieldInstruction);
						if(accessedFieldFromThisClass != null) {
							fields.add(accessedFieldFromThisClass);
						}
					}
				}
			}
		}
		return fields;
	}

	public FieldObject getField(FieldInstructionObject fieldInstruction) {
		for(FieldObject field : fieldList) {
			if(field.equals(fieldInstruction)) {
				return field;
			}
		}
		return null;
	}
	
	protected FieldObject findField(FieldInstructionObject fieldInstruction) {
		FieldObject field = getField(fieldInstruction);
		if(field != null) {
			return field;
		}
		else {
			TypeObject superclassType = getSuperclass();
			if(superclassType != null) {
				ClassObject superclassObject = ASTReader.getSystemObject().getClassObject(superclassType.toString());
				if(superclassObject != null) {
					return superclassObject.findField(fieldInstruction);
				}
			}
		}
		return null;
	}
}
