package es.uniovi.reflection.patternmining.dao;

import java.sql.SQLException;
import java.text.DecimalFormat;
import java.text.DecimalFormatSymbols;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;

import es.uniovi.reflection.patternmining.debug.Expression;
import es.uniovi.reflection.patternmining.debug.FieldDefinition;
import es.uniovi.reflection.patternmining.debug.MethodDefinition;
import es.uniovi.reflection.patternmining.debug.Node;
import es.uniovi.reflection.patternmining.debug.Program;
import es.uniovi.reflection.patternmining.debug.Statement;
import es.uniovi.reflection.patternmining.debug.Type;
import es.uniovi.reflection.patternmining.debug.TypeDefinition;
import es.uniovi.reflection.patternmining.features.ExpressionFeatures;
import es.uniovi.reflection.patternmining.features.FieldFeatures;
import es.uniovi.reflection.patternmining.features.FileFeatures;
import es.uniovi.reflection.patternmining.features.MethodFeatures;
import es.uniovi.reflection.patternmining.features.ProgramFeatures;
import es.uniovi.reflection.patternmining.features.StatementFeatures;
import es.uniovi.reflection.patternmining.features.TypeDefFeatures;
import es.uniovi.reflection.patternmining.features.TypeFeatures;
import introspector.model.IntrospectorModel;
import introspector.view.IntrospectorTree;

public class ASTNodesInsertionIntrospector extends AbstractASTNodesInsertion{	
	private static Map<Long, Node> nodes;
	private static Map<Long, List<Long>> mappings;
	public static Program p;
	
	private static ASTNodesInsertionIntrospector instance;

	private static DecimalFormat df = new DecimalFormat("0.00", new DecimalFormatSymbols(Locale.ENGLISH));
	
	public static ASTNodesInsertionIntrospector getInstance() {
		if(instance == null)
			instance = new ASTNodesInsertionIntrospector();
		return instance;
	}
    
    private ASTNodesInsertionIntrospector() {
    	nodes = new HashMap<Long, Node>();    	
    	mappings = new HashMap<Long, List<Long>>();    	  
    	p = new Program();
    }
    
	public void insertProgram(ProgramFeatures programFeatures) throws Exception {	
		p.id = programFeatures.getId();
		p.classPercentage = Float.valueOf(df.format(programFeatures.getClassPercentage())); 
		p.interfacePercentage = Float.valueOf(df.format(programFeatures.getInterfacePercentage()));
		p.enumPercentage = Float.valueOf(df.format(programFeatures.getEnumPercentage()));
		p.codeInPackages = programFeatures.isCodeInPackages();
		p.defaultPackage = programFeatures.isDefaultPackage();	
		p.numberOfTypesInDefaultPackage = programFeatures.getNumberOfTypesInDefaultPackage();
		p.numberOfTypesInPackages = programFeatures.getNumberOfTypesInPackages();
		p.userID = programFeatures.getUserID();	
	}
	
	public long insertFile(FileFeatures fileFeatures) throws Exception{
		return 0;
	}
	
	public void insertNodeType (long id, long parentId) throws Exception{
		if(id == parentId) return;	
        if(!mappings.containsKey(parentId))
        	mappings.put(parentId, new ArrayList<Long>());
        if(!mappings.get(parentId).contains(id))    
        	mappings.get(parentId).add(id);  
	}
	
	public void insertTypeDefinition(TypeDefFeatures typeDefFeatures) throws Exception {
	    insertNodeType(typeDefFeatures.getId(), 1);	    	        
        TypeDefinition type = new TypeDefinition(
        		typeDefFeatures.getId(),
        		typeDefFeatures.isInnerClass(), 
        		typeDefFeatures.isNestedClass(),
        		typeDefFeatures.isStatic(),
        		typeDefFeatures.getTypeDefinitionCategory().toString(),
        		typeDefFeatures.isPublic(),
        		typeDefFeatures.isFinalClass(),
        		typeDefFeatures.isHasExtends(), 
        		typeDefFeatures.isAbstract(), 
        		typeDefFeatures.isStrictFP(),
        		typeDefFeatures.getNumberAnnotations(), 
        		typeDefFeatures.isInDefaultPackage(), 
        		typeDefFeatures.getNumberImplements(), 
        		typeDefFeatures.getNumberGenericTypes(),
        		typeDefFeatures.getNumberMethods(), 
        		Float.valueOf(df.format(typeDefFeatures.getPercentageOverloadedMethods())),
        		typeDefFeatures.getNumberConstructors(), 
        		typeDefFeatures.getNumberFields(),
        		typeDefFeatures.getNumberNestedTypes(), 
        		typeDefFeatures.getNumberStaticNestedTypes(), 
        		typeDefFeatures.getNumberInnerTypes(),
        		typeDefFeatures.getTypeNaming().toString(),
        		Float.valueOf(df.format(typeDefFeatures.getStaticFieldPercentage())),
        		Float.valueOf(df.format(typeDefFeatures.getStaticMethodPercentage())),
        		typeDefFeatures.getNumberStaticBlocks(),
        		typeDefFeatures.getSourceCode(),
        		typeDefFeatures.getFileId()
        );    
        nodes.put(type.id,type);       
	}

	public void insertFieldDefinition(FieldFeatures fieldFeatures) throws Exception {
		insertNodeType(fieldFeatures.getId(),fieldFeatures.getIdParent());

        FieldDefinition field = new FieldDefinition(
        		fieldFeatures.getId(),
        		fieldFeatures.getFieldVisibility().toString(),
        		fieldFeatures.getParentNode().toString(),
        		fieldFeatures.isFinal(),
                fieldFeatures.isStatic(),
                fieldFeatures.isVolatile(),
                fieldFeatures.isTransient(),
                fieldFeatures.getNumberAnnotations(),
                fieldFeatures.getType().toString(),
                fieldFeatures.getFieldNaming().toString(),
                fieldFeatures.getInitialValue(),
                fieldFeatures.getInnerClases(),
                fieldFeatures.getSourceCode()
        );     
        nodes.put(field.id, field);
	}
	
	public void insertMethodDefinition(MethodFeatures methodFeatures) throws Exception {
        insertNodeType(methodFeatures.getId(),methodFeatures.getIdParent());        
        MethodDefinition method = new MethodDefinition(
        		methodFeatures.getId(),        		
        		methodFeatures.getMethodVisibility().toString(),
        		methodFeatures.isDefaultImplementation(),
        		methodFeatures.isFinal(),
                methodFeatures.hasOverride(),
                methodFeatures.isStatic(),
                methodFeatures.isAbstract(),
                methodFeatures.isConstructor(),
                methodFeatures.isStrictFP(),
                methodFeatures.isNative(),
                methodFeatures.isSynchronized(),
                methodFeatures.getNumberParameters(),
                methodFeatures.getNumberGenericTypes(),
                methodFeatures.getNumberThrows(),
                methodFeatures.getReturnType().toString(),
                methodFeatures.getFirstParameterType().toString(),
                methodFeatures.getSecondParameterType().toString(),
                methodFeatures.getThirdParameterType().toString(),
                methodFeatures.getNumberAnnotations(),
                methodFeatures.getNumberStatements(),
                methodFeatures.getMethodLocalVars(),
                methodFeatures.getLocVarNaming().toString(),
                methodFeatures.getMethodNaming().toString(),
                methodFeatures.getNumberInnerClasses(),
                methodFeatures.getNumberOfOverloaded(),
                methodFeatures.getFirstTypeParameterId(),
                methodFeatures.getSecondTypeParameterId(),
                methodFeatures.getThirdTypeParameterId(),
                methodFeatures.getReturnTypeParameterId(),
                methodFeatures.getSourceCode()
        );    

        nodes.put(method.id, method);
	}
	
	public void insertStatement(StatementFeatures statementFeatures) throws Exception {
        insertNodeType(statementFeatures.getId(),statementFeatures.getParentId());
       
        Statement sta = new Statement(
        		statementFeatures.getId(),        		
        		statementFeatures.getStatementCategory().toString(),
        		statementFeatures.getFirstChild().toString(),
        		statementFeatures.getSecondChild().toString(),
        		statementFeatures.getThirdChild().toString(),
        		statementFeatures.getParentNode().toString(),
        		statementFeatures.getParentChild().toString(),
        		statementFeatures.getHeight(),
        		statementFeatures.getDepth(),
        		statementFeatures.getFirstChildId(),
        		statementFeatures.getSecondChildId(),
        		statementFeatures.getThirdChildId(),
        		statementFeatures.getSourceCode()
        );
        
        nodes.put(sta.id, sta);
	}
	
	public void insertExpression(ExpressionFeatures expressionFeatures) throws Exception {
		insertNodeType(expressionFeatures.getId(),expressionFeatures.getParentId());				
        
		Expression exp = new Expression(
        			expressionFeatures.getId(),
           			expressionFeatures.getExpressionCategory().toString(),
        			expressionFeatures.getFirstChild().toString(),
        			expressionFeatures.getSecondChild().toString(),
        			expressionFeatures.getThirdChild().toString(),
        			expressionFeatures.getParentNode().toString(), 
        			expressionFeatures.getParentChild().toString(),
        			expressionFeatures.getHeight(),
        			expressionFeatures.getDepth(),
        			expressionFeatures.getFirstChildId(),
        			expressionFeatures.getSecondChildId(),
        			expressionFeatures.getThirdChildId(),
        			expressionFeatures.getSourceCode()
        );        
        nodes.put(exp.id, exp);
	}
	
	public void insertType(TypeFeatures typeFeatures) throws Exception {
		insertNodeType(typeFeatures.getId(),typeFeatures.getParentId());				

		Type type = new Type(
					typeFeatures.getId(),
					typeFeatures.getTypeCategory().toString(),
        			typeFeatures.getParentNode().toString(), 
        			typeFeatures.getParentChild().toString(),
					typeFeatures.isPrimitive(),
        			typeFeatures.getHeight(),
        			typeFeatures.getGenerics(),
        			typeFeatures.getDimensions(),
        			typeFeatures.getSourceCode()
        ); 
        nodes.put(type.id, type);
	}


	@Override
	public void disconnect() throws SQLException {
		// TODO Auto-generated method stub		
	}
		
	public void show() {		
		for(Long node : mappings.keySet())
		{
			for(Long child : mappings.get(node))
			{
				if(nodes.containsKey(node) && nodes.containsKey(child))
					nodes.get(node).nodes.add(nodes.get(child));
				else if(node == 1) {
					p.nodes.add(nodes.get(child));
				}
				else
					System.err.println("Mapping error: node(" + node + ") -> child (" + child + ")");
			}
		}
		
		IntrospectorModel model=new IntrospectorModel("Program", p);		
		new IntrospectorTree("Introspector", model);		
	}
}
