package ru.yandex.wmtools.common.util;

import java.math.BigInteger;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Collection;
import java.util.Date;

/**
 * User: amima, baton
 * Date: 11.04.2007
 * Time: 16:45:08
 * To change this template use File | Settings | File Templates.
 */
public class SqlUtil {
    public abstract static class ListParameterizer<T> {
        public abstract String getParameter(int i, T obj);

        public abstract int getParamNumber();

        public boolean isBracketsNeeded() {
            return getParamNumber() > 1;
        }

        public boolean isQuotesNeeded(int i) {
            return false;
        }
    }

    private static class SimpleListParameterizer<T> extends ListParameterizer<T> {
        @Override
        public String getParameter(int i, T obj) {
            return obj.toString();
        }

        @Override
        public int getParamNumber() {
            return 1;
        }
    }

    /**
     * Returns <code>count</code> question marks, separated by commas.
     *
     * @param count ...  Must be > 0
     * @return ...
     */
    public static String createQuestionMarks(int count) {
        StringBuilder sb = new StringBuilder();
        sb.append("?");
        for (int i = 1; i < count; i++) {
            sb.append(", ?");
        }
        return sb.toString();
    }

    public static String createConstList(int count, String value) {
        StringBuilder sb = new StringBuilder();
        sb.append(value);
        for (int i = 1; i < count; i++) {
            sb.append(", ").append(value);
        }
        return sb.toString();
    }

    public static String createQuestionMarkGroups(int groupsCount, int parsCount) {
        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < groupsCount; i++) {
            if (i == 0) {
                sb.append("(?");
            } else {
                sb.append(",(?");
            }
            for (int j = 1; j < parsCount; j++) {
                sb.append(",?");
            }
            sb.append(")");
        }
        return sb.toString();
    }

    public static Object[] prepareFirstParams(int[] paramsArray, Object... firstParams) {
        Object[] params = new Object[paramsArray.length + firstParams.length];

        System.arraycopy(firstParams, 0, params, 0, firstParams.length);

        for (int i = 0; i < paramsArray.length; i++) {
            params[i + firstParams.length] = paramsArray[i];
        }
        return params;
    }

    public static Object[] prepareLastParams(int[] paramsArray, Object... lastParams) {
        Object[] params = new Object[paramsArray.length + lastParams.length];

        for (int i = 0; i < paramsArray.length; i++) {
            params[i] = paramsArray[i];
        }

        System.arraycopy(lastParams, 0, params, paramsArray.length, lastParams.length);

        return params;
    }

    public static Date safeGetTimestamp(ResultSet rs, String field) {
        try {
            return rs.getTimestamp(field);
        } catch (SQLException e) {
            return null;
        }
    }

    public static Date safeGetTimestamp(ResultSet rs, int columnIndex) {
        try {
            return rs.getTimestamp(columnIndex);
        } catch (SQLException e) {
            return null;
        }
    }

    public static <T> String getCommaSeparatedList(Collection<T> list) {
        return getCommaSeparatedList(list, new SimpleListParameterizer<T>());
    }

    public static <T> String getCommaSeparatedList(Collection<T> list, ListParameterizer<T> parameterizer) {
        StringBuilder result = new StringBuilder();
        boolean firstObj = true;
        for (T obj : list) {
            if (!firstObj) {
                result.append(",");
            }
            firstObj = false;

            if (parameterizer.isBracketsNeeded()) {
                result.append("(");
            }

            boolean firstParam = true;
            for (int i = 0; i < parameterizer.getParamNumber(); i++) {
                String param = parameterizer.getParameter(i, obj);
                if (!firstParam) {
                    result.append(",");
                }

                if (parameterizer.isQuotesNeeded(i)) {
                    result.append("'");
                }
                result.append(param);
                if (parameterizer.isQuotesNeeded(i)) {
                    result.append("'");
                }

                firstParam = false;
            }

            if (parameterizer.isBracketsNeeded()) {
                result.append(")");
            }
        }

        return result.toString();
    }

    /**
     * Returns a part of "WHERE" clause which filters rows by datetime interval.
     * NOTE! boundaries of datetime interval are not included
     *
     * @param timeFilter Object that stores information about time filtering.
     * @param fieldName  Database column name in query.
     * @return Returns a part of "WHERE" clause which filters rows by datetime interval.
     */
    public static String getTimeFilterPart(TimeFilter timeFilter, String fieldName) {
        return getTimeFilterPart(timeFilter, fieldName, false, false);
    }

    /**
     * Returns a part of "WHERE" clause which filters rows by datetime interval.
     *
     * @param timeFilter        Object that stores information about time filtering.
     * @param fieldName         Database column name in query.
     * @param includeLeftBound  Include left boundary of the given datetime interval
     * @param includeRightBound Include right boundary of the given datetime interval
     * @return Returns a part of "WHERE" clause which filters rows by datetime interval.
     */
    public static String getTimeFilterPart(TimeFilter timeFilter,
                                           String fieldName,
                                           boolean includeLeftBound,
                                           boolean includeRightBound) {
        if (timeFilter == null) {
            return "";
        }

        StringBuilder builder = new StringBuilder();

        if (timeFilter.getFromInMilliseconds() != null) {
            builder.append(" AND ");
            builder.append(fieldName);
            if (includeLeftBound) {
                builder.append(" >= ");
            } else {
                builder.append(" > ");
            }
            builder.append("'");
            builder.append(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date(timeFilter.getFromInMilliseconds())));
            builder.append("' ");
        }

        if (timeFilter.getToInMilliseconds() != null) {
            builder.append(" AND ");
            builder.append(fieldName);
            if (includeRightBound) {
                builder.append(" <= ");
            } else {
                builder.append(" < ");
            }
            builder.append("'");
            builder.append(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date(timeFilter.getToInMilliseconds())));
            builder.append("' ");
        }

        builder.append(" ");

        return builder.toString();
    }

    public static void alignToMidnight(Calendar cal){
        cal.set(Calendar.HOUR_OF_DAY, 0);
        cal.set(Calendar.MINUTE, 0);
        cal.set(Calendar.SECOND, 0);
        cal.set(Calendar.MILLISECOND, 0);
    }

    public static Date getMidnightTimestamp(Date date) {
        Calendar cal = Calendar.getInstance();
        cal.setTime((date == null) ? new Date() : date);

        alignToMidnight(cal);

        return cal.getTime();
    }

    public static Long getLongNullable(ResultSet rs, String paramName) throws SQLException {
        return getLong(rs, paramName, null);
    }

    public static Integer getIntNullable(ResultSet rs, String paramName) throws SQLException {
        return getInt(rs, paramName, null);
    }

    public static String getStringNullable(ResultSet rs, String paramName) throws SQLException {
        return getString(rs, paramName, null);
    }

    public static Byte getByteNullable(ResultSet rs, String paramName) throws SQLException {
        return getByte(rs, paramName, null);
    }

    public static Boolean getBooleanNullable(ResultSet rs, String paramName) throws SQLException {
        return getBoolean(rs, paramName, null);
    }

    public static Date getDateNullable(ResultSet rs, String paramName) throws SQLException {
        return getDate(rs, paramName, null);
    }

    public static BigInteger getBigIntegerNullable(ResultSet rs, String paramName) throws SQLException {
        return getBigInteger(rs, paramName, null);
    }

    public static BigInteger getBigInteger(ResultSet rs, String paramName) throws SQLException {
        return getBigInteger(rs, paramName, BigInteger.ZERO);
    }

    public static Long getLong(ResultSet rs, String paramName, Long defaultValue) throws SQLException {
        Long res = rs.getLong(paramName);
        return rs.wasNull() ? defaultValue : res;
    }

    public static Integer getInt(ResultSet rs, String paramName, Integer defaultValue) throws SQLException {
        Integer res = rs.getInt(paramName);
        return rs.wasNull() ? defaultValue : res;
    }

    public static String getString(ResultSet rs, String paramName, String defaultValue) throws SQLException {
        String res = rs.getString(paramName);
        return rs.wasNull() ? defaultValue : res;
    }

    public static Byte getByte(ResultSet rs, String paramName, Byte defaultValue) throws SQLException {
        Byte res = rs.getByte(paramName);
        return rs.wasNull() ? defaultValue : res;
    }

    public static Boolean getBoolean(ResultSet rs, String paramName, Boolean defaultValue) throws SQLException {
        Boolean res = rs.getBoolean(paramName);
        return rs.wasNull() ? defaultValue : res;
    }

    public static Date getDate(ResultSet rs, String paramName, Date defaultValue) throws SQLException {
        Date res = rs.getDate(paramName);
        return rs.wasNull() ? defaultValue : res;
    }

    public static BigInteger getBigInteger(ResultSet rs, String paramName, BigInteger defaultValue) throws SQLException {
        String val = rs.getString(paramName);
        if (rs.wasNull()) {
            return defaultValue;
        }
        return new BigInteger(val);
    }
}
