/*
 * 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.Types;
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.commons.util.BlancoNameAdjuster;
import blanco.commons.util.BlancoNameUtil;
import blanco.db.common.expander.BlancoDbAbstractMethod;
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 GetRowMethodDotNet extends BlancoDbAbstractMethod {
    public GetRowMethodDotNet(final BlancoDbSetting argDbSetting,
            final BlancoDbSqlInfoStructure argSqlInfo,
            final BlancoCgObjectFactory argCgFactory,
            final BlancoCgSourceFile argCgSourceFile,
            final BlancoCgClass argCgClass) {
        super(argDbSetting, argSqlInfo, argCgFactory, argCgSourceFile,
                argCgClass);
    }

    public void expand() {
        final BlancoCgMethod cgMethod = fCgFactory.createMethod("GetRow",
                "現在の行のデータをオブジェクトとして取得します。");
        fCgClass.getMethodList().add(cgMethod);

        /*
         * シングル属性が有効である場合には protectedとします。
         */
        if (fSqlInfo.getSingle()) {
            cgMethod.setAccess("protected");
        }

        // 行オブジェクトの型名を取得します。
        final String rowObjectType = fDbSetting.getBasePackage() + ".row."
                + BlancoNameAdjuster.toClassName(fSqlInfo.getName()) + "Row";

        cgMethod.setReturn(fCgFactory.createReturn(rowObjectType, "行オブジェクト"));

        BlancoDbCgUtilDotNet.addExceptionToMethodSqlException(fCgFactory,
                cgMethod);

        final List listDesc = cgMethod.getLangDoc().getDescriptionList();

        if (fSqlInfo.getSingle()) {
            listDesc.add("シングル属性が有効なのでスコープをprotectedとします。");
            listDesc.add("このメソッドの代わりに GetSingleRowメソッドを利用してください。");
        } else {
            listDesc.add("このメソッドを呼び出す前に、Next()などのカーソルを操作するメソッドを呼び出す必要があります。");
        }

        final List listLine = cgMethod.getLineList();

        if (fDbSetting.getLogging()) {
            BlancoDbCgUtilDotNet.addBeginLogToMethod(cgMethod);
        }

        listLine.add(BlancoNameUtil.trimJavaPackage(rowObjectType)
                + " result = new "
                + BlancoNameUtil.trimJavaPackage(rowObjectType) + "();");

        // C#,ADO.NETでは0オリジンです。
        int indexCol = 0;
        for (int index = 0; index < fSqlInfo.getResultSetColumnList().size(); index++) {
            final BlancoDbMetaDataColumnStructure columnStructure = (BlancoDbMetaDataColumnStructure) fSqlInfo
                    .getResultSetColumnList().get(index);

            listLine.add("if (fResultSet.IsDBNull(" + indexCol + "))");
            listLine.add("{");

            switch (columnStructure.getDataType()) {
            case Types.BIT:
            case Types.BOOLEAN:
                listLine
                        .add("// .NET Framework 1.1上ではnullを扱うことができないので、falseに対応付けます。");
                listLine.add("result.Set"
                        + BlancoNameAdjuster.toClassName(columnStructure
                                .getName()) + "(false);");
                break;
            case Types.TINYINT:
                listLine
                        .add("// .NET Framework 1.1上ではnullを扱うことができないので、最小値に対応付けます。");
                listLine.add("result.Set"
                        + BlancoNameAdjuster.toClassName(columnStructure
                                .getName()) + "(byte.MinValue);");
                break;
            case Types.SMALLINT:
                listLine
                        .add("// .NET Framework 1.1上ではnullを扱うことができないので、最小値に対応付けます。");
                listLine.add("result.Set"
                        + BlancoNameAdjuster.toClassName(columnStructure
                                .getName()) + "(short.MinValue);");
                break;
            case Types.INTEGER:
                listLine
                        .add("// .NET Framework 1.1上ではnullを扱うことができないので、最小値に対応付けます。");
                listLine.add("result.Set"
                        + BlancoNameAdjuster.toClassName(columnStructure
                                .getName()) + "(int.MinValue);");
                break;
            case Types.BIGINT:
                listLine
                        .add("// .NET Framework 1.1上ではnullを扱うことができないので、最小値に対応付けます。");
                listLine.add("result.Set"
                        + BlancoNameAdjuster.toClassName(columnStructure
                                .getName()) + "(long.MinValue);");
                break;
            case Types.REAL:
                listLine
                        .add("// .NET Framework 1.1上ではnullを扱うことができないので、最小値に対応付けます。");
                listLine.add("result.Set"
                        + BlancoNameAdjuster.toClassName(columnStructure
                                .getName()) + "(float.MinValue);");
                break;
            case Types.FLOAT:
            case Types.DOUBLE:
                listLine
                        .add("// .NET Framework 1.1上ではnullを扱うことができないので、最小値に対応付けます。");
                listLine.add("result.Set"
                        + BlancoNameAdjuster.toClassName(columnStructure
                                .getName()) + "(double.MinValue);");
                break;
            case Types.NUMERIC:
            case Types.DECIMAL:
                listLine
                        .add("// .NET Framework 1.1上ではnullを扱うことができないので、最小値に対応付けます。");
                listLine.add("result.Set"
                        + BlancoNameAdjuster.toClassName(columnStructure
                                .getName()) + "(decimal.MinValue);");
                break;
            case Types.CHAR:
            case Types.VARCHAR:
                listLine.add("result.Set"
                        + BlancoNameAdjuster.toClassName(columnStructure
                                .getName()) + "(null);");
                break;
            case Types.DATE:
            case Types.TIME:
            case Types.TIMESTAMP:
                listLine
                        .add("// .NET Framework 1.1上ではnullを扱うことができないので、最小値に対応付けます。");
                listLine.add("result.Set"
                        + BlancoNameAdjuster.toClassName(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("result.Set"
                        + BlancoNameAdjuster.toClassName(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("// 型[" + columnStructure.getTypeName()
                        + "]のnull値は無視します。");
                break;
            }

            listLine.add("}");
            listLine.add("else");
            listLine.add("{");

            switch (columnStructure.getDataType()) {
            case Types.BINARY:
            case Types.VARBINARY:
            case Types.LONGVARBINARY:
            case Types.BLOB:
                listLine.add("");
                listLine.add("// 大きなバイト配列型なので、それ専用の処理を行います。");
                listLine.add("long totalLength" + columnStructure.getName()
                        + " = fResultSet.GetBytes(" + indexCol
                        + ", 0, null, 0, 0);");
                listLine.add("byte[] byteBuf" + columnStructure.getName()
                        + " = new byte[totalLength" + columnStructure.getName()
                        + "];");
                listLine.add("int positionOf" + columnStructure.getName()
                        + " = 0;");
                listLine.add("int bufferSizeOf" + columnStructure.getName()
                        + " = (int) Math.Min(totalLength"
                        + columnStructure.getName() + ", 8192);");
                listLine.add("");

                listLine.add("while (positionOf" + columnStructure.getName()
                        + " < totalLength" + columnStructure.getName() + ")");
                listLine.add("{");
                listLine.add("int readLength = (int) fResultSet.GetBytes("
                        + indexCol + ", positionOf" + columnStructure.getName()
                        + ", byteBuf" + columnStructure.getName()
                        + ", positionOf" + columnStructure.getName() + ", "
                        + "bufferSizeOf" + columnStructure.getName() + ");");
                listLine.add("positionOf" + columnStructure.getName()
                        + " += readLength;");
                listLine.add("}");

                listLine.add("result.Set"
                        + BlancoNameAdjuster.toClassName(columnStructure
                                .getName()) + "(byteBuf"
                        + columnStructure.getName() + ");");
                break;
            default:
                listLine
                        .add("result.Set"
                                + BlancoNameAdjuster
                                        .toClassName(columnStructure.getName())
                                + "("
                                + "fResultSet."
                                + BlancoDbMappingUtilDotNet
                                        .getGetterMethodNameForResultSet(columnStructure)
                                + "(" + indexCol + ")" + ");");
                break;
            }

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


            indexCol++;
        }

        listLine.add("");

        listLine.add("return result;");
    }
}