/*
 * Copyright 2010 Proofpoint, Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package io.airlift.dbpool;

import com.google.common.base.Charsets;
import com.google.common.base.Preconditions;
import com.google.common.io.Resources;
import com.google.inject.Inject;
import io.airlift.dbpool.H2EmbeddedDataSourceConfig.Cipher;
import org.h2.jdbcx.JdbcDataSource;
import org.h2.util.ScriptReader;

import javax.sql.PooledConnection;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.Reader;
import java.net.URL;
import java.sql.Connection;
import java.sql.SQLException;
import java.sql.Statement;

import static java.lang.Math.ceil;
import static java.util.concurrent.TimeUnit.SECONDS;

public class H2EmbeddedDataSource extends ManagedDataSource
{
    private final JdbcDataSource dataSource;

    @Inject
    public H2EmbeddedDataSource(H2EmbeddedDataSourceConfig config)
            throws Exception
    {
        super(config.getMaxConnections(), config.getMaxConnectionWait());

        Preconditions.checkNotNull(config.getFilename());
        if (config.getFilename().isEmpty()) {
            throw new IllegalArgumentException("filename is empty");
        }

        // build jdbc url connection string
        StringBuilder jdbcUrlBuilder = new StringBuilder()
                .append("jdbc:h2:").append(config.getFilename())
                .append(";ALLOW_LITERALS=").append(config.getAllowLiterals())
                .append(";CACHE_SIZE=").append(config.getCacheSize());

        if (config.getCipher() != Cipher.NONE) {
            jdbcUrlBuilder.append(";CIPHER=").append(config.getCipher());
        }

        String jdbcUrl = jdbcUrlBuilder.toString();

        // create dataSource
        dataSource = new JdbcDataSource();
        dataSource.setURL(jdbcUrl);
        dataSource.setUser("sa");
        if (config.getCipher() != Cipher.NONE) {
            dataSource.setPassword(config.getFilePassword() + " ");
        }
        else {
            dataSource.setPassword("");
        }
        dataSource.setLoginTimeout((int) ceil(config.getMaxConnectionWait().convertTo(SECONDS)));

        // connect to database and initialize database
        Connection connection = getConnection();
        try {
            setConfig(connection, "CACHE_SIZE", config.getCacheSize());
            setConfig(connection, "COMPRESS_LOB", config.getCompressLob());
            setConfig(connection, "DB_CLOSE_DELAY ", "-1");

            // find init script
            String fileName = config.getInitScript();
            File file = new File(fileName);
            URL url;
            if (file.exists()) {
                url = file.toURI().toURL();
            }
            else {
                url = getClass().getClassLoader().getResource(fileName);
            }

            if (url == null) {
                throw new FileNotFoundException(fileName);
            }

            // execute init script
            Reader reader = Resources.newReaderSupplier(url, Charsets.UTF_8).getInput();
            try {
                ScriptReader scriptReader = new ScriptReader(reader);
                for (String statement = scriptReader.readStatement(); statement != null; statement = scriptReader.readStatement()) {
                    executeCommand(connection, statement);
                }
            }
            finally {
                reader.close();
            }

            // run last so script can contain literals
            setConfig(connection, "ALLOW_LITERALS", config.getAllowLiterals());
        }
        finally {
            closeQuietly(connection);
        }
    }

    @Override
    protected PooledConnection createConnectionInternal()
            throws SQLException
    {
        return dataSource.getPooledConnection();
    }

    private static void setConfig(Connection connection, String name, Object value)
            throws SQLException
    {
        Statement statement = connection.createStatement();
        try {
            String command = String.format("SET %s %s", name, value);
            int count = statement.executeUpdate(command);
            if (count != 0) {
                throw new SQLException("Failed to execute command: " + command);
            }
        }
        finally {
            closeQuietly(statement);
        }
    }


    private static void executeCommand(Connection connection, String command)
            throws SQLException
    {
        Statement statement = connection.createStatement();
        try {
            statement.executeUpdate(command);
        }
        finally {
            closeQuietly(statement);
        }
    }

    private static void closeQuietly(Statement statement)
    {
        try {
            statement.close();
        }
        catch (SQLException ignored) {
        }
    }

    private static void closeQuietly(Connection connection)
    {
        try {
            connection.close();
        }
        catch (SQLException ignored) {
        }
    }
}

@@ -2986,12 +2986,14 @@
  // 
-find
+handle
  ini
@@ -3051,24 +3051,100 @@
 itScript();%0A
+            if (fileName != null) %7B%0A                // find init script%0A    
             
@@ -3187,16 +3187,20 @@
         
+ 
+   
 URL url;
@@ -3200,16 +3200,20 @@
 RL url;%0A
+    
         
@@ -3249,24 +3249,28 @@
             
+    
 url = file.t
@@ -3289,34 +3289,42 @@
 ();%0A            
+  
-%7D%0A
+  %7D%0A    
             else
@@ -3318,32 +3318,36 @@
          else %7B%0A
+    
                 
@@ -3407,35 +3407,43 @@
 e);%0A            
+  
+  
 %7D%0A%0A
+    
             if (
@@ -3449,32 +3449,36 @@
 (url == null) %7B%0A
+    
                 
@@ -3524,35 +3524,43 @@
 e);%0A            
+   
+ 
 %7D%0A%0A
+    
             // e
@@ -3590,16 +3590,25 @@
         
+    try (
 Reader r
@@ -3674,17 +3674,19 @@
 tInput()
-;
+) %7B
 %0A       
@@ -3684,32 +3684,24 @@
 %7B%0A          
-  try %7B%0A
             
@@ -3698,22 +3698,16 @@
         
-      
 ScriptRe
@@ -3752,16 +3752,20 @@
 eader);%0A
+    
         
@@ -3909,16 +3909,20 @@
         
+  
+  
 executeC
@@ -3956,34 +3956,24 @@
 t);%0A        
-        %7D%0A
             
@@ -3990,49 +3990,13 @@
     
-finally %7B%0A                reader.close();
+    %7D
 %0A   

Allow not having an init script