package es.uniovi.reflection.patternmining.dao;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;

import es.uniovi.reflection.patternmining.category.ExpressionCategory;
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 es.uniovi.reflection.patternmining.model.interfaces.ExpressionChild;
import es.uniovi.reflection.patternmining.util.Util;

public class ASTNodesInsertionPostgres extends AbstractASTNodesInsertion {
	
	/* Localhost */
	private final String url = "jdbc:postgresql://localhost:5432/finaldataset";
	/* Virtual Machine */
	//private final String url = "jdbc:postgresql://156.35.94.139:5432/extractiondatalow";
	
    private final String user = "postgres";
    private final String password = "postgres";
    Connection conn = null;
    ResultSet resultSet;
    
    public ASTNodesInsertionPostgres() {
    	try {
			Class.forName("org.postgresql.Driver");
		} catch (ClassNotFoundException e) {
			System.err.println("Where is your PostgreSQL JDBC Driver? "	+ "Include in your library path!");
			e.printStackTrace();
			return;
		}    	
		try {
			conn = DriverManager.getConnection(url, user,password);
			this.programid = getMaxProgramId();
			this.fileid = getMaxFileId();
			this.nodeid = getMaxNodeTypeId();
		} catch (Exception e) {
			System.err.println("Connection Failed! Check output console");
			e.printStackTrace();
			return;
		}

		if (conn != null) {
			
		} else {
			System.err.println("Failed to make connection!");
		}
    }
    
    public long getMaxProgramId() throws Exception{
    	long id=0;
		try {
			Statement stmnt = null;
	        stmnt = conn.createStatement();
	        String sql = "select max(program_id) as maximo from program";
	        resultSet=stmnt.executeQuery(sql);
			while (resultSet.next()) {
				 id = resultSet.getLong("maximo");
			}

	    } catch (SQLException e) {
	        System.out.println(e.getMessage());
	    }
		return id;
    }
    
    public long getMaxNodeTypeId() throws Exception{
    	long id=0;
		try {
			Statement stmnt = null;
	        stmnt = conn.createStatement();
	        String sql = "select max(nodetype_id) as maximo from nodetype";
	        resultSet=stmnt.executeQuery(sql);
			while (resultSet.next()) {
				 id = resultSet.getLong("maximo");
			}

	    } catch (SQLException e) {
	        System.out.println(e.getMessage());
	    }
		return id;
    }
    
    
    public long getMaxFileId() throws Exception{
    	long id=0;
		try {
			Statement stmnt = null;
	        stmnt = conn.createStatement();
	        String sql = "select max(file_id) as maximo from file";
	        resultSet=stmnt.executeQuery(sql);
			while (resultSet.next()) {
				 id = resultSet.getLong("maximo");
			}

	    } catch (SQLException e) {
	        System.out.println(e.getMessage());
	    }
		return id;
    }
    
    public String reduceSourceCode(String sourceCode) {    	
    	/*if (sourceCode != null) {
    		sourceCode = sourceCode.replaceAll("([\t\n])","");
    		if(sourceCode.length() > 500) 
    			return sourceCode.substring(0, 500);    
    	} */   	
    	return sourceCode;
    }
    	
	public void insertProgram(ProgramFeatures programFeatures) throws Exception {		
		try {
			Statement stmnt = null;
	        stmnt = conn.createStatement();
	        String sql = "INSERT INTO program (program_id, user_id, user_class, classpercentage, interfacepercentage, enumpercentage,"
	        		+ "codeinpackages, defaultpackage, numberoftypesinpackages, numberoftypesindefaultpackage) "
	                + "VALUES " + "(" + programFeatures.getId() + ",'" + programFeatures.getUserID() + "','"
	                + programFeatures.getUserClass() + "'," + programFeatures.getClassPercentage() + "," 
	                + programFeatures.getInterfacePercentage() + ","
	                + programFeatures.getEnumPercentage() + "," + programFeatures.isCodeInPackages() + ","
	                + programFeatures.isDefaultPackage() + "," + programFeatures.getNumberOfTypesInPackages() + ","
	                + programFeatures.getNumberOfTypesInDefaultPackage() + ");";
	        stmnt.executeUpdate(sql);
	    } catch (SQLException e) {
	        System.err.println(e.getMessage());
	    }
	}
	
	public long insertFile(FileFeatures fileFeatures) throws Exception{
		long id=this.getNextFileId();
		try {
			Statement stmnt = null;
	        stmnt = conn.createStatement();
	        String sql = "INSERT INTO file (file_id, program_id, pathname) VALUES (" + id + 
	        		"," + fileFeatures.getProgramId() + ",'" + fileFeatures.getFilePathName() +"');";
	        stmnt.executeUpdate(sql);
	    } catch (SQLException e) {
	        System.err.println(e.getMessage());
	    }
		return id;
	}

	
	//Función sincronizada para obtener el máximo id e insertarlo
	public synchronized void insertNodeType (long id, long parentId) throws Exception{
        //System.out.println(id+" "+ parentId);
		try {
	        Statement stmnt = null;
	        stmnt = conn.createStatement();
	        String sql = "INSERT INTO nodetype (nodetype_id, parentnode_id) VALUES(" + id + "," + parentId +");";
	        if(parentId == -1)
	        	sql = "INSERT INTO nodetype (nodetype_id) VALUES(" + id + ");";
	        stmnt.executeUpdate(sql);
	    } catch (SQLException e) {
	        System.err.println(e.getMessage());
	    }
		return;
		
	}
	
	public void insertTypeDefinition(TypeDefFeatures typeDefFeatures) throws Exception {
		try {
	        Statement stmnt = null;
	        stmnt = conn.createStatement();
	        insertNodeType(typeDefFeatures.getId(),-1);
	        typeDefFeatures.setSourceCode(Util.prepareSourceCode(typeDefFeatures.getSourceCode()));
	        String sql = "INSERT INTO typedefinition (nodetype_id, user_id, user_class, typenodetype, typepublicvisibility, isfinalclass, hasextends, numberannotations, isindefaultpackage,"
	        		+ "isabstract, numberimplements,"
	        		+ "numbergenerictypes, numbermethods, percentageoverloadedmethods, "
	        		+ "numberconstructors, numberfields, numbernestedtypes,"
	        		+ "numberstaticnestedtypes, numberinnertypes,"
	        		+ "namingconvention, isinnerclass, isstrictfp, staticfieldpercentage, "
	        		+ "staticmethodpercentage, numberstaticblocks, file_id,"
	        		+ "isstatic, isnestedclass, sourcecode) "
	                + "VALUES " + "("+typeDefFeatures.getId() + ",'" + Util.userID + "','" + Util.userClass
	                + "','" + typeDefFeatures.getTypeDefinitionCategory() 
	                + "'," + typeDefFeatures.isPublic() + ","
	                + typeDefFeatures.isFinalClass()+"," + typeDefFeatures.isHasExtends() 
	                + "," + typeDefFeatures.getNumberAnnotations() + "," 
	                + typeDefFeatures.isInDefaultPackage() + "," + typeDefFeatures.isAbstract() + ","
	                + typeDefFeatures.getNumberImplements() + "," 
	                + typeDefFeatures.getNumberGenericTypes() + "," 
	                + typeDefFeatures.getNumberMethods() + "," 
	                + typeDefFeatures.getPercentageOverloadedMethods() + "," 
	                + typeDefFeatures.getNumberConstructors() + "," 
	                + typeDefFeatures.getNumberFields() + "," + typeDefFeatures.getNumberNestedTypes() 
	                + "," + typeDefFeatures.getNumberStaticNestedTypes() + "," 
	                + typeDefFeatures.getNumberInnerTypes() + ",'" + typeDefFeatures.getTypeNaming() + "',"
	                + typeDefFeatures.isInnerClass() + "," + typeDefFeatures.isStrictFP() 
	                + "," + typeDefFeatures.getStaticFieldPercentage() + ","
	                + typeDefFeatures.getStaticMethodPercentage() + "," +
	                + typeDefFeatures.getNumberStaticBlocks() + "," + Util.fileId
	                + "," + typeDefFeatures.isStatic() + "," + typeDefFeatures.isNestedClass() + 
	                ",'" + reduceSourceCode(typeDefFeatures.getSourceCode()) +"')";
	        stmnt.executeUpdate(sql);

	    } catch (SQLException e) {
	        System.err.println(e.getMessage());
	    }
	}

	public void insertFieldDefinition(FieldFeatures fieldFeatures) throws Exception {
		try {
	        Statement stmnt = null;
	        stmnt = conn.createStatement();
	        insertNodeType(fieldFeatures.getId(),fieldFeatures.getIdParent());
	        fieldFeatures.setSourceCode(Util.prepareSourceCode(fieldFeatures.getSourceCode()));
	        String sql = "INSERT INTO fielddefinition (nodetype_id, user_id, "
	        		+ "user_class, fieldvisibility, parentnode, isfinal, isstatic, numberannotations,"
	        		+ "namingconvention, initialvalue, type, isvolatile, istransient, sourcecode) "
	                + "VALUES " + "("+fieldFeatures.getId() + ",'" + Util.userID + "','" 
	        		+ Util.userClass + "','" + fieldFeatures.getFieldVisibility() + "','"
	                + fieldFeatures.getParentNode() + "'," + fieldFeatures.isFinal() + ","
	                + fieldFeatures.isStatic() + "," + fieldFeatures.getNumberAnnotations() 
	                + ",'" + fieldFeatures.getFieldNaming() + "','"
	                + fieldFeatures.getInitialValue() + "','"+fieldFeatures.getType() 
	                + "',"+fieldFeatures.isVolatile() + ","
	                + fieldFeatures.isTransient() + ",'" + fieldFeatures.getSourceCode() +"');";
	        stmnt.executeUpdate(sql);
	    } catch (SQLException e) {
	        System.err.println(e.getMessage());
	    }
	}
	
	public void insertMethodDefinition(MethodFeatures methodFeatures) throws Exception {
		try {
	        Statement stmnt = null;
	        stmnt = conn.createStatement();
	        insertNodeType(methodFeatures.getId(),methodFeatures.getIdParent());
	        methodFeatures.setSourceCode(Util.prepareSourceCode(methodFeatures.getSourceCode()));
	        String sql = "INSERT INTO methoddefinition (nodetype_id, user_id, "
	        		+ "user_class, methodvisibility, defaultimplementation, isfinal, hasoverride, isstatic, "
	        		+ "isabstract, isconstructor, numberparameters, numbergenerictypes, "
	        		+ "numberthrows, returntype, firstparametertype, secondparametertype, "
	        		+ "thirdparametertype, numberannotations, numberstmts, "
	        		+ "methodlocalvars, locvarnaming, namingconvention, numberinnerclasses, "
	        		+ "isstrictfp, isnative, issynchronized, numberofoverloaded, "
	        		+ "firsttypeparameter_id, secondtypeparameter_id, "
	        		+ "thirdtypeparameter_id, returntype_id, sourcecode) "
	                + "VALUES (" + methodFeatures.getId() + ",'" + Util.userID + "','" + Util.userClass 
	                + "','" + methodFeatures.getMethodVisibility() + "'," 
	                + methodFeatures.isDefaultImplementation()+","+methodFeatures.isFinal()+","
	        		+ methodFeatures.hasOverride() + "," +methodFeatures.isStatic() 
	        		+ ","+methodFeatures.isAbstract() + ","+methodFeatures.isConstructor() + ","
	        		+ methodFeatures.getNumberParameters() + "," + methodFeatures.getNumberGenericTypes() 
	        		+ "," + methodFeatures.getNumberThrows() + ",'" 
	        		+ methodFeatures.getReturnType() + "','" + methodFeatures.getFirstParameterType() + "','" 
	        		+ methodFeatures.getSecondParameterType() + "','" + methodFeatures.getThirdParameterType() 
	        		+ "'," + methodFeatures.getNumberAnnotations() + "," + methodFeatures.getNumberStatements() + ","
	                + methodFeatures.getMethodLocalVars() + ",'" 
	        		+ methodFeatures.getLocVarNaming() + "','"+methodFeatures.getMethodNaming() 
	        		+ "'," + methodFeatures.getNumberInnerClasses()
	                +","+methodFeatures.isStrictFP() + "," + methodFeatures.isNative() 
	                + ","+methodFeatures.isSynchronized() + "," 
	                + methodFeatures.getNumberOfOverloaded() + "," + methodFeatures.getFirstTypeParameterId()
	                + "," + methodFeatures.getSecondTypeParameterId() + ","
	                + methodFeatures.getThirdTypeParameterId() + "," + methodFeatures.getReturnTypeParameterId()
	                + ",'" + reduceSourceCode(methodFeatures.getSourceCode()) + "');";
	        stmnt.executeUpdate(sql);
	    } catch (SQLException e) {
	        System.err.println(e.getMessage());
	    }
	}
	
	public void insertStatement(StatementFeatures statementFeatures) throws Exception {
		try {
	        Statement stmnt = null;
	        stmnt = conn.createStatement();	   
	        insertNodeType(statementFeatures.getId(),statementFeatures.getParentId());
	        statementFeatures.setSourceCode(Util.prepareSourceCode(statementFeatures.getSourceCode()));
	        String sql = "INSERT INTO statement (nodetype_id, user_id, user_class,statement_category, firstchild, "
	        		+ "secondchild, thirdchild, parentnode, parentchild, height, depth, firstchildid, secondchildid, thirdchildid, sourcecode) "
	                + "VALUES " + "(" + statementFeatures.getId() + ",'" 
	        		+ Util.userID + "','" + Util.userClass + "','"
	        		+ statementFeatures.getStatementCategory() + "','" 
	        		+ statementFeatures.getFirstChild() +"','"
	                + statementFeatures.getSecondChild() + "','" + statementFeatures.getThirdChild() 
	                + "','" + statementFeatures.getParentNode() + "','"
	        		+ statementFeatures.getParentChild() + "'," + statementFeatures.getHeight() 
	        		+ "," + statementFeatures.getDepth() + "," + statementFeatures.getFirstChildId() + ","
	        		+ statementFeatures.getSecondChildId() + "," + statementFeatures.getThirdChildId()
	        		+ ",'" + statementFeatures.getSourceCode() + "');";
	        stmnt.executeUpdate(sql);

	    } catch (SQLException e) {
	        System.err.println(e.getMessage());
	    }
	}
	
	public void insertExpression(ExpressionFeatures expressionFeatures) throws Exception {
		try {
	        Statement stmnt = null;
	        stmnt = conn.createStatement();
	        insertNodeType(expressionFeatures.getId(),expressionFeatures.getParentId());
	        expressionFeatures.setSourceCode(Util.prepareSourceCode(expressionFeatures.getSourceCode()));
	        String sql = "INSERT INTO expression (nodetype_id, user_id, user_class, expression_category, "
	        		+ "firstchild, secondchild, thirdchild, parentnode, "
	        		+ "parentchild, height, depth, firstchildid, secondchildid, thirdchildid, sourcecode) "
	                + "VALUES " + "(" + expressionFeatures.getId() + ",'" + Util.userID + "','" 
	        		+ Util.userClass + "','"
	        		+ expressionFeatures.getExpressionCategory() + "','" 
	        		+expressionFeatures.getFirstChild() + "','"
	                + expressionFeatures.getSecondChild() + "','" + expressionFeatures.getThirdChild( ) + "','" 
	        		+ expressionFeatures.getParentNode() + "','" + expressionFeatures.getParentChild() + "',"
	                + expressionFeatures.getHeight() + "," + expressionFeatures.getDepth() 
	                + "," + expressionFeatures.getFirstChildId() + "," + expressionFeatures.getSecondChildId()
	                + "," + expressionFeatures.getThirdChildId()
	                + ",'" + expressionFeatures.getSourceCode() + "');";
	        stmnt.executeUpdate(sql);	       
	    } catch (SQLException e) {
	        System.err.println(e.getMessage());
	    }
	}
	
	public void insertType(TypeFeatures typeFeatures) throws Exception {
		try {
	        Statement stmnt = null;
	        stmnt = conn.createStatement();
	        insertNodeType(typeFeatures.getId(),typeFeatures.getParentId());
	        typeFeatures.setSourceCode(Util.prepareSourceCode(typeFeatures.getSourceCode()));
	        String sql = "INSERT INTO type (nodetype_id, user_id, user_class, type_category, "
	        		+ "primitive, dimensions, generics, height, "
	        		+ "parentnode, parentchild, sourcecode) "
	                + "VALUES " + "("+typeFeatures.getId() + ",'" 
	        		+ Util.userID + "','" + Util.userClass + "','"
	        		+ typeFeatures.getTypeCategory() + "'," + typeFeatures.isPrimitive() + "," 
	        		+ typeFeatures.getDimensions() + ","
	                + typeFeatures.getGenerics() + "," + typeFeatures.getHeight() + ",'"
	                + typeFeatures.getParentNode() + "','" + typeFeatures.getParentChild() 
	                + "','" + typeFeatures.getSourceCode() + "');";
	        stmnt.executeUpdate(sql);	       
	    } catch (SQLException e) {
	        System.err.println(e.getMessage());
	    }
	}

	@Override
	public void disconnect() throws SQLException {
		conn.close();		
	}
}
