package eu.dnetlib.dlms.jdbc;

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

/**
 * 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();
		else
			return null;
	}

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

	/**
	 * {@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();
		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();
		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;
	}

	/**
	 * 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);
	}
	//*************************************************************************************************************************//
}
