/*
 * blancoDb
 * Copyright (C) 2004-2005 Yasuo Nakanishi
 * 
 * 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.collector;

import java.io.FileNotFoundException;
import java.io.IOException;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

import javax.xml.parsers.ParserConfigurationException;

import org.xml.sax.SAXException;

import blanco.db.conf.BlancoDbMetadata;
import blanco.db.conf.BlancoDbSetting;
import blanco.db.conf.SelectQuery;
import blanco.db.definition.FieldFactory;
import blanco.db.definition.QueryField;
import blanco.db.definition.QueryIterator;
import blanco.db.exception.BlancoDbException;
import blanco.db.mapping.BlancoDbMappingUtil;
import blanco.db.util.BlancoDbQueryParserUtil;
import blanco.ig.expander.Value;
import blanco.ig.tool.ConsolePrinter;

/**
 * @author Yasuo Nakanishi
 */
public class IteratorCollector {
    private DatabaseService _databaseService = null;

    private BlancoDbSetting _setting = null;

    private BlancoDbMetadata _metadata = null;

    private List _margedQuery = new ArrayList();

    private List _resultList = new ArrayList();

    private ConsolePrinter _printer = ConsolePrinter.get();

    public IteratorCollector(BlancoDbSetting setting,
            DatabaseService databaseService) {
        _setting = setting;
        _databaseService = databaseService;
    }

    public List collect(BlancoDbMetadata metadata) throws SAXException,
            IOException, ParserConfigurationException, BlancoDbException {
        _metadata = metadata;

        load();

        Iterator iterator = null;
        iterator = _margedQuery.iterator();
        while (iterator.hasNext()) {
            SelectQuery selectQuery = (SelectQuery) iterator.next();
            try {
                collectIterator(selectQuery);
            } catch (SQLException ex) {
                throw new BlancoDbException("クエリ名[" + selectQuery.getName()
                        + "]においてSQL例外が発生しました。クエリ名[" + selectQuery.getName()
                        + "]のSQL文やSQL入力パラメータに誤りが含まれていないか調べてください。" + "SQLState["
                        + ex.getSQLState() + "] メッセージ[" + ex.toString() + "]");
            }
        }
        return _resultList;
    }

    private void collectIterator(SelectQuery query) throws SQLException,
            BlancoDbException {
        List fields = getQueryFields(query);
        for (int i = 0; i < fields.size(); i++) {
            query.addField((QueryField) fields.get(i));
        }
        _resultList.add(new QueryIterator(query));
    }

    private List getQueryFields(SelectQuery selectQuery) throws SQLException,
            BlancoDbException {
        List list = new ArrayList();

        BlancoDbQueryParserUtil sqlQuery = new BlancoDbQueryParserUtil(
                selectQuery.getQuery());

        PreparedStatement _statement = null;
        ResultSet _resultSet = null;
        ResultSetMetaData meta = null;

        final FieldFactory _factory = new FieldFactory();
        try {
            _statement = _databaseService.getConnection().getConnection()
                    .prepareStatement(sqlQuery.getNaturalSqlString());

            // ここでバインド処理を実施。
            // 2005.05.20 t.iga
            for (Iterator iteParameter = selectQuery.getParameterIterator(); iteParameter
                    .hasNext();) {
                Value val = (Value) iteParameter.next();
                int[] listCol = sqlQuery.getSqlInputParameters(val.getName());
                if (listCol == null) {
                    throw new BlancoDbException("クエリ名[" + selectQuery.getName()
                            + "]において 結びつけられていないSQL入力パラメータ[" + val.getName()
                            + "]が発見されました。");
                    // continue;
                }
                for (int iteSame = 0; iteSame < listCol.length; iteSame++) {
                    int index = listCol[iteSame];
                    BlancoDbMappingUtil.processPreparedStatementWithSomeValue(
                            val.getType().getName(), _statement, index);
                }
            }

            _resultSet = _statement.executeQuery();
            meta = _resultSet.getMetaData();
            for (int i = 0; i < meta.getColumnCount(); i++) {
                list.add(_factory.createQueryField(i + 1, meta));
            }
        } finally {
            if (_resultSet != null) {
                _resultSet.close();
            }
            if (_statement != null) {
                _statement.close();
            }
        }
        return list;
    }

    private void load() throws SAXException, IOException,
            ParserConfigurationException, BlancoDbException {
        SelectQueryLoader loader = new SelectQueryLoader(_setting
                .getQueryDirectory());
        for (int i = 0; i < _metadata.getSelectQueryCount(); i++) {
            try {
                loader.load(_metadata.getSelectQuery(i).getQueryPath());
                _margedQuery.add(loader.getResult());
            } catch (FileNotFoundException e) {
                String message = "";
                message += "クエリファイルが見つかりませんでした。";
                message += "次のクエリは作成されません。";
                message += "Name:" + _metadata.getSelectQuery(i).getName();
                message += ", FilePath:"
                        + _metadata.getSelectQuery(i).getFilePath();
                _printer.printWarning(message);
            }
        }
    }
}