package eu.dnetlib.dlms.jdbc;

import java.sql.Array;
import java.sql.Blob;
import java.sql.CallableStatement;
import java.sql.Clob;
import java.sql.Connection;
import java.sql.DatabaseMetaData;
import java.sql.NClob;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLClientInfoException;
import java.sql.SQLException;
import java.sql.SQLFeatureNotSupportedException;
import java.sql.SQLWarning;
import java.sql.SQLXML;
import java.sql.Savepoint;
import java.sql.Statement;
import java.sql.Struct;
import java.util.Map;
import java.util.Properties;

import org.springframework.beans.factory.annotation.Required;

import eu.dnetlib.dlms.jdbc.server.AbstractDOLEngine;

/**
 * Connection implementation. This implementation assumes that the jdbc client and the server run on the same machine.
 * Communication is directly through an instance of class DummyDOLEngine.
 * 
 * @author lexis
 */
public class DorotyConnection implements Connection {

	/** Doroty metadata. */
	private DorotyMetaData metaData;
	/** Auto commit state. Default is true */
	private boolean autoCommit;
	/**
	 * connection holdability. Default is ResultSet.HOLD_CURSORS_OVER_COMMIT. TODO: what does this imply? What do i have
	 * to do in the result set to implement this?
	 */
	private int holdability;
	/** Transaction isolation level. Defualt is TRANSACTION_READ_COMMITTED; */
	private int transactionIsolation;
	/** True if this connection is closed. */
	private boolean closed;
	/** True if this connection is read only. Default is false. */
	private boolean readOnly;
	/** Engine that executes DOL commands. */
	private AbstractDOLEngine dolEngine;
	/** Factory used by statement created by this connection to create ResultSet instances. */
	private AbstractResultSetFactory resultSetFactory;

	/**
	 * No args constructor.
	 */
	protected DorotyConnection() {
		this.metaData = new DorotyMetaData(this);
		this.autoCommit = true;
		this.transactionIsolation = java.sql.Connection.TRANSACTION_READ_COMMITTED;
		this.readOnly = false;
		this.holdability = ResultSet.HOLD_CURSORS_OVER_COMMIT;
	}

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

	void setClosed(final boolean closed) {
		this.closed = closed;
	}

	void setDolEngine(final AbstractDOLEngine dolEngine) {
		this.dolEngine = dolEngine;
	}

	AbstractDOLEngine getDolEngine() {
		return this.dolEngine;
	}

	@Required
	public void setResultSetFactory(final AbstractResultSetFactory resultSetFactory) {
		this.resultSetFactory = resultSetFactory;
	}

	public AbstractResultSetFactory getResultSetFactory() {
		return this.resultSetFactory;
	}

	/**
	 * {@inheritDoc}
	 * 
	 * @see java.sql.Connection#clearWarnings()
	 */
	public void clearWarnings() throws SQLException {
		throw new SQLFeatureNotSupportedException();
	}

	/**
	 * {@inheritDoc}
	 * 
	 * @see java.sql.Connection#close()
	 */
	public void close() throws SQLException {
		this.closed = true;
		//this.setDolEngine(null);
	}

	/**
	 * {@inheritDoc}
	 * 
	 * @see java.sql.Connection#commit()
	 */
	public void commit() throws SQLException {
		if (this.getAutoCommit()) {
			throw new SQLException("Cannot execute commit on a Connection in auto-commit mode");
		}
		if (this.isClosed()) {
			throw new SQLException("Cannot execute commit on a closed Connection");
		}
		throw new SQLFeatureNotSupportedException("Sorry: Commit is not implemented yet!");
		/*
		 * TODO: Makes all changes made since the previous commit/rollback permanent and releases any database locks
		 * currently held by this Connection object.
		 */
	}

	/**
	 * {@inheritDoc}
	 * 
	 * @see java.sql.Connection#rollback()
	 */
	public void rollback() throws SQLException {
		if (this.getAutoCommit()) {
			throw new SQLException("Cannot rollback on a auto-commit enabled Connection");
		}
		throw new SQLFeatureNotSupportedException("Sorry: Rollback is not implemented yet!");
		// TODO: rollback!
	}

	/**
	 * {@inheritDoc}
	 * 
	 * @see java.sql.Connection#createArrayOf(java.lang.String, java.lang.Object[])
	 */
	public Array createArrayOf(final String typeName, final Object[] elements) throws SQLException {
		throw new SQLFeatureNotSupportedException();
	}

	/**
	 * {@inheritDoc}
	 * 
	 * @see java.sql.Connection#createBlob()
	 */
	public Blob createBlob() throws SQLException {
		throw new SQLFeatureNotSupportedException();
	}

	/**
	 * {@inheritDoc}
	 * 
	 * @see java.sql.Connection#createClob()
	 */
	public Clob createClob() throws SQLException {
		throw new SQLFeatureNotSupportedException();
	}

	/**
	 * {@inheritDoc}
	 * 
	 * @see java.sql.Connection#createNClob()
	 */
	public NClob createNClob() throws SQLException {
		throw new SQLFeatureNotSupportedException();
	}

	/**
	 * {@inheritDoc}
	 * 
	 * @see java.sql.Connection#createSQLXML()
	 */
	public SQLXML createSQLXML() throws SQLException {
		throw new SQLFeatureNotSupportedException();
	}

	/**
	 * {@inheritDoc}
	 * 
	 * @see java.sql.Connection#createStatement()
	 */
	public Statement createStatement() throws SQLException {
		DOLStatement stm = new DOLStatement(this);
		stm.setResultSetFactory(this.resultSetFactory);
		return stm;
	}

	/**
	 * {@inheritDoc}
	 * 
	 * @see java.sql.Connection#createStatement(int, int)
	 */
	public Statement createStatement(final int resultSetType, final int resultSetConcurrency) throws SQLException {
		throw new SQLFeatureNotSupportedException();
	}

	/**
	 * {@inheritDoc}
	 * 
	 * @see java.sql.Connection#createStatement(int, int, int)
	 */
	public Statement createStatement(final int resultSetType, final int resultSetConcurrency, final int resultSetHoldability) throws SQLException {
		throw new SQLFeatureNotSupportedException();
	}

	/**
	 * {@inheritDoc}
	 * 
	 * @see java.sql.Connection#createStruct(java.lang.String, java.lang.Object[])
	 */
	public Struct createStruct(final String typeName, final Object[] attributes) throws SQLException {
		throw new SQLFeatureNotSupportedException();
	}

	/**
	 * {@inheritDoc}
	 * 
	 * @see java.sql.Connection#getAutoCommit()
	 */
	public boolean getAutoCommit() throws SQLException {
		return this.autoCommit;
	}

	/**
	 * {@inheritDoc}
	 * 
	 * @see java.sql.Connection#getCatalog()
	 */
	public String getCatalog() throws SQLException {
		throw new SQLFeatureNotSupportedException();
	}

	/**
	 * {@inheritDoc}
	 * 
	 * @see java.sql.Connection#getClientInfo()
	 */
	public Properties getClientInfo() throws SQLException {
		throw new SQLFeatureNotSupportedException();
	}

	/**
	 * {@inheritDoc}
	 * 
	 * @see java.sql.Connection#getClientInfo(java.lang.String)
	 */
	public String getClientInfo(final String name) throws SQLException {
		throw new SQLFeatureNotSupportedException();
	}

	/**
	 * {@inheritDoc}
	 * 
	 * @see java.sql.Connection#getHoldability()
	 */
	public int getHoldability() throws SQLException {
		return this.holdability;
	}

	/**
	 * {@inheritDoc}
	 * 
	 * @see java.sql.Connection#getMetaData()
	 */
	public DatabaseMetaData getMetaData() throws SQLException {
		return this.metaData;
	}

	/**
	 * {@inheritDoc}
	 * 
	 * @see java.sql.Connection#getTransactionIsolation()
	 */
	public int getTransactionIsolation() throws SQLException {
		return this.transactionIsolation;
	}

	/**
	 * {@inheritDoc}
	 * 
	 * @see java.sql.Connection#getTypeMap()
	 */
	public Map<String, Class<?>> getTypeMap() throws SQLException {
		throw new SQLFeatureNotSupportedException();
	}

	/**
	 * {@inheritDoc}
	 * 
	 * @see java.sql.Connection#getWarnings()
	 */
	public SQLWarning getWarnings() throws SQLException {
		throw new SQLFeatureNotSupportedException();
	}

	/**
	 * {@inheritDoc}
	 * 
	 * @see java.sql.Connection#isClosed()
	 */
	public boolean isClosed() throws SQLException {
		return this.closed;
	}

	/**
	 * {@inheritDoc}
	 * 
	 * @see java.sql.Connection#isReadOnly()
	 */
	public boolean isReadOnly() throws SQLException {
		return this.readOnly;
	}

	/**
	 * {@inheritDoc}
	 * 
	 * @see java.sql.Connection#isValid(int)
	 */
	public boolean isValid(final int timeout) throws SQLException {
		/*
		 * TODO: Returns true if the connection has not been closed and is still valid. The driver shall submit a query
		 * on the connection or use some other mechanism that positively verifies the connection is still valid when
		 * this method is called.The query submitted by the driver to validate the connection shall be executed in the
		 * context of the current transaction.Parameters:timeout - - The time in seconds to wait for the database
		 * operation used to validate the connection to complete. If the timeout period expires before the operation
		 * completes, this method returns false. A value of 0 indicates a timeout is not applied to the database
		 * operation.
		 */
		return !this.closed;
	}

	/**
	 * {@inheritDoc}
	 * 
	 * @see java.sql.Connection#nativeSQL(java.lang.String)
	 */
	public String nativeSQL(final String sql) throws SQLException {
		throw new SQLFeatureNotSupportedException();
	}

	/**
	 * {@inheritDoc}
	 * 
	 * @see java.sql.Connection#prepareCall(java.lang.String)
	 */
	public CallableStatement prepareCall(final String sql) throws SQLException {
		throw new SQLFeatureNotSupportedException();
	}

	/**
	 * {@inheritDoc}
	 * 
	 * @see java.sql.Connection#prepareCall(java.lang.String, int, int)
	 */
	public CallableStatement prepareCall(final String sql, final int resultSetType, final int resultSetConcurrency) throws SQLException {
		throw new SQLFeatureNotSupportedException();
	}

	/**
	 * {@inheritDoc}
	 * 
	 * @see java.sql.Connection#prepareCall(java.lang.String, int, int, int)
	 */
	public CallableStatement prepareCall(final String sql, final int resultSetType, final int resultSetConcurrency, final int resultSetHoldability)
			throws SQLException {
		throw new SQLFeatureNotSupportedException();
	}

	/**
	 * {@inheritDoc}
	 * 
	 * @see java.sql.Connection#prepareStatement(java.lang.String)
	 */
	public PreparedStatement prepareStatement(final String sql) throws SQLException {
		DOLPreparedStatement stm = new DOLPreparedStatement(this, sql);
		stm.setResultSetFactory(this.resultSetFactory);
		return stm;
	}

	/**
	 * {@inheritDoc}
	 * 
	 * @see java.sql.Connection#prepareStatement(java.lang.String, int)
	 */
	public PreparedStatement prepareStatement(final String sql, final int autoGeneratedKeys) throws SQLException {
		throw new SQLFeatureNotSupportedException();
	}

	/**
	 * {@inheritDoc}
	 * 
	 * @see java.sql.Connection#prepareStatement(java.lang.String, int[])
	 */
	public PreparedStatement prepareStatement(final String sql, final int[] columnIndexes) throws SQLException {
		throw new SQLFeatureNotSupportedException();
	}

	/**
	 * {@inheritDoc}
	 * 
	 * @see java.sql.Connection#prepareStatement(java.lang.String, java.lang.String[])
	 */
	public PreparedStatement prepareStatement(final String sql, final String[] columnNames) throws SQLException {
		throw new SQLFeatureNotSupportedException();
	}

	/**
	 * {@inheritDoc}
	 * 
	 * @see java.sql.Connection#prepareStatement(java.lang.String, int, int)
	 */
	public PreparedStatement prepareStatement(final String sql, final int resultSetType, final int resultSetConcurrency) throws SQLException {
		throw new SQLFeatureNotSupportedException();
	}

	/**
	 * {@inheritDoc}
	 * 
	 * @see java.sql.Connection#prepareStatement(java.lang.String, int, int, int)
	 */
	public PreparedStatement prepareStatement(final String sql, final int resultSetType, final int resultSetConcurrency, final int resultSetHoldability)
			throws SQLException {
		throw new SQLFeatureNotSupportedException();
	}

	/**
	 * {@inheritDoc}
	 * 
	 * @see java.sql.Connection#releaseSavepoint(java.sql.Savepoint)
	 */
	public void releaseSavepoint(final Savepoint arg0) throws SQLException {
		throw new SQLFeatureNotSupportedException();

	}

	/**
	 * {@inheritDoc}
	 * 
	 * @see java.sql.Connection#rollback(java.sql.Savepoint)
	 */
	public void rollback(final Savepoint arg0) throws SQLException {
		throw new SQLFeatureNotSupportedException();
	}

	/**
	 * {@inheritDoc}
	 * 
	 * @see java.sql.Connection#setAutoCommit(boolean)
	 */
	public void setAutoCommit(final boolean autoCommit) throws SQLException {
		if (this.isClosed()) {
			throw new SQLException("Cannot set autocommit on a closed Connection");
		}
		if (autoCommit) {
			throw new SQLFeatureNotSupportedException("Sorry: Transactions are not implemented yet!");
		}
		this.autoCommit = autoCommit;
	}

	/**
	 * {@inheritDoc}
	 * 
	 * @see java.sql.Connection#setCatalog(java.lang.String)
	 */
	public void setCatalog(final String catalog) throws SQLException {
		throw new SQLFeatureNotSupportedException();

	}

	/**
	 * {@inheritDoc}
	 * 
	 * @see java.sql.Connection#setClientInfo(java.util.Properties)
	 */
	public void setClientInfo(final Properties clientInfo) throws SQLClientInfoException {
		throw new SQLClientInfoException();

	}

	/**
	 * {@inheritDoc}
	 * 
	 * @see java.sql.Connection#setClientInfo(java.lang.String, java.lang.String)
	 */
	public void setClientInfo(final String name, final String value) throws SQLClientInfoException {
		throw new SQLClientInfoException();

	}

	/**
	 * {@inheritDoc}
	 * 
	 * @see java.sql.Connection#setHoldability(int)
	 */
	public void setHoldability(final int holdability) throws SQLException {
		this.holdability = holdability;

	}

	/**
	 * {@inheritDoc}
	 * 
	 * @see java.sql.Connection#setReadOnly(boolean)
	 */
	public void setReadOnly(final boolean readOnly) throws SQLException {
		this.readOnly = readOnly;

	}

	/**
	 * {@inheritDoc}
	 * 
	 * @see java.sql.Connection#setSavepoint()
	 */
	public Savepoint setSavepoint() throws SQLException {
		throw new SQLFeatureNotSupportedException();
	}

	/**
	 * {@inheritDoc}
	 * 
	 * @see java.sql.Connection#setSavepoint(java.lang.String)
	 */
	public Savepoint setSavepoint(final String name) throws SQLException {
		throw new SQLFeatureNotSupportedException();
	}

	/**
	 * {@inheritDoc}
	 * 
	 * @see java.sql.Connection#setTransactionIsolation(int)
	 */
	public void setTransactionIsolation(final int level) throws SQLException {
		if (level != Connection.TRANSACTION_READ_UNCOMMITTED && level != Connection.TRANSACTION_READ_COMMITTED
				&& level != Connection.TRANSACTION_REPEATABLE_READ && level != Connection.TRANSACTION_SERIALIZABLE)
			throw new SQLException();
		else
			this.transactionIsolation = level;

	}

	/**
	 * {@inheritDoc}
	 * 
	 * @see java.sql.Connection#setTypeMap(java.util.Map)
	 */
	public void setTypeMap(final Map<String, Class<?>> arg0) throws SQLException {
		throw new SQLFeatureNotSupportedException();

	}

	/**
	 * {@inheritDoc}
	 * 
	 * @see java.sql.Wrapper#isWrapperFor(java.lang.Class)
	 */
	public boolean isWrapperFor(final Class<?> iface) throws SQLException {
		throw new SQLException("Do I need to be a wrapper of class " + iface + " ?");
	}

	/**
	 * {@inheritDoc}
	 * 
	 * @see java.sql.Wrapper#unwrap(java.lang.Class)
	 */
	public <T> T unwrap(final Class<T> iface) throws SQLException {
		throw new SQLException("Do I need to call unwrap with parameter class " + iface + " ?");
	}

}
