/*
 * 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.expander.query.iterator;

import java.util.Iterator;

import blanco.commons.util.BlancoNameAdjuster;
import blanco.db.definition.QueryFieldDotNet;
import blanco.db.definition.QueryIteratorDotNet;
import blanco.db.mapping.BlancoDbMappingUtilDotNet;
import blanco.db.util.BlancoDbObjectStorageDotNet;
import blanco.ig.expander.IgType;
import blanco.ig.expander.IgValue;
import blanco.ig.expander.Scope;
import blanco.ig.expander.implementor.Implementor;
import blanco.ig.expander.method.MethodExpander;

/**
 * @author Tosiki Iga
 */
public class GetRowMethodDotNet extends MethodExpander {
    private BlancoDbObjectStorageDotNet storage = null;

    private IgValue _result = null;

    private IgType rowObjectType = null;

    private QueryIteratorDotNet _iterator = null;

    public GetRowMethodDotNet(final BlancoDbObjectStorageDotNet storage,
            final IgType rowObjectType, final QueryIteratorDotNet iterator) {
        super("GetRow");
        _iterator = iterator;
        this.rowObjectType = rowObjectType;
        this.storage = storage;

        _result = new IgValue(rowObjectType, "result");
    }

    /**
     * シングル属性が有効である場合には protectedとします。
     */
    protected Scope getScope() {
        if (_iterator.isSingle()) {
            return Scope.PROTECTED;
        } else {
            return Scope.PUBLIC;
        }
    }

    public void setupSignature() {
        setReturnType(_result.getType());
        getJavaDoc().addLine("現在の行のデータをオブジェクトとして取得します。");

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

        getJavaDoc().addReturn("行オブジェクト");
    }

    public void implement() {
        final Implementor implementor = new Implementor(getData());

        if (storage.getSetting().isLogging()) {
            implementor.addLine("if (fLog.isDebugEnabled()) {");
            implementor.addLine("fLog.debug(\"" + getName() + "\");");
            implementor.addLine("}");
            implementor.addLine("");
        }

        final Iterator fieldIterator = _iterator.getFieldIterator();

        implementor.addLine(rowObjectType.getName() + " result = new "
                + rowObjectType.getName() + "();");

        // C#,ADO.NETでは0オリジンです。
        int indexCol = 0;
        while (fieldIterator.hasNext()) {
            QueryFieldDotNet field = (QueryFieldDotNet) fieldIterator.next();

            implementor.addLine("if (fResultSet.IsDBNull(" + indexCol + "))");
            implementor.addLine("{");
            if (field.getTypeName().equals("string")
                    || field.getTypeName().equals("byte[]")) {
                implementor.addLine("result.Set"
                        + BlancoNameAdjuster.toClassName(field.getName())
                        + "(null);");
            } else if (field.getTypeName().equals("DateTime")) {
                implementor
                        .addLine("// .NET Framework 1.1上ではnullを扱うことができないので、最小値に対応付けます。");
                implementor.addLine("result.Set"
                        + BlancoNameAdjuster.toClassName(field.getName())
                        + "(DateTime.MinValue);");
            } else if (field.getTypeName().equals("bool")) {
                implementor
                        .addLine("// .NET Framework 1.1上ではnullを扱うことができないので、falseに対応付けます。");
                implementor.addLine("result.Set"
                        + BlancoNameAdjuster.toClassName(field.getName())
                        + "(false);");
            } else if (field.getTypeName().equals("byte")) {
                implementor
                        .addLine("// .NET Framework 1.1上ではnullを扱うことができないので、最小値に対応付けます。");
                implementor.addLine("result.Set"
                        + BlancoNameAdjuster.toClassName(field.getName())
                        + "(byte.MinValue);");
            } else if (field.getTypeName().equals("short")) {
                implementor
                        .addLine("// .NET Framework 1.1上ではnullを扱うことができないので、最小値に対応付けます。");
                implementor.addLine("result.Set"
                        + BlancoNameAdjuster.toClassName(field.getName())
                        + "(short.MinValue);");
            } else if (field.getTypeName().equals("int")) {
                implementor
                        .addLine("// .NET Framework 1.1上ではnullを扱うことができないので、最小値に対応付けます。");
                implementor.addLine("result.Set"
                        + BlancoNameAdjuster.toClassName(field.getName())
                        + "(int.MinValue);");
            } else if (field.getTypeName().equals("long")) {
                implementor
                        .addLine("// .NET Framework 1.1上ではnullを扱うことができないので、最小値に対応付けます。");
                implementor.addLine("result.Set"
                        + BlancoNameAdjuster.toClassName(field.getName())
                        + "(long.MinValue);");
            } else if (field.getTypeName().equals("float")) {
                implementor
                        .addLine("// .NET Framework 1.1上ではnullを扱うことができないので、最小値に対応付けます。");
                implementor.addLine("result.Set"
                        + BlancoNameAdjuster.toClassName(field.getName())
                        + "(float.MinValue);");
            } else if (field.getTypeName().equals("double")) {
                implementor
                        .addLine("// .NET Framework 1.1上ではnullを扱うことができないので、最小値に対応付けます。");
                implementor.addLine("result.Set"
                        + BlancoNameAdjuster.toClassName(field.getName())
                        + "(double.MinValue);");
            } else if (field.getTypeName().equals("decimal")) {
                implementor
                        .addLine("// .NET Framework 1.1上ではnullを扱うことができないので、最小値に対応付けます。");
                implementor.addLine("result.Set"
                        + BlancoNameAdjuster.toClassName(field.getName())
                        + "(decimal.MinValue);");
            } else {
                implementor.addLine("// 型[" + field.getTypeName()
                        + "]のnull値は無視します。");
            }

            implementor.addLine("}");
            implementor.addLine("else");
            implementor.addLine("{");

            if (field.getTypeName().equals("byte[]")) {
                implementor.addLine("");
                implementor.addLine("// 大きなバイト配列型なので、それ専用の処理を行います。");
                implementor.addLine("long totalLength" + field.getName()
                        + " = fResultSet.GetBytes(" + indexCol
                        + ", 0, null, 0, 0);");
                implementor.addLine("byte[] byteBuf" + field.getName()
                        + " = new byte[totalLength" + field.getName() + "];");
                implementor.addLine("int positionOf" + field.getName()
                        + " = 0;");
                implementor.addLine("int bufferSizeOf" + field.getName()
                        + " = (int) Math.Min(totalLength" + field.getName()
                        + ", 8192);");
                implementor.addLine("");

                implementor.addLine("while (positionOf" + field.getName()
                        + " < totalLength" + field.getName() + ")");
                implementor.addLine("{");
                implementor
                        .addLine("int readLength = (int) fResultSet.GetBytes("
                                + indexCol + ", positionOf" + field.getName()
                                + ", byteBuf" + field.getName()
                                + ", positionOf" + field.getName() + ", "
                                + "bufferSizeOf" + field.getName() + ");");
                implementor.addLine("positionOf" + field.getName()
                        + " += readLength;");
                implementor.addLine("}");

                implementor.addLine("result.Set"
                        + BlancoNameAdjuster.toClassName(field.getName())
                        + "(byteBuf" + field.getName() + ");");
            } else {
                implementor.addLine("result.Set"
                        + BlancoNameAdjuster.toClassName(field.getName())
                        + "("
                        + "fResultSet."
                        + BlancoDbMappingUtilDotNet
                                .getResultSetGetterMethodName(field
                                        .getTypeName()) + "(" + indexCol + ")"
                        + ");");
            }

            implementor.addLine("}");
            implementor.addLine("");

            if (field.getObjectMappingForPrimitiveNullSupport()) {
                implementor.addLine("if(fResultSet.IsDBNull(" + indexCol
                        + ")) {");
                implementor.addLine("// この型では NULL許容はサポートしません。");
                implementor.addLine("}");
            }
            indexCol++;
        }

        implementor.addLine("return result;");
    }
}