/*
 * blanco Framework
 * Copyright (C) 2004-2005 IGA Tosiki
 * 
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 */
package blanco.db.mapping;

import java.io.ByteArrayInputStream;
import java.io.CharArrayReader;
import java.math.BigDecimal;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.sql.Timestamp;
import java.sql.Types;

public final class BlancoDbMappingUtilDotNet {
    /**
     * Java型名をきっかけに、ResultSetに対するゲッターメソッド名を取得します。
     * 
     * @author tosiki iga
     * @since 2005.05.03
     * @param csTypeName
     *            C#上の型名
     * @return
     */
    public static final String getResultSetGetterMethodName(
            final String csTypeName) {
        String setAccessorName = "";
        if (csTypeName.equals("int")) {
            setAccessorName = "GetInt32";
        } else if (csTypeName.equals("byte")) {
            setAccessorName = "GetByte";
        } else if (csTypeName.equals("short")) {
            setAccessorName = "GetInt16";
        } else if (csTypeName.equals("long")) {
            setAccessorName = "GetInt64";
        } else if (csTypeName.equals("bool")) {
            setAccessorName = "GetBoolean";
        } else if (csTypeName.equals("float")) {
            setAccessorName = "GetFloat";
        } else if (csTypeName.equals("double")) {
            setAccessorName = "GetDouble";
        } else if (csTypeName.equals("string")) {
            setAccessorName = "GetString";
        } else if (csTypeName.equals("DateTime")) {
            setAccessorName = "GetDateTime";
        } else if (csTypeName.equals("decimal")) {
            setAccessorName = "GetDecimal";
        } else if (csTypeName.equals("Integer")) {
            setAccessorName = "GetInt32";
        } else if (csTypeName.equals("Byte")) {
            setAccessorName = "GetByte";
        } else if (csTypeName.equals("Short")) {
            setAccessorName = "GetShort";
        } else if (csTypeName.equals("Long")) {
            setAccessorName = "GetInt64";
        } else if (csTypeName.equals("Boolean")) {
            setAccessorName = "GetBoolean";
        } else if (csTypeName.equals("Float")) {
            setAccessorName = "GetFloat";
        } else if (csTypeName.equals("Double")) {
            setAccessorName = "GetDouble";
        } else if (csTypeName.equals("byte[]")) {
            setAccessorName = "GetBinaryStream";
        } else if (csTypeName.equals("Reader")) {
            setAccessorName = "GetCharacterStream";
        } else {
            throw new IllegalArgumentException("ゲッターを取得する処理で、型[" + csTypeName
                    + "]に対応するゲッターの解析に失敗しました。");
        }

        return setAccessorName;
    }

    /**
     * 適当な値でpreparedStatementにセットを行います。 <br>
     * TODO: 適当な値というのが、0でよいのかどうかは再考の必要あり。1の可能性が高いです。
     * 
     * @throws SQLException
     */
    public static final void processPreparedStatementWithSomeValue(
            final String javaTypeName, final PreparedStatement stmt,
            final int columnId) throws SQLException {
        if (javaTypeName.equals("int")) {
            stmt.setInt(columnId, 0);
        } else if (javaTypeName.equals("byte")) {
            stmt.setByte(columnId, (byte) 0x00);
        } else if (javaTypeName.equals("short")) {
            stmt.setShort(columnId, (short) 0);
        } else if (javaTypeName.equals("long")) {
            stmt.setLong(columnId, 0);
        } else if (javaTypeName.equals("bool")) {
            stmt.setBoolean(columnId, false);
        } else if (javaTypeName.equals("float")) {
            stmt.setFloat(columnId, 0);
        } else if (javaTypeName.equals("double")) {
            stmt.setDouble(columnId, 0);
        } else if (javaTypeName.equals("string")) {
            // SQL Server 2005のuniqueidentifierの場合に文字列を入れると
            // 文字列変換エラーが発生するため、ここではnullを代入しています。
            stmt.setString(columnId, null);
        } else if (javaTypeName.equals("DateTime")) {
            stmt.setTimestamp(columnId, new Timestamp(0));
        } else if (javaTypeName.equals("decimal")) {
            // BigDecimal(int)は J2SE 1.5から導入されたメソッドです。
            // "0"と明示的に文字列として指定しています。
            stmt.setBigDecimal(columnId, new BigDecimal("0"));
        } else if (javaTypeName.equals("Integer")) {
            stmt.setInt(columnId, 0);
        } else if (javaTypeName.equals("Byte")) {
            stmt.setByte(columnId, (byte) 0x00);
        } else if (javaTypeName.equals("Short")) {
            stmt.setShort(columnId, (short) 0);
        } else if (javaTypeName.equals("Long")) {
            stmt.setLong(columnId, 0);
        } else if (javaTypeName.equals("Boolean")) {
            stmt.setBoolean(columnId, false);
        } else if (javaTypeName.equals("Float")) {
            stmt.setFloat(columnId, 0);
        } else if (javaTypeName.equals("Double")) {
            stmt.setDouble(columnId, 0);
        } else if (javaTypeName.equals("byte[]")) {
            byte[] dummy = "0".getBytes();
            stmt.setBinaryStream(columnId, new ByteArrayInputStream(dummy),
                    dummy.length);
        } else if (javaTypeName.equals("Reader")) {
            char[] dummy = new char[1];
            dummy[0] = '0';
            stmt.setCharacterStream(columnId, new CharArrayReader(dummy),
                    dummy.length);
        } else {
            throw new IllegalArgumentException("ダミー値をバインドする処理において、型["
                    + javaTypeName + "]に対応するセッターの解析に失敗しました。");
        }
    }

    /**
     * ラッパークラス対応が必要かどうかを判定します。
     * 
     * @param javaTypeName
     *            短いJava言語上の名称 例：Integer
     * @return
     */
    public static final boolean isMapWrapperClassNecessity(
            final String javaTypeName) {
        if (javaTypeName.equals("Integer") || javaTypeName.equals("Byte")
                || javaTypeName.equals("Short") || javaTypeName.equals("Long")
                || javaTypeName.equals("Float")
                || javaTypeName.equals("Double")
                || javaTypeName.equals("Boolean")
                || javaTypeName.equals("Date")) {
            return true;
        }
        return false;
    }

    public final static String convertJdbcTypeToString(final int jdbcType) {
        final String TYPES_BASE = "java.sql.Types.";
        switch (jdbcType) {
        case Types.BIT:
            return TYPES_BASE + "BIT";
        case Types.TINYINT:
            return TYPES_BASE + "TINYINT";
        case Types.SMALLINT:
            return TYPES_BASE + "SMALLINT";
        case Types.INTEGER:
            return TYPES_BASE + "INTEGER";
        case Types.BIGINT:
            return TYPES_BASE + "BIGINT";
        case Types.FLOAT:
            return TYPES_BASE + "FLOAT";
        case Types.REAL:
            return TYPES_BASE + "REAL";
        case Types.DOUBLE:
            return TYPES_BASE + "DOUBLE";
        case Types.NUMERIC:
            return TYPES_BASE + "NUMERIC";
        case Types.DECIMAL:
            return TYPES_BASE + "DECIMAL";
        case Types.CHAR:
            return TYPES_BASE + "CHAR";
        case Types.VARCHAR:
            return TYPES_BASE + "VARCHAR";
        case Types.LONGVARCHAR:
            return TYPES_BASE + "LONGVARCHAR";
        case Types.DATE:
            return TYPES_BASE + "DATE";
        case Types.TIME:
            return TYPES_BASE + "TIME";
        case Types.TIMESTAMP:
            return TYPES_BASE + "TIMESTAMP";
        case Types.BINARY:
            return TYPES_BASE + "BINARY";
        case Types.VARBINARY:
            return TYPES_BASE + "VARBINARY";
        case Types.LONGVARBINARY:
            return TYPES_BASE + "LONGVARBINARY";
        case Types.NULL:
            return TYPES_BASE + "NULL";
        case Types.OTHER:
            return TYPES_BASE + "OTHER";
        case Types.JAVA_OBJECT:
            return TYPES_BASE + "JAVA_OBJECT";
        case Types.DISTINCT:
            return TYPES_BASE + "DISTINCT";
        case Types.STRUCT:
            return TYPES_BASE + "STRUCT";
        case Types.ARRAY:
            return TYPES_BASE + "ARRAY";
        case Types.BLOB:
            return TYPES_BASE + "BLOB";
        case Types.CLOB:
            return TYPES_BASE + "CLOB";
        case Types.REF:
            return TYPES_BASE + "REF";
        case Types.DATALINK:
            // since 1.4
            return TYPES_BASE + "DATALINK";
        case Types.BOOLEAN:
            // since 1.4
            return TYPES_BASE + "BOOLEAN";
        default:
            // 判定不能
            throw new IllegalArgumentException("型名[" + jdbcType + "]は未サポートです。");
        }
    }

    /**
     * 言語上の型からADO.NETのSqlDbType型へのマッピングを行います。
     * 
     * @param javaFullType
     * @return
     */
    public final static String convertJavaTypeToJdbcType(
            final String javaFullType) {
        final String TYPES_BASE = "SqlDbType.";
        if (javaFullType.equals("bool")) {
            return TYPES_BASE + "Bit";
        } else if (javaFullType.equals("byte")) {
            return TYPES_BASE + "TinyInt";
        } else if (javaFullType.equals("short")) {
            return TYPES_BASE + "SmallInt";
        } else if (javaFullType.equals("int")) {
            return TYPES_BASE + "Int";
        } else if (javaFullType.equals("long")) {
            return TYPES_BASE + "BigInt";
        } else if (javaFullType.equals("float")) {
            return TYPES_BASE + "Real";
        } else if (javaFullType.equals("double")) {
            return TYPES_BASE + "Float";
        } else if (javaFullType.equals("decimal")) {
            return TYPES_BASE + "Decimal";
        } else if (javaFullType.equals("string")) {
            return TYPES_BASE + "VarChar";
        } else if (javaFullType.equals("System.DateTime")) {
            return TYPES_BASE + "DateTime";
        } else if (javaFullType.equals("byte[]")) {
            return TYPES_BASE + "Image";
        } else {
            throw new IllegalArgumentException("型名[" + javaFullType
                    + "]は未サポートです。");
        }
    }

    /**
     * 与えられたプログラミング言語上の型がnullを設定可能かどうかについて調べます。
     * 
     * C#.NETのstring型はnullを設定可能です。decimalやSystem.DateTimeはnullを設定不可です。
     * 
     * @param arg
     *            プログラミング言語上の型。
     * @return trueならnullが設定可能。falseならnullは設定不可能。
     */
    public static final boolean isNullable(final String arg) {
        if ("byte".equals(arg) || "bool".equals(arg) || "short".equals(arg)
                || "int".equals(arg) || "long".equals(arg)
                || "decimal".equals(arg) || "float".equals(arg)
                || "double".equals(arg) || "System.DateTime".equals(arg)) {
            return false;
        }
        return true;
    }
}
