package eu.dnetlib.dlms.jdbc;

import java.net.URL;
import java.sql.Date;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import java.util.Collection;

import eu.dnetlib.dlms.jdbc.serialization.RSMetadataWrapper;

/**
 * @author lexis
 * 
 */
public class WSRemoteMetaData implements ResultSetMetaData {

	/**
	 * Wrapper holding metatada information.
	 */
	private RSMetadataWrapper metaData;

	/**
	 * {@inheritDoc}
	 * 
	 * @see java.sql.ResultSetMetaData#getColumnCount()
	 */
	public int getColumnCount() throws SQLException {
		return this.metaData.getColumnCount();
	}

	/**
	 * {@inheritDoc}
	 * 
	 * @see java.sql.ResultSetMetaData#getColumnLabel(int)
	 */
	public String getColumnLabel(final int column) throws SQLException {
		return this.getColumnName(column);
	}

	/**
	 * {@inheritDoc}
	 * 
	 * @see java.sql.ResultSetMetaData#getColumnName(int)
	 */
	public String getColumnName(final int column) throws SQLException {
		return this.metaData.getColumnNames().get(column - 1);
	}

	/**
	 * {@inheritDoc}.
	 * 
	 * @see java.sql.ResultSetMetaData#getColumnType(int)
	 */
	public int getColumnType(final int column) throws SQLException {
		return this.metaData.getColumnTypeCodes().get(column - 1);
	}

	/**
	 * {@inheritDoc}. Returns the fully-qualified name of the Java class whose instances are manufactured if the method
	 * ResultSet.getObject is called to retrieve a value from the column. ResultSet.getObject may return a subclass of
	 * the class returned by this method. This method is used by getColumnClassName(int) method.
	 * 
	 * @see java.sql.ResultSetMetaData#getColumnClassName(int)
	 */
	public String getColumnClassName(final int column) throws SQLException {
		return this.getClassName(this.metaData.getColumnTypeCodes().get(column - 1));
	}

	/**
	 * {@inheritDoc}.type name used by the database. If the column type is a user-defined type, then a fully-qualified
	 * type name is returned.
	 * 
	 * @see java.sql.ResultSetMetaData#getColumnTypeName(int)
	 */
	public String getColumnTypeName(final int column) throws SQLException {
		return this.getTypeName(this.metaData.getColumnTypeCodes().get(column - 1));
	}

	public void setMetaData(final RSMetadataWrapper metaData) {
		this.metaData = metaData;
	}

	/**
	 * Returns the fully-qualified name of the Java class whose instances are manufactured if the method
	 * ResultSet.getObject is called to retrieve a value from the column. ResultSet.getObject may return a subclass of
	 * the class returned by this method. This method is used by getColumnClassName(int) method. TODO: devi fare in modo
	 * che la getObject del result set possa tornare il contenuto di qualsiasi colonna, non solo object e collection.
	 * 
	 * @param column
	 * @return
	 */
	private String getClassName(final int typeCode) {
		switch (typeCode) {
		case 0:
		case 1:
			return String.class.getCanonicalName();
		case 2:
		case 3:
			return Integer.class.getCanonicalName();
		case 4:
		case 5:
			return Date.class.getCanonicalName();
		case 6:
		case 7:
			return Boolean.class.getCanonicalName();
		case 8:
			return URL.class.getCanonicalName();
		case 9:
			return Long.class.getCanonicalName();
		case 10:
		case 11:
		case 12:
		case 13:
			return InformationObject.class.getCanonicalName();
		case 14:
		case 15:
			return Double.class.getCanonicalName();
		case 16:
			//collection fields ar Collection<Map<String, Object>>
			return Collection.class.getCanonicalName();
		case 17:
			//Set
			return InformationObject.class.getCanonicalName();
		default:
			return null;
		}

	}

	/**
	 * Gets the name of the type having typeCode as code. This method is used by getColumnTypeName method.
	 * 
	 * @param typeCode
	 *            code of the column type
	 * @return the name of the type, 'unknown' if the code is not known
	 * 
	 */
	private String getTypeName(final int typeCode) {
		switch (typeCode) {
		case 0:
		case 1:
			return String.class.getCanonicalName();
		case 2:
		case 3:
			return int.class.getCanonicalName();
		case 4:
		case 5:
			return Date.class.getCanonicalName();
		case 6:
		case 7:
			return boolean.class.getCanonicalName();
		case 8:
			return URL.class.getCanonicalName();
		case 9:
			return long.class.getCanonicalName();
		case 10:
			return "Atom";
		case 11:
			return "Relation";
		case 12:
			return "Structure";
		case 13:
			return "LLDigitalObject";
		case 14:
		case 15:
			return double.class.getCanonicalName();
		case 16:
			return "DescriptionValueCollection";
		case 17:
			return "Set";
		default:
			return null;
		}

	}

	public RSMetadataWrapper getMetaData() {
		return this.metaData;
	}

	private void unimplemented() throws SQLException {
		throw new SQLException("Currently unimplemented, stay tuned");
	}

	/**
	 * Default constructor.
	 */
	public WSRemoteMetaData() {
		super();
		//left blank
	}

	/**
	 * Constructor.
	 * 
	 * @param metaData
	 *            RSMetadataWrapper
	 */
	public WSRemoteMetaData(final RSMetadataWrapper metaData) {
		super();
		this.metaData = metaData;
	}

	/**
	 * {@inheritDoc}
	 * 
	 * @see java.sql.ResultSetMetaData#getCatalogName(int)
	 */
	public String getCatalogName(final int column) throws SQLException {
		this.unimplemented();
		return null;
	}

	/**
	 * {@inheritDoc}
	 * 
	 * @see java.sql.ResultSetMetaData#getColumnDisplaySize(int)
	 */
	public int getColumnDisplaySize(final int column) throws SQLException {
		this.unimplemented();
		return 0;
	}

	/**
	 * {@inheritDoc}
	 * 
	 * @see java.sql.ResultSetMetaData#getPrecision(int)
	 */
	public int getPrecision(final int column) throws SQLException {
		this.unimplemented();
		return 0;
	}

	/**
	 * {@inheritDoc}
	 * 
	 * @see java.sql.ResultSetMetaData#getScale(int)
	 */
	public int getScale(final int column) throws SQLException {
		this.unimplemented();
		return 0;
	}

	/**
	 * {@inheritDoc}
	 * 
	 * @see java.sql.ResultSetMetaData#getSchemaName(int)
	 */
	public String getSchemaName(final int column) throws SQLException {
		this.unimplemented();
		return null;
	}

	/**
	 * {@inheritDoc}
	 * 
	 * @see java.sql.ResultSetMetaData#getTableName(int)
	 */
	public String getTableName(final int column) throws SQLException {
		this.unimplemented();
		return null;
	}

	/**
	 * {@inheritDoc}
	 * 
	 * @see java.sql.ResultSetMetaData#isAutoIncrement(int)
	 */
	public boolean isAutoIncrement(final int column) throws SQLException {
		this.unimplemented();
		return false;
	}

	/**
	 * {@inheritDoc}
	 * 
	 * @see java.sql.ResultSetMetaData#isCaseSensitive(int)
	 */
	public boolean isCaseSensitive(final int column) throws SQLException {
		this.unimplemented();
		return false;
	}

	/**
	 * {@inheritDoc}
	 * 
	 * @see java.sql.ResultSetMetaData#isCurrency(int)
	 */
	public boolean isCurrency(final int column) throws SQLException {
		this.unimplemented();
		return false;
	}

	/**
	 * {@inheritDoc}
	 * 
	 * @see java.sql.ResultSetMetaData#isDefinitelyWritable(int)
	 */
	public boolean isDefinitelyWritable(final int column) throws SQLException {
		this.unimplemented();
		return false;
	}

	/**
	 * {@inheritDoc}
	 * 
	 * @see java.sql.ResultSetMetaData#isNullable(int)
	 */
	public int isNullable(final int column) throws SQLException {
		this.unimplemented();
		return 0;
	}

	/**
	 * {@inheritDoc}
	 * 
	 * @see java.sql.ResultSetMetaData#isReadOnly(int)
	 */
	public boolean isReadOnly(final int column) throws SQLException {
		this.unimplemented();
		return false;
	}

	/**
	 * {@inheritDoc}
	 * 
	 * @see java.sql.ResultSetMetaData#isSearchable(int)
	 */
	public boolean isSearchable(final int column) throws SQLException {
		this.unimplemented();
		return false;
	}

	/**
	 * {@inheritDoc}
	 * 
	 * @see java.sql.ResultSetMetaData#isSigned(int)
	 */
	public boolean isSigned(final int column) throws SQLException {
		this.unimplemented();
		return false;
	}

	/**
	 * {@inheritDoc}
	 * 
	 * @see java.sql.ResultSetMetaData#isWritable(int)
	 */
	public boolean isWritable(final int column) throws SQLException {
		this.unimplemented();
		return false;
	}

	/**
	 * {@inheritDoc}
	 * 
	 * @see java.sql.Wrapper#isWrapperFor(java.lang.Class)
	 */
	public boolean isWrapperFor(final Class<?> iface) throws SQLException {
		this.unimplemented();
		return false;
	}

	/**
	 * {@inheritDoc}
	 * 
	 * @see java.sql.Wrapper#unwrap(java.lang.Class)
	 */
	public <T> T unwrap(final Class<T> iface) throws SQLException {
		this.unimplemented();
		return null;
	}

}
