/*
 * blanco Framework
 * Copyright (C) 2004-2006 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.expander.query.iterator;

import java.sql.ResultSetMetaData;
import java.sql.Types;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;

import blanco.cg.BlancoCgObjectFactory;
import blanco.cg.valueobject.BlancoCgClass;
import blanco.cg.valueobject.BlancoCgMethod;
import blanco.cg.valueobject.BlancoCgSourceFile;
import blanco.db.common.expander.BlancoDbAbstractMethod;
import blanco.db.common.util.BlancoDbQueryParserUtil;
import blanco.db.common.valueobject.BlancoDbSetting;
import blanco.db.common.valueobject.BlancoDbSqlInfoStructure;
import blanco.db.util.BlancoDbCgUtilDotNet;
import blanco.db.util.BlancoDbMappingUtilDotNet;
import blanco.dbmetadata.valueobject.BlancoDbMetaDataColumnStructure;

/**
 * 個別のメソッドを展開するためのクラス。
 * 
 * @author Tosiki Iga
 */
public class SetInputParameterMethodDotNet extends BlancoDbAbstractMethod {
    private boolean fIsCallableStatement = false;

    public SetInputParameterMethodDotNet(final BlancoDbSetting argDbSetting,
            final BlancoDbSqlInfoStructure argSqlInfo,
            final BlancoCgObjectFactory argCgFactory,
            final BlancoCgSourceFile argCgSourceFile,
            final BlancoCgClass argCgClass, final boolean isCallableStatement) {
        super(argDbSetting, argSqlInfo, argCgFactory, argCgSourceFile,
                argCgClass);
        fIsCallableStatement = isCallableStatement;
    }

    public void expand() {
        fCgSourceFile.getImportList().add("System.Data.SqlDbType");

        final BlancoCgMethod cgMethod = fCgFactory.createMethod(
                "SetInputParameter", "クエリに与える入力パラメータをセットします。");
        fCgClass.getMethodList().add(cgMethod);

        BlancoDbCgUtilDotNet.addExceptionToMethodSqlException(fCgFactory,
                cgMethod);

        for (int index = 0; index < fSqlInfo.getInParameterList().size(); index++) {
            BlancoDbMetaDataColumnStructure columnStructure = (BlancoDbMetaDataColumnStructure) fSqlInfo
                    .getInParameterList().get(index);

            cgMethod.getParameterList().add(
                    fCgFactory.createParameter(columnStructure.getName(),
                            BlancoDbMappingUtilDotNet
                                    .getFullClassName(columnStructure), "'"
                                    + columnStructure.getName() + "'パラメータの値"));
        }

        final List listLine = cgMethod.getLineList();

        if (fDbSetting.getLogging()) {
            listLine.add("if (fLog.isDebugEnabled()) {");

            String strLine = "fLog.debug(\"" + cgMethod.getName() + ": ";
            boolean isFirst = true;
            for (int index = 0; index < fSqlInfo.getInParameterList().size(); index++) {
                if (isFirst) {
                    isFirst = false;
                } else {
                    strLine += " + \", ";
                }

                final BlancoDbMetaDataColumnStructure parameter = (BlancoDbMetaDataColumnStructure) fSqlInfo
                        .getInParameterList().get(index);

                strLine += parameter.getName() + " = \" + "
                        + parameter.getName();
            }
            strLine += ");";

            listLine.add(strLine);
            listLine.add("}");
            listLine.add("");
        }

        // statementが未確保であるばあい、強制的にprepareStatementを呼び出します。
        listLine.add("if (fStatement == null) {");
        if (fIsCallableStatement == false) {
            listLine.add("PrepareStatement();");
        } else {
            listLine.add("PrepareCall();");
        }
        listLine.add("}");

        final BlancoDbQueryParserUtil query = new BlancoDbQueryParserUtil(
                fSqlInfo.getQuery());

        // 既に処理をしたパラメータのマップ。
        final HashMap mapProcessedParam = new HashMap();

        final Iterator ite = fSqlInfo.getInParameterList().iterator();
        for (int index = 1; ite.hasNext(); index++) {
            // SQL文からパラメータを発見しています。
            final BlancoDbMetaDataColumnStructure columnStructure = (BlancoDbMetaDataColumnStructure) ite
                    .next();

            final int[] listCol = query.getSqlParameters(columnStructure
                    .getName());
            if (listCol == null) {
                throw new IllegalArgumentException("SQL定義ID["
                        + fSqlInfo.getName() + "]の SQL入力パラメータ["
                        + columnStructure.getName() + "]が結びついていせん.");
            }
            for (int iteSame = 0; iteSame < listCol.length; iteSame++) {
                if (mapProcessedParam.get(columnStructure.getName()) != null) {
                    // 既に処理をされています。
                    // ADO.NETの場合にはSQLパラメータは名前引き当てであるので、2度目は処理せずにスキップします。
                    continue;
                }
                // 既に処理済のパラメータとして記憶します。
                mapProcessedParam.put(columnStructure.getName(),
                        columnStructure);

                listLine.add("");

                listLine.add("// SQL入力パラメータ[" + columnStructure.getName()
                        + "]を設定します。");
                listLine
                        .add("SqlParameter sqlInParameter" + index + " = null;");
                listLine.add("if (fStatement.Parameters.IndexOf(\"@"
                        + columnStructure.getName() + "\") < 0) {");
                listLine.add("sqlInParameter"
                        + index
                        + " = new SqlParameter(\"@"
                        + columnStructure.getName()
                        + "\", SqlDbType."
                        + BlancoDbMappingUtilDotNet
                                .getDotNetSqlDbType(columnStructure) + ");");
                listLine.add("sqlInParameter" + index
                        + ".Direction = ParameterDirection.Input;");
                listLine.add("fStatement.Parameters.Add(sqlInParameter" + index
                        + ");");

                switch (columnStructure.getDataType()) {
                case Types.BINARY:
                case Types.VARBINARY:
                case Types.LONGVARBINARY:
                case Types.BLOB:
                    // case Types.LONGVARCHAR:
                    // case Types.CLOB:
                    listLine.add("sqlInParameter" + index + ".Size = "
                            + columnStructure.getName() + ".Length;");
                    break;
                }
                listLine.add("} else {");
                listLine.add("sqlInParameter" + index
                        + " = fStatement.Parameters[\"@"
                        + columnStructure.getName() + "\"];");
                listLine.add("}");
                listLine.add("sqlInParameter" + index + ".Value = "
                        + columnStructure.getName() + ";");

                final boolean isNullable = columnStructure.getNullable() == ResultSetMetaData.columnNullable;
                switch (columnStructure.getDataType()) {
                case Types.BIT:
                case Types.BOOLEAN:
                case Types.TINYINT:
                    listLine.add("if (false)");
                    break;
                case Types.SMALLINT:
                    listLine.add("if (" + columnStructure.getName()
                            + " == short.MinValue)");
                    break;
                case Types.INTEGER:
                    listLine.add("if (" + columnStructure.getName()
                            + " == int.MinValue)");
                    break;
                case Types.BIGINT:
                    listLine.add("if (" + columnStructure.getName()
                            + " == long.MinValue)");
                    break;
                case Types.REAL:
                    listLine.add("if (" + columnStructure.getName()
                            + " == float.MinValue)");
                    break;
                case Types.FLOAT:
                case Types.DOUBLE:
                    listLine.add("if (" + columnStructure.getName()
                            + " == double.MinValue)");
                    break;
                case Types.NUMERIC:
                case Types.DECIMAL:
                    listLine.add("if (" + columnStructure.getName()
                            + " == decimal.MinValue)");
                    break;
                case Types.CHAR:
                case Types.VARCHAR:
                    listLine.add("if (" + columnStructure.getName()
                            + " == null)");
                    break;
                case Types.DATE:
                case Types.TIME:
                case Types.TIMESTAMP:
                    listLine.add("if (" + columnStructure.getName()
                            + " == DateTime.MinValue)");
                    break;
                case Types.BINARY:
                case Types.VARBINARY:
                case Types.LONGVARBINARY:
                case Types.BLOB:
                case Types.LONGVARCHAR:
                case Types.CLOB:
                    listLine.add("if (" + columnStructure.getName()
                            + " == null)");
                    break;
                case Types.JAVA_OBJECT:
                case Types.DISTINCT:
                case Types.STRUCT:
                case Types.ARRAY:
                case Types.NULL:
                case Types.OTHER:
                case Types.REF:
                case Types.DATALINK:
                default:
                    listLine.add("if (false)");
                    break;
                }

                listLine.add("{");
                listLine.add("sqlInParameter" + index
                        + ".Value = DBNull.Value;");
                listLine.add("}");
            }
        }
    }
}