/*
 *    GeoTools - OpenSource mapping toolkit
 *    http://geotools.org
 *    (C) Copyright IBM Corporation, 2005-2007. All rights reserved.
 *
 *    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;
 *    version 2.1 of the License.
 *
 *    This library is distributed in the hope that it will be useful,
 *    but WITHOUT ANY WARRANTY; without even the implied warranty of
 *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 *    Lesser General Public License for more details.
 *
 */
package org.geotools.data.db2;

import org.geotools.data.jdbc.JDBCUtils;
import org.geotools.data.jdbc.fidmapper.BasicFIDMapper;
import org.geotools.data.jdbc.fidmapper.DefaultFIDMapperFactory;
import org.geotools.data.jdbc.fidmapper.FIDMapper;
import org.geotools.data.jdbc.fidmapper.MaxIncFIDMapper;
import org.geotools.data.jdbc.fidmapper.TypedFIDMapper;
import java.io.IOException;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.logging.Logger;


/**
 * Overrides DefaultFIDMapperFactory methods for DB2-specific handling.
 *
 * @author David Adler - IBM Corporation
 * @source $URL: http://svn.geotools.org/geotools/tags/2.4.1/modules/unsupported/db2/src/main/java/org/geotools/data/db2/DB2FIDMapperFactory.java $
 */
public class DB2FIDMapperFactory extends DefaultFIDMapperFactory {
    private static final Logger LOGGER = org.geotools.util.logging.Logging.getLogger(
            "org.geotools.data.db2");
    private String databaseSchemaName = null;

    /**
     * Default constructor will cause FID columns to be returned as business
     * attributes.
     */
    public DB2FIDMapperFactory(String databaseSchemaName) {
        super(true);
        this.databaseSchemaName = databaseSchemaName;
    }

    /**
     * Constructs a DB2FIDMapperFactory with user specification of whether to
     * return FID columns as business attributes.
     *
     * @param returnFIDColumnsAsAttributes true if FID columns should be
     *        returned as business attributes.
     */
    public DB2FIDMapperFactory(boolean returnFIDColumnsAsAttributes) {
        super(returnFIDColumnsAsAttributes);
    }

    /**
     * Gets the appropriate FIDMapper for the specified table.
     *
     * @param catalog
     * @param schema
     * @param tableName
     * @param connection the active database connection to get table key
     *        information
     *
     * @return the appropriate FIDMapper for the specified table.
     *
     * @throws IOException if any error occurs.
     */
    public FIDMapper getMapper(String catalog, String schema, String tableName,
        Connection connection) throws IOException {
        FIDMapper fm = super.getMapper(catalog, schema, tableName, connection);
        LOGGER.fine(toString(fm));

        return fm;
    }

    /**
     * Determine whether this column is autoincrement.  An open connection to
     * the database must be provided.
     *
     * @param catalog not used
     * @param schema not used
     * @param tableName the table name
     * @param conn an open database connection
     * @param tableInfo not used
     * @param columnName the FID column name
     * @param dataType not used
     *
     * @return true if this is an autoincrement column
     *
     * @throws SQLException
     */
    protected boolean isAutoIncrement(String catalog, String schema,
        String tableName, Connection conn, ResultSet tableInfo,
        String columnName, int dataType) throws SQLException {
        // if it's not an integer type it can't be an auto increment type
        if (!isIntegralType(dataType)) {
            return false;
        }

        // It seems like there should be a better way to get this directly from the catalog
        boolean autoIncrement = false;
        Statement statement = null;
        ResultSet rs = null;

        try {
            statement = conn.createStatement();

            String stmtString = "SELECT IDENTITY, GENERATED FROM SYSCAT.COLUMNS "
                + "WHERE TABSCHEMA = '" + tableInfo.getString("TABLE_SCHEM")
                + "' " + "AND TABNAME = '" + tableName + "' "
                + "AND COLNAME = '" + columnName + "'";

            rs = statement.executeQuery(stmtString);

            if (rs.next()) {
                if (rs.getString(1).equals("Y") && rs.getString(2).equals("A")) {
                    autoIncrement = true;
                }
            }
        } finally {
            JDBCUtils.close(statement);
            JDBCUtils.close(rs);
        }

        return autoIncrement;
    }

    /**
     * Returns a DB2NullFIDMapper when there is no primary key.
     *
     * @param schema ignored.
     * @param tableName ignored.
     * @param connection ignored.
     *
     * @return a DB2NullFIDMapper.
     */
    protected FIDMapper buildNoPKMapper(String schema, String tableName,
        Connection connection) {
        FIDMapper mapper;
        mapper = new DB2NullFIDMapper(schema, tableName);

        return mapper;
    }

    /**
     * Builds a FID mapper based on a single column primary key. Default
     * version tries the auto-increment way, then a mapping on an {@link
     * MaxIncFIDMapper} type for numeric columns, and a plain {@link
     * BasicFIDMapper} of text based columns.
     *
     * @param schema
     * @param tableName
     * @param connection not used
     * @param ci the column information
     *
     * @return a FIDMapper
     */
    protected FIDMapper buildSingleColumnFidMapper(String schema,
        String tableName, Connection connection, ColumnInfo ci) {

              if (ci.isAutoIncrement()) {
                  return new DB2AutoIncrementFIDMapper(databaseSchemaName, tableName, ci.getColName(), ci.getDataType());
              } else if (isIntegralType(ci.getDataType())) {
                  return new MaxIncFIDMapper(databaseSchemaName, tableName, ci.getColName(), ci.getDataType(), true);
              } else {
                  return new BasicFIDMapper(databaseSchemaName, tableName, ci.getColName(), ci.getSize(), true);
              }
    }

    /**
     * Create a nice string representation of a FID Mapper
     *
     * @param fm the FID Mapper
     *
     * @return the String representation
     */
    String toString(FIDMapper fm) {
        String mapperName = ((TypedFIDMapper) fm).getWrappedMapper().getClass()
                             .toString();
        String colInfo = "";

        if (fm.getColumnCount() > 0) {
            colInfo = fm.getColumnName(0) + ":" + fm.getColumnType(0) + ":"
                + fm.getColumnSize(0) + ":" + fm.getColumnDecimalDigits(0);
        }

        String s = mapperName + ":" + fm.getColumnCount() + ":" + colInfo + ":"
            + fm.returnFIDColumnsAsAttributes() + ":"
            + fm.hasAutoIncrementColumns() + ":" + "";

        return s;
    }
}
