package jp.sourceforge.glad.jdbc.iteration;

import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.Reader;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.List;

import jp.sourceforge.glad.collection.closure.ClosureSupport;
import jp.sourceforge.glad.collection.exception.CheckedException;
import jp.sourceforge.glad.collection.exception.ReturnException;
import jp.sourceforge.glad.collection.iteration.IterationHandler;
import jp.sourceforge.glad.collection.iteration.IterationHandlerAdapter;
import jp.sourceforge.glad.jdbc.h2.H2Database;
import junit.framework.TestCase;

import org.h2.tools.RunScript;

public class ResultSetIterationHandlerTest extends TestCase {

    static volatile boolean initialized = false;

    ResultSetIterationHandler handler;
    H2Database database;
    Connection connection;
    Statement statement;
    ResultSet resultSet;

    protected void setUp() throws Exception {
        database = new H2Database();
        connection = database.getConnection();
        initialize(connection);
        statement = connection.createStatement();
        resultSet = statement.executeQuery(
                "SELECT ID, DATA FROM RESULT_SET_ITERATION_HANDLER_TEST ORDER BY ID");
        handler = new ResultSetIterationHandler(resultSet);
    }

    protected void tearDown() throws Exception {
        handler = null;
        resultSet = null;
        try {
            statement.close();
        } finally {
            statement = null;
            try {
                connection.close();
            } finally {
                connection = null;
                database = null;
            }
        }
    }

    void initialize(Connection connection) throws Exception {
        Reader reader = loadResource("ResultSetIterationHandlerTest_SETUP.sql");
        try {
            RunScript.execute(connection, reader);
        } finally {
            reader.close();
        }
    }

    Reader loadResource(String name) {
        InputStream is = getClass().getResourceAsStream(name);
        return new InputStreamReader(is);
    }

    public void testIterate() throws Exception {
        handler = new ResultSetIterationHandler(resultSet, false);
        try {
            final List<String> list = new ArrayList<String>();
            handler.iterate(new ClosureSupport<ResultSet>() {
                public void execute(ResultSet each) throws SQLException {
                    list.add(each.getString("DATA"));
                }
            });
            assertEquals(3, list.size());
            assertEquals("[a, c, b]", list.toString());
        } finally {
            resultSet.close();
        }
    }

    public void testIterateAndClose() throws Exception {
        final List<String> list = new ArrayList<String>();
        handler.iterate(new ClosureSupport<ResultSet>() {
            public void execute(ResultSet each) throws SQLException {
                list.add(each.getString("DATA"));
            }
        });
        assertEquals(3, list.size());
        assertEquals("[a, c, b]", list.toString());
    }

    public void testContinue() throws Exception {
        final List<String> list = new ArrayList<String>();
        handler.iterate(new ClosureSupport<ResultSet>() {
            public void execute(ResultSet each) throws SQLException {
                if (each.getInt("ID") == 2) {
                    doContinue();
                }
                list.add(each.getString("DATA"));
            }
        });
        assertEquals(2, list.size());
        assertEquals("[a, b]", list.toString());
    }

    public void testBreak() throws Exception {
        final List<String> list = new ArrayList<String>();
        handler.iterate(new ClosureSupport<ResultSet>() {
            public void execute(ResultSet each) throws SQLException {
                if (each.getInt("ID") == 2) {
                    doBreak();
                }
                list.add(each.getString("DATA"));
            }
        });
        assertEquals(1, list.size());
        assertEquals("[a]", list.toString());
    }

    public void testRetuen() throws Exception {
        final List<String> list = new ArrayList<String>();
        try {
            handler.iterate(new ClosureSupport<ResultSet>() {
                public void execute(ResultSet each) throws SQLException {
                    if (each.getInt("ID") == 2) {
                        doReturn(-1);
                    }
                    list.add(each.getString("DATA"));
                }
            });
            fail();
        } catch (ReturnException e) {
            assertEquals(-1, e.getResult());
        }
        assertEquals(1, list.size());
        assertEquals("[a]", list.toString());
    }

    public void testRuntimeException() throws Exception {
        final List<String> list = new ArrayList<String>();
        try {
            handler.iterate(new ClosureSupport<ResultSet>() {
                public void execute(ResultSet each) throws SQLException {
                    if (each.getInt("ID") == 2) {
                        throw new RuntimeException("test");
                    }
                    list.add(each.getString("DATA"));
                }
            });
            fail();
        } catch (RuntimeException e) {
            assertEquals("test", e.getMessage());
        }
        assertEquals(1, list.size());
        assertEquals("[a]", list.toString());
    }

    public void testCheckedException() throws Exception {
        final List<String> list = new ArrayList<String>();
        try {
            handler.iterate(new ClosureSupport<ResultSet>() {
                public void execute(ResultSet each) throws Exception {
                    if (each.getInt("ID") == 2) {
                        throw new Exception("test");
                    }
                    list.add(each.getString("DATA"));
                }
            });
            fail();
        } catch (CheckedException e) {
            assertEquals("test", e.getCause().getMessage());
        }
        assertEquals(1, list.size());
        assertEquals("[a]", list.toString());
    }

    public void testConvert() throws Exception {
        IterationHandler<Data> handler =
                new IterationHandlerAdapter<ResultSet, Data>(
                        new ResultSetIterationHandler(resultSet)) {
            @Override
            protected Data convert(ResultSet resultSet) throws SQLException {
                return getData(resultSet);
            }
        };
        final List<Data> list = new ArrayList<Data>();
        handler.iterate(new ClosureSupport<Data>() {
            public void execute(Data data) {
                list.add(data);
            }
        });
        assertEquals(3, list.size());
        assertEquals("[Data(1, a), Data(2, c), Data(3, b)]", list.toString());
    }

    static Data getData(ResultSet resultSet) throws SQLException {
        long id = resultSet.getLong("ID");
        String value = resultSet.getString("DATA");
        return new Data(id, value);
    }

    static class Data {
        long id;
        String value;
        public Data(long id, String value) {
            this.id = id;
            this.value = value;
        }
        public long getId() {
            return id;
        }
        public String getValue() {
            return value;
        }
        public String toString() {
            return "Data(" + id + ", " + value + ")";
        }
    }

}
