package eu.dnetlib.dlms.jdbc;

import java.sql.ParameterMetaData;
import java.sql.SQLException;
import java.sql.SQLFeatureNotSupportedException;

import eu.dnetlib.dlms.lowlevel.objects.DorotyObjectEnum;

/**
 * Implementation class of ParameterMetadata interface that can be used to get
 * information about tyoes and properties of each parameter placeholder in a
 * PreparedStatement. Conforming to the interface description, the data returned
 * by DOLParameterMetadata objects are not available until the PreparedStatement
 * has been executed (well, in fact they are available just after the first time
 * parameters are set, since this class uses the array of ParamInfo of a
 * DOLPreparedStatement object).
 * 
 * @author lexis
 */
public class DOLParameterMetadata implements ParameterMetaData {

    /**
     * PreparedStatement instance whose parameters are descripted by this
     * metadata.
     */
    private DOLPreparedStatement preparedStm;

    public void setPreparedStm(final DOLPreparedStatement preparedStm) {
	this.preparedStm = preparedStm;
    }

    public DOLPreparedStatement getPreparedStm() {
	return this.preparedStm;
    }

    /**
     * Constructor.
     * 
     * @param stm
     *            a DOLPreparedStatement instance whose parameters info are
     *            accessible by this instance.
     */
    public DOLParameterMetadata(final DOLPreparedStatement stm) {
	this.preparedStm = stm;
    }

    /**
     * {@inheritDoc}
     * 
     * @see java.sql.ParameterMetaData#getParameterClassName(int)
     */
    public String getParameterClassName(final int param) throws SQLException {
	if (this.parameterSet(param)) {
	    // return
	    // this.preparedStm.getParameters()[param].getJavaClassName();
	    return this.preparedStm.getParameters().get(param + "")
		    .getJavaClassName();
	} else
	    return null;
    }

    /**
     * {@inheritDoc}
     * 
     * @see java.sql.ParameterMetaData#getParameterCount()
     */
    public int getParameterCount() throws SQLException {
	if (this.parametersNull())
	    return 0;
	// return this.preparedStm.getParameters().length;
	return this.preparedStm.getParameters().size();
    }

    /**
     * {@inheritDoc}
     * 
     * @see java.sql.ParameterMetaData#getParameterMode(int)
     */
    public int getParameterMode(final int param) throws SQLException {
	return ParameterMetaData.parameterModeUnknown;
    }

    /**
     * {@inheritDoc}. Note that the integer returned are DorotyObjectEnum code
     * and not java.sql.Types
     * 
     * @see java.sql.ParameterMetaData#getParameterType(int)
     */
    public int getParameterType(final int param) throws SQLException {
	if (this.parameterSet(param))
	    // return this.preparedStm.getParameters()[param].getTypeCode();
	    return this.preparedStm.getParameters().get("" + param)
		    .getTypeCode();
	else
	    return DorotyObjectEnum.unknown.getTypeCode();
    }

    /**
     * {@inheritDoc}
     * 
     * @see java.sql.ParameterMetaData#getParameterTypeName(int)
     */
    public String getParameterTypeName(final int param) throws SQLException {
	if (this.parameterSet(param))
	    // return this.preparedStm.getParameters()[param].getType().name();
	    return this.preparedStm.getParameters().get("" + param).getType()
		    .name();
	else
	    return null;
    }

    /**
     * {@inheritDoc}
     * 
     * @see java.sql.ParameterMetaData#getPrecision(int)
     */
    public int getPrecision(final int param) throws SQLException {
	return 0;
    }

    /**
     * {@inheritDoc}
     * 
     * @see java.sql.ParameterMetaData#getScale(int)
     */
    public int getScale(final int param) throws SQLException {
	return 0;
    }

    /**
     * {@inheritDoc}
     * 
     * @see java.sql.ParameterMetaData#isNullable(int)
     */
    public int isNullable(final int param) throws SQLException {
	return ParameterMetaData.parameterNullableUnknown;
    }

    /**
     * {@inheritDoc}
     * 
     * @see java.sql.ParameterMetaData#isSigned(int)
     */
    public boolean isSigned(final int param) throws SQLException {
	return false;
    }

    /**
     * {@inheritDoc}
     * 
     * @see java.sql.Wrapper#isWrapperFor(java.lang.Class)
     */
    public boolean isWrapperFor(final Class<?> iface) throws SQLException {
	throw new SQLFeatureNotSupportedException(this.getClass()
		+ ".isWrapperFor(" + iface.getName() + ")?");
    }

    /**
     * {@inheritDoc}
     * 
     * @see java.sql.Wrapper#unwrap(java.lang.Class)
     */
    public <T> T unwrap(final Class<T> iface) throws SQLException {
	throw new SQLFeatureNotSupportedException(this.getClass() + ".unwrap("
		+ iface.getName() + ")?");
    }

    // ****************************Utility********************************************************************//
    /**
     * Checks if the array of parameter of the prepared statement is null or
     * not.
     * 
     * @return true if the parameters' array is null, false otherwise
     */
    private boolean parametersNull() {
	return (this.preparedStm.getParameters() == null);
    }

    /**
     * Checks if the given index is in the correct boundaries of the array of
     * parameter of the prepared statement associated with this Metadata.
     * Allowed values go from 1 to this.preparedStm.getParameters().length
     * 
     * @param paramIndex
     *            index of the parameter
     * @return true if paramIndex > 0 && paramIndex <=
     *         this.preparedStm.getParameters().length;
     */
    private boolean isValidIndex(final int paramIndex) {
	// return paramIndex > 0
	// && paramIndex <= this.preparedStm.getParameters().length;
	return paramIndex > 0
		&& paramIndex <= this.preparedStm.getParameters().size();
    }

    /**
     * Checks if the parameter at the given index has already been set or not.
     * 
     * @param paramIndex
     *            index of the parameter.
     * @return true if the parameter at the given index has already been set. If
     *         the index is out of the parameter's array bounds or if the
     *         parameter is not set returns false.
     */
    private boolean parameterSet(final int paramIndex) {
	// return (!this.parametersNull() && this.isValidIndex(paramIndex) &&
	// this.preparedStm
	// .getParameters()[paramIndex] != null);
	return (!this.parametersNull() && this.isValidIndex(paramIndex) && this.preparedStm
		.getParameters().get("" + paramIndex) != null);
    }
    // *************************************************************************************************************************//
}
