package databaseconnector.impl;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;

import com.mendix.logging.ILogNode;
import com.mendix.systemwideinterfaces.core.IContext;
import com.mendix.systemwideinterfaces.core.IMendixObject;

import databaseconnector.interfaces.ConnectionManager;
import databaseconnector.interfaces.ObjectInstantiator;

public class JdbcConnector {
  private final ILogNode logNode;
  private ObjectInstantiator objectInstantiator;
  private ConnectionManager connectionManager;

  public JdbcConnector(final ILogNode logNode, ObjectInstantiator objectInstantiator, ConnectionManager connectionManager) {
    this.logNode = logNode;
    this.objectInstantiator = objectInstantiator;
    this.connectionManager = connectionManager;
  }

  public JdbcConnector(ILogNode logNode) {
    this(logNode, new ObjectInstantiatorImpl(), ConnectionManagerSingleton.getInstance());
  }

  public List<IMendixObject> executeQuery(String jdbcUrl, String userName, String password, String entityName, String sql,
      IContext context) throws SQLException {
    List<IMendixObject> resultList = new ArrayList<>();
    logNode.info(String.format("executeQuery: %s, %s, %s", jdbcUrl, userName, sql));

    // TODO: make sure user doesn't crash runtime due to retrieving too many records
    try (Connection connection = connectionManager.getConnection(jdbcUrl, userName, password);
        PreparedStatement preparedStatement = connection.prepareStatement(sql);
        ResultSet resultSet = preparedStatement.executeQuery()) {
      ResultSetMetaData resultSetMetaData = resultSet.getMetaData();
      int columnCount = resultSetMetaData.getColumnCount();

      while (resultSet.next()) {
        IMendixObject obj = objectInstantiator.instantiate(context, entityName);
        for (int i = 0; i < columnCount; i++) {
          String columnName = resultSetMetaData.getColumnName(i + 1);
          Object columnValue = resultSet.getObject(i + 1);
          logNode.info(String.format("setting col: %s = %s", columnName, columnValue));
          obj.setValue(context, columnName, columnValue);
        }
        resultList.add(obj);
        logNode.info("obj: " + obj);
      }
    }
    logNode.info(String.format("List: %d", resultList.size()));
    return resultList;
  }

  public String executeQueryToJson(String jdbcUrl, String userName, String password, String sql, IContext context) throws SQLException {
    return null;
  }

  public long executeStatement(String jdbcUrl, String userName, String password, String sql) throws SQLException {
    logNode.info(String.format("executeStatement: %s, %s, %s", jdbcUrl, userName, sql));

    try (Connection connection = connectionManager.getConnection(jdbcUrl, userName, password);
        PreparedStatement preparedStatement = connection.prepareStatement(sql)) {
      return preparedStatement.executeUpdate();
    }
  }
}

@@ -240,16 +240,53 @@
 l.List;%0D
+%0Aimport java.util.function.Consumer;%0D
 %0A%0D%0Aimpor
@@ -1342,24 +1342,754 @@
 ayList%3C%3E();%0D
+%0A%0D%0A    executeQuery(jdbcUrl, userName, password, sql, context, row -%3E %7B%0D%0A      IMendixObject obj = objectInstantiator.instantiate(context, entityName);%0D%0A      row.stream().forEach(c -%3E obj.setValue(context, c.name, c.value));%0D%0A      resultList.add(obj);%0D%0A      logNode.info(%22obj: %22 + obj);%0D%0A    %7D);%0D%0A%0D%0A    logNode.info(String.format(%22List: %25d%22, resultList.size()));%0D%0A    return resultList;%0D%0A  %7D%0D%0A%0D%0A  public String executeQueryToJson(String jdbcUrl, String userName, String password, String sql, IContext context) throws SQLException %7B%0D%0A    return null;%0D%0A  %7D%0D%0A%0D%0A  public void executeQuery(String jdbcUrl, String userName, String password, String sql,%0D%0A      IContext context, Consumer%3CList%3CColumn%3E%3E consumer) throws SQLException %7B%0D
 %0A    logNode
@@ -2674,80 +2674,58 @@
     
-IMendixObject obj = objectInstantiator.instantiate(context, entityName);
+ArrayList%3CColumn%3E row = new ArrayList%3CColumn%3E();%0D%0A
 %0D%0A  
@@ -3001,38 +3001,35 @@
         
-obj.setValue(context, 
+row.add(new Column(
 columnNa
@@ -3044,16 +3044,17 @@
 mnValue)
+)
 ;%0D%0A     
@@ -3059,24 +3059,26 @@
      %7D%0D%0A
+%0D%0A
         
 resultLi
@@ -3073,334 +3073,237 @@
     
-resultList.add(obj);%0D%0A        logNode.info(%22obj: %22 + obj);%0D%0A    
+consumer.accept(row);%0D%0A      %7D%0D%0A    %7D%0D%0A
   %7D%0D%0A
+%0D%0A
   
-  %7D%0D%0A    logNode.info(String.format(%22List: %25d%22, resultList.size()));%0D%0A    return resultList;%0D%0A  %7D%0D%0A%0D%0A  public String executeQueryToJson(String jdbcUrl, String userName, String password, String sql, IContext context) throws SQLException %7B%0D%0A    return null;
+public class Column %7B%0D%0A    public String name;%0D%0A    public Object value;%0D%0A%0D%0A    public Column(String name, Object value) %7B%0D%0A      this.name = name;%0D%0A      this.value = value;%0D%0A    %7D
 %0D%0A  

Make executeQuery implementation more generic.