package com.github.database.rider.core.dsl;

import com.github.database.rider.core.configuration.DBUnitConfig;
import com.github.database.rider.core.configuration.DataSetConfig;
import com.github.database.rider.core.connection.ConnectionHolderImpl;
import com.github.database.rider.core.dataset.DataSetExecutorImpl;

import java.sql.Connection;

/**
 * @author rmpestano
 * @since 1.13.0
 * DSL for populating database which is an alternative to `@DataSet` annotation
 */
public class RiderDSL {

    private static RiderDSL INSTANCE;
    private Connection connection;
    private DataSetConfig dataSetConfig;
    private DBUnitConfig dbUnitConfig;


    /**
     * Creates a dataset in database using the provided connection ({@link Connection}), dataset configuration ({@link DataSetConfig}
     * and dbunit configuration ({@link DBUnitConfig})
     */
    public void createDataSet() {
        validateConnection(connection);
        validateDataSetConfig(dataSetConfig);
        final DataSetExecutorImpl dataSetExecutor = DataSetExecutorImpl.instance(dataSetConfig.getExecutorId(), new ConnectionHolderImpl(connection));
        if (dbUnitConfig != null) {
            dataSetExecutor.setDBUnitConfig(dbUnitConfig);
        }
        dataSetExecutor.createDataSet(dataSetConfig);
    }

    private static void createInstance() {
        if (INSTANCE == null) {
            INSTANCE = new RiderDSL();
        }
    }

    /**
     * Configures the DSL with provided JDBC connection
     *
     * @param connection jdbc connection to be used when populating the database
     * @return
     */
    public static DataSetConfigDSL withConnection(Connection connection) {
        createInstance();
        validateConnection(connection);
        INSTANCE.connection = connection;
        return new DataSetConfigDSL();
    }

    /**
     * Reuses current connection configured in the DSL
     *
     * @return DataSet config DSL
     */
    public static DataSetConfigDSL withConnection() {
        return withConnection(INSTANCE.connection);
    }

    public static class DataSetConfigDSL {

        /**
         * Configures the DSL with provided DataSet configuration
         *
         * @param dataSetConfig {@link DataSetConfig}
         * @return A DBUnitConfigDSL to create DBUnit configuration ({@link DBUnitConfig}
         */
        public static DBUnitConfigDSL withDataSet(DataSetConfig dataSetConfig) {
            validateDataSetConfig(dataSetConfig);
            INSTANCE.dataSetConfig = dataSetConfig;
            return new DBUnitConfigDSL();
        }

        /**
         * Reuses dataset configuration already provided to the DSL
         *
         * @return A DBUnitConfigDSL to create DBUnit configuration ({@link DBUnitConfig}
         */
        public static DBUnitConfigDSL withDataSet() {
            return withDataSet(INSTANCE.dataSetConfig);
        }

    }

    public static class DBUnitConfigDSL {

        /**
         * Configures the DSL with provided DBUnit configuration
         *
         * @param dbUnitConfig {@link DBUnitConfig}
         * @return
         */
        public static RiderDSL withDBUnitConfig(DBUnitConfig dbUnitConfig) {
            INSTANCE.dbUnitConfig = dbUnitConfig;
            return INSTANCE;
        }

        /**
         * Creates a dataset in database using the provided connection ({@link Connection}), dataset configuration ({@link DataSetConfig}
         * and dbunit configuration ({@link DBUnitConfig})
         */
        public static void createDataSet() {
            INSTANCE.createDataSet();
        }
    }

    private static void validateConnection(Connection connection) {
        if (connection == null) {
            throw new RuntimeException("Invalid jdbc connection.");
        }
    }

    private static void validateDataSetConfig(DataSetConfig dataSetConfig) {
        if (dataSetConfig == null || (!dataSetConfig.hasDataSets() && !dataSetConfig.hasDataSetProvider())) {
            throw new RuntimeException("Invalid dataset configuration. You must provide at least one dataset or dataset provider.");
        }
    }

}

@@ -509,24 +509,36 @@
  static 
+ThreadLocal%3C
 RiderDSL
  INSTANC
@@ -529,16 +529,17 @@
 RiderDSL
+%3E
  INSTANC
@@ -657,17 +657,16 @@
 onfig;%0A%0A
-%0A
     /**%0A
@@ -1310,32 +1310,45 @@
  private static 
+synchronized 
 void createInsta
@@ -1414,16 +1414,62 @@
 E = new 
+ThreadLocal%3C%3E();%0A            INSTANCE.set(new 
 RiderDSL
@@ -1466,24 +1466,25 @@
 w RiderDSL()
+)
 ;%0A        %7D%0A
@@ -1815,32 +1815,38 @@
        INSTANCE.
+get().
 connection = con
@@ -2105,16 +2105,22 @@
 NSTANCE.
+get().
 connecti
@@ -2564,32 +2564,38 @@
        INSTANCE.
+get().
 dataSetConfig = 
@@ -2949,16 +2949,22 @@
 NSTANCE.
+get().
 dataSetC
@@ -3292,24 +3292,72 @@
         
-INSTANCE
+RiderDSL riderDSL = INSTANCE.get();%0A            riderDSL
 .dbUnitC
@@ -3397,24 +3397,24 @@
  return 
-INSTANCE
+riderDSL
 ;%0A      
@@ -3705,16 +3705,22 @@
 NSTANCE.
+get().
 createDa

Use ThreadLocal to avoid concurrency issues