/*
 * Copyright (c) 2015, Kieron Wilkinson
 */
package org.softpres.donkeysql;

import org.softpres.donkeysql.params.ParamQuery;

import javax.sql.DataSource;
import java.sql.Connection;
import java.util.HashMap;
import java.util.Map;

/**
 * Database DSL entry point.
 */
public class DB {

  /**
   * Start a DB query with an explicit {@link Connection}, which assumes any connection
   * management is being done at the call site. The connection will not be automatically
   * closed.
   */
  public static ConnectionBuilder with(Connection connection) {
    return new ConnectionBuilder(() -> connection, false);
  }

  /**
   * Start a DB query with the specified DataSource, which assumes any connection management
   * is either being done by an underlying pool, or that it is okay to close the connection
   * when done. The connection will be automatically closed when all the results have been
   * consumed.
   */
  public static ConnectionBuilder with(DataSource dataSource) {
    return new ConnectionBuilder(dataSource::getConnection, true);
  }

  public static class ConnectionBuilder {
    private final ConnectionFactory connectionFactory;
    private final boolean autoClose;

    ConnectionBuilder(ConnectionFactory connectionFactory, boolean autoClose) {
      this.connectionFactory = connectionFactory;
      this.autoClose = autoClose;
    }

    public QueryBuilder query(String sql) {
      return new QueryBuilder(this, sql);
    }
  }

  /**
   * Signifies a class can construct a {@link StagedQuery} from a {@link RowMapper},
   * mainly used to allow code reuse at a call-site.
   */
  public interface MappableQuery {
    <T> StagedQuery<T> map(RowMapper<T> mapper);
  }

  public static class QueryBuilder implements MappableQuery {
    private final ConnectionBuilder connection;
    private final String sql;

    QueryBuilder(ConnectionBuilder connection, String sql) {
      this.connection = connection;
      this.sql = sql;
    }

    /** Convenience method to allow delegating setting of named params. */
    public NamedQueryBuilder named() {
      return new NamedQueryBuilder(this);
    }

    public IndexedQueryBuilder params(Object... params) {
      return new IndexedQueryBuilder(this, params);
    }

    public NamedQueryBuilder param(String name, Object value) {
      return named().param(name, value);
    }

    public <T> StagedQuery<T> map(RowMapper<T> mapper) {
      return new StagedQuery<>(
            connection.connectionFactory,
            connection.autoClose,
            mapper,
            ParamQuery.none(sql));
    }
  }

  public static class IndexedQueryBuilder implements MappableQuery {
    private final QueryBuilder builder;
    private final Object[] params;

    IndexedQueryBuilder(QueryBuilder builder, Object[] params) {
      this.builder = builder;
      this.params = params;
    }

    public <T> StagedQuery<T> map(RowMapper<T> mapper) {
      return new StagedQuery<>(
            builder.connection.connectionFactory,
            builder.connection.autoClose,
            mapper,
            ParamQuery.indexed(builder.sql, params));
    }
  }

  public static class NamedQueryBuilder implements MappableQuery {
    private final QueryBuilder builder;
    private final Map<String, Object> params;

    NamedQueryBuilder(QueryBuilder builder) {
      this.builder = builder;
      params = new HashMap<>();
    }

    public NamedQueryBuilder param(String name, Object value) {
      params.put(name, value);
      return this;
    }

    public <T> StagedQuery<T> map(RowMapper<T> mapper) {
      return new StagedQuery<>(
            builder.connection.connectionFactory,
            builder.connection.autoClose,
            mapper,
            ParamQuery.named(builder.sql, params));
    }
  }

}

@@ -285,16 +285,301 @@
 s DB %7B%0A%0A
+  private final ConnectionFactory connectionFactory;%0A  private final boolean autoCloseConnection;%0A%0A  private DB(ConnectionFactory connectionFactory, boolean autoCloseConnection) %7B%0A    this.connectionFactory = connectionFactory;%0A    this.autoCloseConnection = autoCloseConnection;%0A  %7D%0A%0A
   /**%0A  
@@ -788,33 +788,18 @@
  static 
-ConnectionBuilder
+DB
  with(Co
@@ -836,33 +836,18 @@
 urn new 
-ConnectionBuilder
+DB
 (() -%3E c
@@ -1189,33 +1189,18 @@
  static 
-ConnectionBuilder
+DB
  with(Da
@@ -1237,33 +1237,18 @@
 urn new 
-ConnectionBuilder
+DB
 (dataSou
@@ -1285,314 +1285,214 @@
 %0A%0A  
-public static class ConnectionBuilder %7B%0A    private final ConnectionFactory connectionFactory;%0A    private final boolean autoClose;%0A%0A    ConnectionBuilder(ConnectionFactory connectionFactory, boolean autoClose) %7B%0A      this.connectionFactory = connectionFactory;%0A      this.autoClose = autoClose;%0A    %7D%0A%0A  
+/**%0A   * A query is usually constructed from a static SQL statement, the returned builder here%0A   * allowing the specification of any required parameters before mapping the result for%0A   * execution.%0A   */%0A
   pu
@@ -1525,34 +1525,32 @@
 ring sql) %7B%0A    
-  
 return new Query
@@ -1561,25 +1561,13 @@
 der(
-this, sql);%0A    %7D
+sql);
 %0A  %7D
@@ -1812,31 +1812,24 @@
 %7D%0A%0A  public 
-static 
 class QueryB
@@ -1884,165 +1884,51 @@
 nal 
-ConnectionBuilder connection;%0A    private final String sql;%0A%0A    QueryBuilder(ConnectionBuilder connection, String sql) %7B%0A      this.connection = connection;
+String sql;%0A%0A    QueryBuilder(String sql) %7B
 %0A   
@@ -2446,27 +2446,16 @@
         
-connection.
 connecti
@@ -2477,36 +2477,35 @@
         
-connection.autoClose
+autoCloseConnection
 ,%0A      
@@ -2569,31 +2569,24 @@
 %7D%0A%0A  public 
-static 
 class Indexe
@@ -2933,35 +2933,16 @@
         
-builder.connection.
 connecti
@@ -2968,36 +2968,27 @@
     
-builder.connection.autoClose
+autoCloseConnection
 ,%0A  
@@ -3079,23 +3079,16 @@
  public 
-static 
 class Na
@@ -3554,35 +3554,16 @@
         
-builder.connection.
 connecti
@@ -3589,36 +3589,27 @@
     
-builder.connection.autoClose
+autoCloseConnection
 ,%0A  

Simplify DSL construction