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.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Spliterator;
import java.util.Spliterators;
import java.util.Spliterators.AbstractSpliterator;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Supplier;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
import java.util.stream.Stream;
import java.util.stream.StreamSupport;
import java.util.stream.Stream.Builder;

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 Stream<IMendixObject> executeQuery(String jdbcUrl, String userName, String password, String entityName, String sql,
      IContext context) throws SQLException {
    Function<Map<String, Object>, IMendixObject> toMendixObject = columns -> {
      IMendixObject obj = objectInstantiator.instantiate(context, entityName);
      columns.forEach((n, v) -> obj.setValue(context, n, v));
      logNode.info("obj: " + obj);
      return obj;
    };

    Stream<Map<String,Object>> stream = executeQuery(jdbcUrl, userName, password, sql);

    return stream.map(toMendixObject);
  }

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

  public boolean getResultSet(ResultSet rs) throws SQLException {
    return rs.next();
  }

  public Stream<Map<String, Object>> executeQuery(String jdbcUrl, String userName, String password, String sql) throws SQLException {
    logNode.info(String.format("executeQuery: %s, %s, %s", jdbcUrl, userName, sql));

    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();
      ColumnNameInfoGenerator columnNameInfo = new ColumnNameInfoGenerator(resultSetMetaData);
      List<ColumnInfo> columnNames = IntStream.rangeClosed(1, columnCount).mapToObj(columnNameInfo::getColumnInfo).collect(Collectors.toList());
      Stream<ResultSet> stream = ResultSetStream.stream(resultSet);
      Function<ResultSet, Map<String, Object>> toRowMap = rs -> {
        Map<String, Object> row = new HashMap<>();
        Consumer<ColumnInfo> setColumnValue = columnInfo -> {
          try {
            String columnName = columnInfo.getName();
            Object columnValue = rs.getObject(columnInfo.getIndex());
            logNode.info(String.format("setting col: %s = %s", columnName, columnValue));
            row.put(columnName, columnValue);
          } catch (SQLException e) {
            throw new RuntimeException(e);
          }
        };

        columnNames.forEach(setColumnValue);

        return row;
      };

      Stream<Map<String, Object>> rowStream = stream.map(toRowMap);

      // Force the stream to read the whole ResultSet, so that the connection can be closed.
      return rowStream.collect(Collectors.toList()).stream();
    }
  }

  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();
    }
  }
}

@@ -202,307 +202,70 @@
 til.
-HashMap;%0Aimport java.util.Iterator;%0Aimport java.util.List;%0Aimport java.util.Map;%0Aimport java.util.Spliterator;%0Aimport java.util.Spliterators;%0Aimport java.util.Spliterators.AbstractSpliterator;%0Aimport java.util.function.Consumer;%0Aimport java.util.function.Function;%0Aimport java.util.function.Supplier
+List;%0Aimport java.util.Map;%0Aimport java.util.function.Function
 ;%0Aim
@@ -368,87 +368,8 @@
 eam;
-%0Aimport java.util.stream.StreamSupport;%0Aimport java.util.stream.Stream.Builder;
 %0A%0Aim
@@ -2779,16 +2779,27 @@
 mnCount)
+%0A          
 .mapToOb
@@ -2885,17 +2885,26 @@
 ultSet%3E 
-s
+resultSetS
 tream = 
@@ -3016,67 +3016,16 @@
     
-Map%3CString, Object%3E row = new HashMap%3C%3E();%0A        Consumer
+Function
 %3CCol
@@ -3035,19 +3035,21 @@
 Info
+, Object
 %3E 
-s
+g
 et
-Column
 Valu
@@ -3088,62 +3088,8 @@
 y %7B%0A
-            String columnName = columnInfo.getName();%0A
     
@@ -3219,28 +3219,38 @@
  %25s%22, column
+Info.get
 Name
+()
 , columnValu
@@ -3271,26 +3271,13 @@
    r
-ow.put(columnName,
+eturn
  col
@@ -3284,17 +3284,16 @@
 umnValue
-)
 ;%0A      
@@ -3394,24 +3394,31 @@
 %7D;%0A%0A        
+return 
 columnNames.
@@ -3421,52 +3421,73 @@
 mes.
-forEach(setColumnValue);%0A%0A        return row
+stream().collect(Collectors.toMap(ColumnInfo::getName, getValue))
 ;%0A  
@@ -3540,17 +3540,26 @@
 tream = 
-s
+resultSetS
 tream.ma

Simplify getting results from ResultSet.