/*
 * 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.definition;

import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;

import blanco.db.mapping.BlancoDbMappingUtilDotNet;
import blanco.ig.expander.IgType;

/**
 * @author Yasuo Nakanishi
 * @author Tosiki Iga 抜本的に見直しver.1 2005.05.06
 */
public class FieldFactoryDotNet {
    public boolean isSupportedDatabaseType(final ResultSet resultSet)
            throws SQLException {
        return isSupportedDatabaseType(resultSet.getInt("DATA_TYPE"));
    }

    public boolean isSupportedDatabaseType(final int type) {
        boolean result = false;

        switch (type) {
        case java.sql.Types.CHAR:
        case java.sql.Types.VARCHAR:
        case java.sql.Types.LONGVARCHAR:
        case java.sql.Types.DATE:
        case java.sql.Types.TIMESTAMP:
        case java.sql.Types.DECIMAL:
        case java.sql.Types.NUMERIC:
        case java.sql.Types.REAL:
        case java.sql.Types.FLOAT:
        case java.sql.Types.DOUBLE:
        case java.sql.Types.INTEGER:
        case java.sql.Types.BIGINT:
        case java.sql.Types.SMALLINT:
        case java.sql.Types.TINYINT:
        case java.sql.Types.BIT:
        case java.sql.Types.BOOLEAN:
        case java.sql.Types.BINARY:
        case java.sql.Types.VARBINARY:
        case java.sql.Types.LONGVARBINARY:
            result = true;
            break;
        default:
            // サポートしていません。
            break;
        }
        return result;
    }

    /**
     * JDBCの型とJavaの型をマッピングする箇所です。 <br>
     * TODO 将来的に null許容項目と非null項目とで型マップの使い分けなどが必要です。
     */
    public TableFieldDotNet createTableField(final ResultSet resultSet)
            throws SQLException {
        // assert isSupportedDatabaseType(resultSet);

        final int type = resultSet.getInt("DATA_TYPE");
        final String name = resultSet.getString("COLUMN_NAME");

        TableFieldDotNet result = null;

        final int isNullable = resultSet.getInt("NULLABLE");

        switch (type) {
        case java.sql.Types.CHAR:
        case java.sql.Types.VARCHAR:
            result = new TableFieldDotNet(new IgType("string"), name);
            result.setSize(resultSet.getInt("CHAR_OCTET_LENGTH"));
            break;
        case java.sql.Types.LONGVARCHAR:
            // .NET FrameworkではStringにマッピングします。
            result = new TableFieldDotNet(new IgType("string"), name);
            break;
        case java.sql.Types.DATE:
        case java.sql.Types.TIME:
        case java.sql.Types.TIMESTAMP:
            // TODO DATEとTIMESTAMPが紋切り型にDateになるのかどうかの考察が必要。
            result = new TableFieldDotNet(new IgType("System.DateTime"), name);
            break;
        case java.sql.Types.REAL:
            if (isNullable == ResultSetMetaData.columnNoNulls) {
                result = new TableFieldDotNet(new IgType("float"), name);
            } else {
                result = new TableFieldDotNet(new IgType("float"), name);
                result.setObjectMappingForPrimitiveNullSupport(true);
            }
            break;
        case java.sql.Types.FLOAT:
        case java.sql.Types.DOUBLE:
            if (isNullable == ResultSetMetaData.columnNoNulls) {
                result = new TableFieldDotNet(new IgType("double"), name);
            } else {
                result = new TableFieldDotNet(new IgType("double"), name);
                // 不本意ながらプリミティブ型にマップします。
            }
            break;
        case java.sql.Types.DECIMAL:
        case java.sql.Types.NUMERIC:
            // DECIMALとNUMERICとの差異は、Sunのマップ表には現れない
            result = new TableFieldDotNet(new IgType("decimal"), name);
            result.setScale(resultSet.getInt("DECIMAL_DIGITS"));
            break;
        case java.sql.Types.INTEGER:
            if (isNullable == ResultSetMetaData.columnNoNulls) {
                result = new TableFieldDotNet(new IgType("int"), name);
            } else {
                result = new TableFieldDotNet(new IgType("int"), name);
                // 不本意ながらプリミティブ型にマップします。
            }
            break;
        case java.sql.Types.BIGINT:
            if (isNullable == ResultSetMetaData.columnNoNulls) {
                result = new TableFieldDotNet(new IgType("long"), name);
            } else {
                result = new TableFieldDotNet(new IgType("long"), name);
                // 不本意ながらプリミティブ型にマップします。
            }
            break;
        case java.sql.Types.SMALLINT:
            if (isNullable == ResultSetMetaData.columnNoNulls) {
                result = new TableFieldDotNet(new IgType("short"), name);
            } else {
                result = new TableFieldDotNet(new IgType("short"), name);
                // 不本意ながらプリミティブ型にマップします。
            }
            break;
        case java.sql.Types.TINYINT:
            if (isNullable == ResultSetMetaData.columnNoNulls) {
                result = new TableFieldDotNet(new IgType("byte"), name);
            } else {
                result = new TableFieldDotNet(new IgType("byte"), name);
                // 不本意ながらプリミティブ型にマップします。
            }
            break;
        case java.sql.Types.BIT:
        case java.sql.Types.BOOLEAN:
            if (isNullable == ResultSetMetaData.columnNoNulls) {
                result = new TableFieldDotNet(new IgType("bool"), name);
            } else {
                result = new TableFieldDotNet(new IgType("bool"), name);
                // 不本意ながらプリミティブ型にマップします。
            }
            break;
        case java.sql.Types.BINARY:
        case java.sql.Types.VARBINARY:
        case java.sql.Types.LONGVARBINARY:
            result = new TableFieldDotNet(new IgType("byte[]"), name);
            break;
        default:
            throw new IllegalArgumentException("型[" + type + "]("
                    + BlancoDbMappingUtilDotNet.convertJdbcTypeToString(type)
                    + ")はサポートされません。");
        }
        result.setJdbcType(type);
        result.setSize(resultSet.getInt("COLUMN_SIZE"));

        result.setNullable(resultSet.getInt("NULLABLE"));

        return result;
    }

    /**
     * JDBCの型とJavaの型をマッピングする箇所です。 <br>
     * TODO 将来的に null許容項目と非null項目とで型マップの使い分けなどが必要です。
     */
    public QueryFieldDotNet createQueryField(final int index,
            final ResultSetMetaData meta) throws SQLException {
        // assert isSupportedDatabaseType(meta.getColumnType(index));

        final int type = meta.getColumnType(index);
        final String name = meta.getColumnName(index);

        QueryFieldDotNet result = null;

        final int isNullable = meta.isNullable(index);

        switch (type) {
        case java.sql.Types.CHAR:
        case java.sql.Types.VARCHAR:
            result = new QueryFieldDotNet(new IgType("string"), name);
            break;
        case java.sql.Types.LONGVARCHAR:
            // .NET FrameworkではStringにマッピングします。
            result = new QueryFieldDotNet(new IgType("string"), name);
            break;
        case java.sql.Types.DATE:
        case java.sql.Types.TIME:
        case java.sql.Types.TIMESTAMP:
            result = new QueryFieldDotNet(new IgType("System.DateTime"), name);
            break;
        case java.sql.Types.REAL:
            if (isNullable == ResultSetMetaData.columnNoNulls) {
                result = new QueryFieldDotNet(new IgType("float"), name);
            } else {
                result = new QueryFieldDotNet(new IgType("float"), name);
                // 不本意ながらプリミティブ型にマップします。
            }
            break;
        case java.sql.Types.FLOAT:
        case java.sql.Types.DOUBLE:
            if (isNullable == ResultSetMetaData.columnNoNulls) {
                result = new QueryFieldDotNet(new IgType("double"), name);
            } else {
                result = new QueryFieldDotNet(new IgType("double"), name);
                // 不本意ながらプリミティブ型にマップします。
            }
            break;
        case java.sql.Types.DECIMAL:
        case java.sql.Types.NUMERIC:
            // DECIMALとNUMERICとの差異は、Sunのマップ表には現れない
            result = new QueryFieldDotNet(new IgType("decimal"), name);
            result.setPrecision(meta.getPrecision(index));
            result.setScale(meta.getScale(index));
            break;
        case java.sql.Types.INTEGER:
            if (isNullable == ResultSetMetaData.columnNoNulls) {
                result = new QueryFieldDotNet(new IgType("int"), name);
            } else {
                result = new QueryFieldDotNet(new IgType("int"), name);
                // 不本意ながらプリミティブ型にマップします。
            }
            break;
        case java.sql.Types.BIGINT:
            if (isNullable == ResultSetMetaData.columnNoNulls) {
                result = new QueryFieldDotNet(new IgType("long"), name);
            } else {
                result = new QueryFieldDotNet(new IgType("long"), name);
                // 不本意ながらプリミティブ型にマップします。
            }
            break;
        case java.sql.Types.SMALLINT:
            if (isNullable == ResultSetMetaData.columnNoNulls) {
                result = new QueryFieldDotNet(new IgType("short"), name);
            } else {
                result = new QueryFieldDotNet(new IgType("short"), name);
                // 不本意ながらプリミティブ型にマップします。
            }
            break;
        case java.sql.Types.TINYINT:
            if (isNullable == ResultSetMetaData.columnNoNulls) {
                result = new QueryFieldDotNet(new IgType("byte"), name);
            } else {
                result = new QueryFieldDotNet(new IgType("byte"), name);
                // 不本意ながらプリミティブ型にマップします。
            }
            break;
        case java.sql.Types.BIT:
        case java.sql.Types.BOOLEAN:
            if (isNullable == ResultSetMetaData.columnNoNulls) {
                result = new QueryFieldDotNet(new IgType("bool"), name);
            } else {
                result = new QueryFieldDotNet(new IgType("bool"), name);
                // 不本意ながらプリミティブ型にマップします。
            }
            break;
        case java.sql.Types.BINARY:
        case java.sql.Types.VARBINARY:
        case java.sql.Types.LONGVARBINARY:
            result = new QueryFieldDotNet(new IgType("byte[]"), name);
            break;
        default:
            System.out.println("型チェック: サポートしていない型が与えられました: "
                    + BlancoDbMappingUtilDotNet.convertJdbcTypeToString(type));
            throw new IllegalArgumentException("型[" + type + "]("
                    + BlancoDbMappingUtilDotNet.convertJdbcTypeToString(type)
                    + ")はサポートされません。");
        }
        result.setJdbcType(type);

        // 各種メタ情報を記憶します。
        // 2005.05.01 tosiki iga
        result.setNullable(isNullable);
        result.setReadOnly(meta.isReadOnly(index));
        result.setWritable(meta.isWritable(index));
        result.setDefinitelyWritable(meta.isDefinitelyWritable(index));

        result.setSize(meta.getColumnDisplaySize(index));

        return result;
    }
}