/*
 * 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.HashMap;
import java.util.Iterator;

import blanco.db.definition.QueryIteratorDotNet;
import blanco.db.mapping.BlancoDbMappingUtilDotNet;
import blanco.db.util.BlancoDbObjectStorageDotNet;
import blanco.db.util.BlancoDbQueryParserUtilDotNet;
import blanco.ig.expander.IgValue;
import blanco.ig.expander.implementor.Implementor;
import blanco.ig.expander.method.MethodExpander;

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

    private QueryIteratorDotNet _iterator = null;

    public SetInputParameterMethodDotNet(
            final BlancoDbObjectStorageDotNet storage,
            final QueryIteratorDotNet iterator) {
        super("SetInputParameter");
        _iterator = iterator;
        this.storage = storage;
    }

    public void setupSignature() {
        final Iterator iteInParameter = _iterator.getInParameterIterator();
        while (iteInParameter.hasNext()) {
            final IgValue value = (IgValue) iteInParameter.next();
            addArgument(value);
        }

        getJavaDoc().addLine("クエリに与える入力パラメータをセットします。");

        final Iterator iteInParameterJavaDoc = _iterator
                .getInParameterIterator();
        boolean isFirst = true;
        while (iteInParameterJavaDoc.hasNext()) {
            if (isFirst) {
                isFirst = false;
                // getJavaDoc().addLine("");
            }
            final IgValue parameter = (IgValue) iteInParameterJavaDoc.next();
            final String name = parameter.getName();
            getJavaDoc().addParameter(name, "'" + name + "'パラメータの値");
        }
    }

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

        if (storage.getSetting().isLogging()) {
            implementor.addLine("if (fLog.isDebugEnabled()) {");

            String strLine = "fLog.debug(\"" + getName() + ": ";
            final Iterator ite = _iterator.getInParameterIterator();
            boolean isFirst = true;
            while (ite.hasNext()) {
                if (isFirst) {
                    isFirst = false;
                } else {
                    strLine += " + \", ";
                }
                IgValue parameter = (IgValue) ite.next();
                strLine += parameter.getName() + " = \" + "
                        + parameter.getName();
            }
            strLine += ");";

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

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

        final BlancoDbQueryParserUtilDotNet query = new BlancoDbQueryParserUtilDotNet(
                _iterator.getQuery());

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

        final Iterator ite = _iterator.getInParameterIterator();
        for (int index = 1; ite.hasNext(); index++) {
            final IgValue val = (IgValue) ite.next();

            final int[] listCol = query.getSqlParameters(val.getName());
            if (listCol == null) {
                throw new IllegalArgumentException("SQL定義ID["
                        + _iterator.getName() + "]の SQL入力パラメータ["
                        + val.getName() + "]が結びついていません.");
            }

            for (int iteSame = 0; iteSame < listCol.length; iteSame++) {
                if (mapProcessedParam.get(val.getName()) != null) {
                    // 既に処理をされています。
                    // ADO.NETの場合にはSQLパラメータは名前引き当てであるので、2度目は処理せずにスキップします。
                    continue;
                }
                // 既に処理済のパラメータとして記憶します。
                mapProcessedParam.put(val.getName(), val);

                implementor.addLine("");

                implementor.addLine("// SQL入力パラメータ[" + val.getName()
                        + "]を設定します。");
                implementor.addLine("SqlParameter sqlInParameter" + index
                        + " = null;");
                implementor.addLine("if (fStatement.Parameters.IndexOf(\"@"
                        + val.getName() + "\") < 0) {");
                implementor.addLine("sqlInParameter"
                        + index
                        + " = new SqlParameter(\"@"
                        + val.getName()
                        + "\", "
                        + BlancoDbMappingUtilDotNet
                                .convertJavaTypeToJdbcType(val.getType()
                                        .getFullName()) + ");");
                implementor.addStatement("sqlInParameter" + index
                        + ".Direction = ParameterDirection.Input");
                implementor.addLine("fStatement.Parameters.Add(sqlInParameter"
                        + index + ");");
                if (val.getType().getName().equals("byte[]")
                        || val.getType().getName().equals("Reader")) {
                    implementor.addLine("sqlInParameter" + index + ".Size = "
                            + val.getName() + ".Length;");
                }
                implementor.addLine("} else {");
                implementor.addLine("sqlInParameter" + index
                        + " = fStatement.Parameters[\"@" + val.getName()
                        + "\"];");
                implementor.addLine("}");
                implementor.addLine("sqlInParameter" + index + ".Value = "
                        + val.getName() + ";");

                final boolean isNullable = BlancoDbMappingUtilDotNet
                        .isNullable(val.getType().getFullName());
                if (isNullable) {
                    implementor.addLine("if (" + val.getName() + " == null)");
                    implementor.addLine("{");

                    implementor.addLine("sqlInParameter" + index
                            + ".Value = DBNull.Value;");
                    implementor.addLine("}");
                } else {
                    // プリミティブ型
                    if (val.getType().getName().equals("DateTime")) {
                        implementor.addLine("if (" + val.getName()
                                + " == DateTime.MinValue)");
                    } else if (val.getType().getName().equals("short")) {
                        implementor.addLine("if (" + val.getName()
                                + " == short.MinValue)");
                    } else if (val.getType().getName().equals("int")) {
                        implementor.addLine("if (" + val.getName()
                                + " == int.MinValue)");
                    } else if (val.getType().getName().equals("long")) {
                        implementor.addLine("if (" + val.getName()
                                + " == long.MinValue)");
                    } else if (val.getType().getName().equals("float")) {
                        implementor.addLine("if (" + val.getName()
                                + " == float.MinValue)");
                    } else if (val.getType().getName().equals("double")) {
                        implementor.addLine("if (" + val.getName()
                                + " == double.MinValue)");
                    } else if (val.getType().getName().equals("decimal")) {
                        implementor.addLine("if (" + val.getName()
                                + " == decimal.MinValue)");
                    } else {
                        implementor.addLine("if (false)");
                    }

                    implementor.addLine("{");
                    implementor.addLine("sqlInParameter" + index
                            + ".Value = DBNull.Value;");
                    implementor.addLine("}");
                }
            }
        }
    }
}