package enkan.component.doma2;

import enkan.component.ComponentLifecycle;
import enkan.component.DataSourceComponent;
import enkan.component.SystemComponent;
import enkan.exception.MisconfigurationException;
import org.seasar.doma.jdbc.Config;
import org.seasar.doma.jdbc.ConfigProvider;
import org.seasar.doma.jdbc.SqlFileRepository;
import org.seasar.doma.jdbc.dialect.Dialect;
import org.seasar.doma.jdbc.dialect.StandardDialect;

import javax.sql.DataSource;
import java.lang.reflect.Constructor;
import java.util.concurrent.ConcurrentHashMap;

import static enkan.util.ReflectionUtils.*;

/**
 * @author kawasima
 */
public class DomaProvider extends SystemComponent {
    private DataSource dataSource;
    private ConcurrentHashMap<String, Object> daoCache = new ConcurrentHashMap<>();
    private Config defaultConfig;
    private Dialect dialect;

    /**
     * Gets the DAO.
     *
     * @param daoInterface a type of DAO
     * @param <T> a type of DAO
     * @return a DAO of the given class.
     */
    public <T> T getDao(Class<? extends T> daoInterface) {
        return (T) daoCache.computeIfAbsent(daoInterface.getName(), key ->
                tryReflection(() -> {
                    Class<? extends T> daoClass;
                    try {
                         daoClass = (Class<? extends T>) Class.forName(daoInterface.getName() + "Impl", true, daoInterface.getClassLoader());
                    } catch (ClassNotFoundException ex) {
                        throw new MisconfigurationException("doma2.DAO_IMPL_NOT_FOUND", daoInterface.getName(), ex);
                    }
                    try {
                        Constructor<? extends T> daoConstructor = daoClass.getConstructor(DataSource.class);
                        return daoConstructor.newInstance(dataSource);
                    } catch (NoSuchMethodException e) {
                        Constructor<? extends T> daoConstructor = daoClass.getConstructor(Config.class);
                        return daoConstructor.newInstance(defaultConfig);
                    }

            }));
    }

    @Override
    protected ComponentLifecycle lifecycle() {
        return new ComponentLifecycle<DomaProvider>() {
            @Override
            public void start(DomaProvider component) {
                DataSourceComponent dataSourceComponent = getDependency(DataSourceComponent.class);
                component.dataSource = dataSourceComponent.getDataSource();
                if (component.dialect == null) component.dialect = new StandardDialect();
                component.defaultConfig = new Config() {
                    @Override
                    public DataSource getDataSource() {
                        return component.dataSource;
                    }

                    @Override
                    public Dialect getDialect() {
                        return component.dialect;
                    }
                };
            }

            @Override
            public void stop(DomaProvider component) {
                component.dataSource = null;
                daoCache.values().stream()
                        .filter(ConfigProvider.class::isInstance)
                        .map(ConfigProvider.class::cast)
                        .map(ConfigProvider::getConfig)
                        .map(Config::getSqlFileRepository)
                        .forEach(SqlFileRepository::clearCache);
                daoCache.clear();
            }
        };
    }

    public void setDialect(Dialect dialect) {
        this.dialect = dialect;
    }
}

@@ -282,16 +282,52 @@
 ovider;%0A
+import org.seasar.doma.jdbc.Naming;%0A
 import o
@@ -365,16 +365,16 @@
 sitory;%0A
-
 import o
@@ -886,17 +886,185 @@
 ialect;%0A
+    private Naming naming = Naming.DEFAULT;%0A    private int maxRows = 0;%0A    private int fetchSize = 0;%0A    private int queryTimeout = 0;%0A    private int batchSize = 0;
 %0A
-
     /**%0A
@@ -2801,24 +2801,174 @@
  Config() %7B%0A
+                    @Override%0A                    public Naming getNaming() %7B%0A                        return component.naming;%0A                    %7D%0A%0A
             
@@ -3260,32 +3260,646 @@
                %7D
+%0A%0A                    @Override%0A                    public int getMaxRows() %7B%0A                        return component.maxRows;%0A                    %7D%0A%0A                    @Override%0A                    public int getFetchSize() %7B%0A                        return component.fetchSize;%0A                    %7D%0A%0A                    @Override%0A                    public int getQueryTimeout() %7B%0A                        return component.queryTimeout;%0A                    %7D%0A%0A                    @Override%0A                    public int getBatchSize() %7B%0A                        return component.batchSize;%0A                    %7D
 %0A               
@@ -4493,24 +4493,24 @@
  dialect) %7B%0A
-
         this
@@ -4534,11 +4534,451 @@
 t;%0A    %7D
+%0A%0A    public void setNaming(Naming naming) %7B%0A        this.naming = naming;%0A    %7D%0A%0A    public void setMaxRows(int maxRows) %7B%0A        this.maxRows = maxRows;%0A    %7D%0A%0A    public void setFetchSize(int fetchSize) %7B%0A        this.fetchSize = fetchSize;%0A    %7D%0A%0A    public void setQueryTimeout(int queryTimeout) %7B%0A        this.queryTimeout = queryTimeout;%0A    %7D%0A%0A    public void setBatchSize(int batchSize) %7B%0A        this.batchSize = batchSize;%0A    %7D
 %0A%7D%0A

Add some option for doma configuration