/*
 * blancoDb Enterprise Edition
 * Copyright (C) 2005 Tosiki Iga
 * 
 * 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.db.definition.QueryIterator;
import blanco.db.mapping.BlancoDbMappingUtil;
import blanco.db.util.BlancoDbObjectStorage;
import blanco.db.util.BlancoDbQueryParserUtil;
import blanco.ig.expander.Type;
import blanco.ig.expander.Value;
import blanco.ig.expander.implementor.Implementor;
import blanco.ig.expander.method.MethodExpander;

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

    private QueryIterator _iterator = null;

    public SetInputParameterMethod(final BlancoDbObjectStorage storage,
            final QueryIterator iterator) {
        super("setInputParameter");
        _iterator = iterator;
        this.storage = storage;
    }

    public void setupSignature() {
        getJavaDoc().addException(new Type("java.sql.SQLException"),
                "SQL例外が発生した場合。");
        addException(new Type("java.sql.SQLException"));

        final Iterator iteInParameter = _iterator.getInParameterIterator();
        while (iteInParameter.hasNext()) {
            Value value = (Value) iteInParameter.next();
            addArgument(value);
            if (value.getType().getName().equals("InputStream")
                    || value.getType().getName().equals("Reader")) {
                addArgument(new Value(new Type(int.class), value.getName()
                        + "StreamLength"));
            }
        }

        getJavaDoc().addLine("SQL文に与えるSQL入力パラメータをセットします。");
        getJavaDoc().addLine("");
        getJavaDoc().addLine("内部的には PreparedStatementにSQL入力パラメータをセットします。");

        final Iterator iteInParameterJavaDoc = _iterator
                .getInParameterIterator();
        boolean isFirst = true;
        while (iteInParameterJavaDoc.hasNext()) {
            if (isFirst) {
                isFirst = false;
                // getJavaDoc().addLine("");
            }
            final Value parameter = (Value) 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 += " + \", ";
                }
                Value parameter = (Value) 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 BlancoDbQueryParserUtil query = new BlancoDbQueryParserUtil(
                _iterator.getQuery());

        final Iterator ite = _iterator.getInParameterIterator();
        while (ite.hasNext()) {
            // TODO: SQL文からパラメータを発見しなくてはなりません。
            // 現状、とりあえず与えられた順序で登場と仮定が加わっています。
            final Value val = (Value) 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++) {
                final int index = listCol[iteSame];
                if (BlancoDbMappingUtil.isMapWrapperClassNecessity(val
                        .getType().getName())) {
                    implementor.openIf(val.getName() + " == null");
                    implementor.addStatement("fStatement.setNull("
                            + index
                            + ", "
                            + BlancoDbMappingUtil.convertJavaTypeToJdbcType(val
                                    .getType().getFullName()) + ")");
                    implementor.addElse();
                }

                final String type = BlancoDbMappingUtil
                        .getPreparedStatementSetterMethodName(val.getType()
                                .getName());
                if (val.getType().getName().equals("Date")) {
                    // BlancoDbMappingUtil.mapWrapperClassIntoPrimitiveメソッドでTimestamp型が必要です。
                    implementor.addUsingType(new Type("java.sql.Timestamp"));
                }

                String stmtLine = "fStatement."
                        + type
                        + "("
                        + index
                        + ", "
                        + BlancoDbMappingUtil.mapWrapperClassIntoPrimitive(val
                                .getName(), val.getType().getName());
                if (val.getType().getName().equals("InputStream")
                        || val.getType().getName().equals("Reader")) {
                    stmtLine += ", " + val.getName() + "StreamLength";
                }
                stmtLine += ")";
                implementor.addStatement(stmtLine);

                if (BlancoDbMappingUtil.isMapWrapperClassNecessity(val
                        .getType().getName())) {
                    implementor.closeIf();
                }
            }
        }
    }
}