package es.uniovi.reflection.patternmining.visitor.fields;

import javax.lang.model.element.Modifier;

import com.sun.source.tree.AnnotationTree;
import com.sun.source.tree.ClassTree;
import com.sun.source.tree.MethodInvocationTree;
import com.sun.source.tree.ModifiersTree;
import com.sun.source.tree.NewClassTree;
import com.sun.source.tree.StatementTree;
import com.sun.source.tree.VariableTree;
import com.sun.source.util.TreeScanner;

import es.uniovi.reflection.patternmining.category.ExpressionCategory;
import es.uniovi.reflection.patternmining.category.ExpressionRole;
import es.uniovi.reflection.patternmining.category.JavaType;
import es.uniovi.reflection.patternmining.category.Others;
import es.uniovi.reflection.patternmining.category.TypeCategory;
import es.uniovi.reflection.patternmining.category.TypeParent;
import es.uniovi.reflection.patternmining.category.TypeRole;
import es.uniovi.reflection.patternmining.features.ExpressionFeatures;
import es.uniovi.reflection.patternmining.features.FieldFeatures;
import es.uniovi.reflection.patternmining.features.MethodFeatures;
import es.uniovi.reflection.patternmining.features.TypeFeatures;
import es.uniovi.reflection.patternmining.model.ReturnFeatures;
import es.uniovi.reflection.patternmining.util.Util;
import es.uniovi.reflection.patternmining.util.Util.NodeType;

public class FieldDefinitionsVisitor extends TreeScanner<ReturnFeatures, FieldFeatures>{	
    
    @Override
    public ReturnFeatures visitClass(ClassTree classTree, FieldFeatures o) {
        super.visitClass(classTree, o);
        return null;
    }
    
    @Override
    public ReturnFeatures visitVariable(VariableTree variableTree, FieldFeatures fieldFeatures) {
        fieldFeatures.setId(Util.getNextNodeId());
        
        fieldFeatures.setFinal(variableTree.getModifiers().getFlags().contains(Modifier.FINAL));
        fieldFeatures.setStatic(variableTree.getModifiers().getFlags().contains(Modifier.STATIC));
        fieldFeatures.setVolatile(variableTree.getModifiers().getFlags().contains(Modifier.VOLATILE));
        fieldFeatures.setTransient(variableTree.getModifiers().getFlags().contains(Modifier.TRANSIENT));
        fieldFeatures.setFieldVisibility(Util.getVisibility(variableTree.getModifiers()));
        fieldFeatures.setFieldNaming(Util.getNamingCovention(variableTree.getName().toString()));
        fieldFeatures.setNumberAnnotations(variableTree.getModifiers().getAnnotations().size());
        fieldFeatures.setSourceCode(Util.prepareSourceCode(variableTree.toString()));
        
        ReturnFeatures returnFeatures = new ReturnFeatures();
        ReturnFeatures returnFeaturesAux = new ReturnFeatures();
        
        returnFeaturesAux = visitModifiers(variableTree.getModifiers(), fieldFeatures);
        
        
		if (fieldFeatures.getParentNode() == NodeType.Enum) {
			fieldFeatures.setType(TypeCategory.Reference);
		} else {
			
	        TypeFeatures typeChildFeatures = new TypeFeatures();
	    	
	        typeChildFeatures.setParentId(fieldFeatures.getId());
	        typeChildFeatures.setParentNode(TypeParent.FieldDefinition);
	        typeChildFeatures.setParentChild(TypeRole.FieldType);
	        typeChildFeatures.setHeight(fieldFeatures.getHeight() + 1);
	        
	        returnFeaturesAux = Util.visitors.getVisitorType().scan(variableTree.getType(), typeChildFeatures);
	        fieldFeatures.setType(returnFeaturesAux.getType());
		}
		
		//initializer
        fieldFeatures.setValueDefined(variableTree.getInitializer() != null);
        if (fieldFeatures.isValueDefined() == true) {
        	fieldFeatures.setInitialValue(variableTree.getInitializer().getKind().toString());
        	
            ExpressionFeatures expressionChildFeatures = new ExpressionFeatures();
            
            expressionChildFeatures.setParentId(fieldFeatures.getId());
            expressionChildFeatures.setParentNode(Others.FieldDefinition);
            expressionChildFeatures.setParentChild(ExpressionRole.VariableDeclarationInit);
            expressionChildFeatures.setHeight(fieldFeatures.getHeight() + 1);
	        
            returnFeaturesAux = Util.visitors.getVisitorExpressions().scan(variableTree.getInitializer(), expressionChildFeatures);
        }
        else {
        	fieldFeatures.setInitialValue("None");
        }
        
      //Insercción antes porque no necesitamos devolver nada
        try {       
        	Util.databaseService.insertFieldDefinition(fieldFeatures);
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}

        //System.out.println("number of inner clases "+ fieldFeatures.getInnerClases());
        returnFeatures.setNumberOfInnerTypes(fieldFeatures.getInnerClases());
        return returnFeatures;
    }
    
    //expressions
    
    @Override
    public ReturnFeatures visitMethodInvocation(MethodInvocationTree methodInvocationTree, FieldFeatures fieldFeatures)  {
        
    	//revisar
        ExpressionFeatures expressionFeatures = new ExpressionFeatures();
        
        expressionFeatures.setParentId(fieldFeatures.getId());
        //expressionFeatures.setParentNode(Others.FieldDefinition);
        //expressionFeatures.setParentChild(TypeRole.FieldType);
        expressionFeatures.setHeight(fieldFeatures.getHeight());
        
        ReturnFeatures returnFeatures = Util.visitors.getVisitorExpressions().visitMethodInvocation(methodInvocationTree, expressionFeatures);
        return returnFeatures;
    }
    
    @Override
    public ReturnFeatures visitNewClass(NewClassTree newClassTree, FieldFeatures fieldFeatures)  {
        
    	ExpressionFeatures expressionFeatures = new ExpressionFeatures();
        
        expressionFeatures.setParentId(fieldFeatures.getId());
        //expressionFeatures.setParentNode(Others.FieldDefinition);
        //expressionFeatures.setParentChild(TypeRole.FieldType);
        expressionFeatures.setHeight(fieldFeatures.getHeight());
        
        ReturnFeatures returnFeatures = Util.visitors.getVisitorExpressions().visitNewClass(newClassTree, expressionFeatures);
        //System.out.println("number of inner clases "+ returnExpressionFeatures.getInnerClasses());
        fieldFeatures.setInnerClases(fieldFeatures.getInnerClases() + returnFeatures.getNumberOfInnerTypes());
        //System.out.println("number of inner clases "+ fieldFeatures.getInnerClases());
        return returnFeatures;
    }
    
    public ReturnFeatures visitModifiers(ModifiersTree modifiersTree, FieldFeatures fieldFeatures) {
    	ReturnFeatures returnFeatures = new ReturnFeatures();
    	
    	for(AnnotationTree annotation: modifiersTree.getAnnotations()) {
    		returnFeatures = scan(annotation, fieldFeatures);
    	}

		return returnFeatures;
	}
    
    @Override
    public ReturnFeatures visitAnnotation(AnnotationTree annotationTree, FieldFeatures fieldFeatures)  { //hoja
      
        ExpressionFeatures expressionFeatures = new ExpressionFeatures();
        
        expressionFeatures.setHeight(fieldFeatures.getHeight());
        expressionFeatures.setParentId(fieldFeatures.getId());
        expressionFeatures.setParentNode(Others.FieldDefinition);
        expressionFeatures.setParentChild(ExpressionRole.FieldAnnotation);  
        
        ReturnFeatures returnFeatures = Util.visitors.getVisitorExpressions().visitAnnotation(annotationTree, expressionFeatures); 
        return returnFeatures;
    }

}
