package org.sfm.jdbc.impl;

import org.sfm.jdbc.JdbcMapper;
import org.sfm.map.*;
import org.sfm.map.impl.DiscriminatorEnumerable;
import org.sfm.tuples.Tuple2;
import org.sfm.tuples.Tuple3;
import org.sfm.utils.*;
import org.sfm.utils.conv.Converter;

import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.List;



public final class DiscriminatorJdbcMapper<T> extends AbstractEnumarableJdbcMapper<T> {


    private final String discriminatorColumn;
    private final List<Tuple2<Predicate<String>, JdbcMapper<T>>> mappers;

    public DiscriminatorJdbcMapper(String discriminatorColumn, List<Tuple2<Predicate<String>, JdbcMapper<T>>> mappers, RowHandlerErrorHandler rowHandlerErrorHandler) {
        super(rowHandlerErrorHandler);
        this.discriminatorColumn = discriminatorColumn;
        this.mappers = mappers;
    }

    private MappingContext<ResultSet>[] getMappingContexts(ResultSet rs) throws SQLException {
        @SuppressWarnings("unchecked")
        MappingContext<ResultSet>[] mappingContexts = new MappingContext[mappers.size()];

        int i = 0;
        for(Tuple2<Predicate<String>, JdbcMapper<T>> tm : mappers) {
            mappingContexts[i] = tm.getElement1().newMappingContext(rs);
            i++;
        }
        return mappingContexts;
    }

    private JdbcMapper<T> getMapper(int index) {
        return mappers.get(index).getElement1();
    }

    @Override
    protected JdbcMapper<T> getMapper(final ResultSet rs) throws MappingException {
        try {
            String value = rs.getString(discriminatorColumn);

            for (Tuple2<Predicate<String>, JdbcMapper<T>> tm : mappers) {
                if (tm.first().test(value)) {
                    return tm.second();
                }
            }
            throw new MappingException("No jdbcMapper found for " + discriminatorColumn + " = " + value);
        } catch(SQLException e) {
            return ErrorHelper.rethrow(e);
        }
    }

    private int getMapperIndex(final ResultSet rs) throws MappingException, SQLException {

        String value = rs.getString(discriminatorColumn);

        int i = 0;
        for(Tuple2<Predicate<String>, JdbcMapper<T>> tm : mappers) {
            if (tm.first().test(value)) {
                return i;
            }
            i++;
        }
		throw new MappingException("No jdbcMapper found for " + discriminatorColumn + " = " + value);
	}


    protected DiscriminatorEnumerable<ResultSet, T> newEnumarableOfT(ResultSet rs) throws SQLException {
        @SuppressWarnings("unchecked") Tuple3<Predicate<ResultSet>, Mapper<ResultSet, T>, MappingContext<ResultSet>>[] mapperDiscriminators =
                new Tuple3[this.mappers.size()];

        for(int i = 0; i < mapperDiscriminators.length; i++) {

            Tuple2<Predicate<String>, JdbcMapper<T>> mapper = mappers.get(i);

            Predicate<ResultSet> discriminatorPredicate = new DiscriminatorPredicate(discriminatorColumn, mapper.first());

            mapperDiscriminators[i] =
                    new Tuple3<Predicate<ResultSet>, Mapper<ResultSet, T>, MappingContext<ResultSet>>(
                            discriminatorPredicate,
                            mapper.second(),
                            mapper.second().newMappingContext(rs));
        }

        return new DiscriminatorEnumerable<ResultSet, T>(
                mapperDiscriminators,
                new ResultSetEnumarable(rs),
                new ErrorMessageConverter(discriminatorColumn));
    }

    @Override
    public String toString() {
        return "DiscriminatorJdbcMapper{" +
                "discriminatorColumn='" + discriminatorColumn + '\'' +
                ", mappers=" + mappers +
                '}';
    }


    private static class DiscriminatorPredicate implements Predicate<ResultSet> {
        private String discriminatorColumn;
        private Predicate<String> predicate;

        public DiscriminatorPredicate(String discriminatorColumn, Predicate<String> predicate) {
            this.discriminatorColumn = discriminatorColumn;
            this.predicate = predicate;
        }

        @Override
        public boolean test(ResultSet resultSet) {
            try {
                return predicate.test(resultSet.getString(discriminatorColumn));
            } catch (SQLException e) {
                ErrorHelper.rethrow(e);
                return false;
            }
        }
    }

    private static class ErrorMessageConverter implements Converter<ResultSet, String> {
        private final String discriminatorColumn;

        private ErrorMessageConverter(String discriminatorColumn) {
            this.discriminatorColumn = discriminatorColumn;
        }

        @Override
        public String convert(ResultSet in) throws Exception {
            return " column " + discriminatorColumn + " = " + in.getObject(discriminatorColumn);
        }
    }
}

@@ -846,565 +846,8 @@
  %7D%0A%0A
-    private MappingContext%3CResultSet%3E%5B%5D getMappingContexts(ResultSet rs) throws SQLException %7B%0A        @SuppressWarnings(%22unchecked%22)%0A        MappingContext%3CResultSet%3E%5B%5D mappingContexts = new MappingContext%5Bmappers.size()%5D;%0A%0A        int i = 0;%0A        for(Tuple2%3CPredicate%3CString%3E, JdbcMapper%3CT%3E%3E tm : mappers) %7B%0A            mappingContexts%5Bi%5D = tm.getElement1().newMappingContext(rs);%0A            i++;%0A        %7D%0A        return mappingContexts;%0A    %7D%0A%0A    private JdbcMapper%3CT%3E getMapper(int index) %7B%0A        return mappers.get(index).getElement1();%0A    %7D%0A%0A
     
@@ -1413,457 +1413,8 @@
  %7D%0A%0A
-    private int getMapperIndex(final ResultSet rs) throws MappingException, SQLException %7B%0A%0A        String value = rs.getString(discriminatorColumn);%0A%0A        int i = 0;%0A        for(Tuple2%3CPredicate%3CString%3E, JdbcMapper%3CT%3E%3E tm : mappers) %7B%0A            if (tm.first().test(value)) %7B%0A                return i;%0A            %7D%0A            i++;%0A        %7D%0A%09%09throw new MappingException(%22No jdbcMapper found for %22 + discriminatorColumn + %22 = %22 + value);%0A%09%7D%0A%0A%0A
     

remove unused code