/*-----------------------------------------------------------------------------
 *
 * Enhydra JdbcS
 * The Initial Developer of the Original Code is Lutris Technologies Inc.
 * Portions created by Lutris are Copyright 1997-2000 Lutris Technologies Inc.
 * All Rights Reserved.
 *
 * The contents of this file are subject to the Enhydra Public License
 * Version 1.1 (the "License"); you may not use this file except in compliance
 * with the License. You may obtain a copy of the License at
 * www.enhydra.org/license/epl.html
 *
 * Software distributed under the License is distributed on an "ASIS" basis,
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for
 * the specific language governing rights and limitations under the License.
 *
 * -----------------------------------------------------------------------------
 * $Id$
 * -----------------------------------------------------------------------------
 */
package org.enhydra.jdbc.standard;

import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.sql.Statement;
import java.sql.CallableStatement;
import java.sql.ResultSet;
import java.util.Hashtable;
import javax.transaction.Transaction;
import javax.transaction.TransactionManager;

import org.enhydra.jdbc.util.LRUCache;

public class StandardXAConnectionHandle extends StandardConnectionHandle {
    
    private boolean		globalTransaction;	// true if a global transaction is in effect
    public TransactionManager	transactionManager;
    public Transaction		tx;
    public StandardXAConnection xacon;

    /**
     * Constructor
     */
    public StandardXAConnectionHandle (StandardXAConnection pooledCon, Hashtable preparedStatementCache, int preparedStmtCacheSize, TransactionManager tm) {
	super (pooledCon, preparedStatementCache, preparedStmtCacheSize); // setup StandardXAConnectionHandle
	xacon = pooledCon;
	transactionManager = tm;
	// This will have set the Connection to the current Connection.
	// However this might change if a global transaction gets selected.
    } // constructor

    public void setTransactionManager(TransactionManager tm) {
	this.transactionManager = tm;
    } // method setTransactionManager

    /**
     * Called by the StandardXADataSource when a global transaction
     * gets associated with this connection.
     */
    void setGlobalTransaction (boolean setting) throws SQLException {
	globalTransaction = setting;			// set global flag
	con = pooledCon.getPhysicalConnection();	// get the real connection
	setupPreparedStatementCache();
	super.setAutoCommit (!setting);			// commits must be done by transaction manager
    } // method setGlobalTransaction
    
    public void setAutoCommit(boolean autoCommit) throws SQLException {
	if (globalTransaction)			// if taking part in a global transaction
	    throw new SQLException ("setAutoCommit:This connection is part of a global transaction");
	super.setAutoCommit(autoCommit);	// delegation
    } // method setAutoCommit
    
    public void commit() throws SQLException {
	if (globalTransaction)			// if taking part in a global transaction
	    throw new SQLException ("commit:This connection is part of a global transaction");
	super.commit();			// delegation
    } // method commit
    
    public void rollback() throws SQLException {
	if (globalTransaction)			// if taking part in a global transaction
	    throw new SQLException ("rollback:This connection is part of a global transaction");
	super.rollback();			// delegation
    } // method rollback

    synchronized PreparedStatement checkPreparedCache (String sql, int type, int concurrency, Object lookupKey) throws SQLException {
	PreparedStatement ret = null;				// the return value
	// NOTE - We include the Connection in the lookup key. This has no 
	// effect here but is needed by StandardXAConnection where the the physical 
	// Connection used can vary over time depending on the global transaction.
	if (preparedStatementCache != null) {
	    Object obj = preparedStatementCache.get (lookupKey);// see if there's a PreparedStatement already
	    if (obj != null) {					// if there is
		ret = (PreparedStatement)obj;		// use as return value
		try {
		    ret.clearParameters();			// make it look like new
		} catch (SQLException e) {
		    // Bad statement, so we have to create a new one
		    ret = createPreparedStatement(sql, type, concurrency); // create new prepared statement
		}
		preparedStatementCache.remove(lookupKey);	// make sure it cannot be re-used
		inUse.put (lookupKey, ret);			// make sure it gets reused by later delegates
	    } else {					// no PreparedStatement ready
		ret = createPreparedStatement(sql, type, concurrency); // create new prepared statement
		inUse.put (lookupKey, ret);			// will get saved in prepared statement cache
	    } 
	} else {
	    ret = createPreparedStatement(sql, type, concurrency); // create new prepared statement
	}
	// We don't actually give the application a real PreparedStatement. Instead
	// they get a StandardPreparedStatement that delegates everything except
	// PreparedStatement.close();

	return ret;
    } // method checkPreparedCache

    /**
     * Creates a PreparedStatement for the given SQL. If possible, the
     * statement is fetched from the cache.
     */
    public PreparedStatement prepareStatement(String sql) throws SQLException {
	return new StandardXAPreparedStatement (this, sql, 0, 0);
    } // method PreparedStatement

    /**
     * Creates a PreparedStatement for the given SQL, type and concurrency. 
     * If possible, the statement is fetched from the cache.
     */
    public PreparedStatement prepareStatement(String sql,int resultSetType,int resultSetConcurrency) throws SQLException {
	return new StandardXAPreparedStatement (this, sql, resultSetType, resultSetConcurrency);
    } // prepareStatement
   
 
     /**
      * Creates a CallableStatement for the given SQL, result set type and concurency
      */
     public CallableStatement prepareCall(String sql, int resultSetType, int resultSetConcurrency) throws SQLException {
 	return new StandardXACallableStatement(this, sql, resultSetType, resultSetConcurrency);
     }
  
     /**
      * Creates a CallableStatement for the given SQL
      */
     public CallableStatement prepareCall(String sql) throws SQLException {
 	return new StandardXACallableStatement(this, sql, 0, 0);
     }
        
     public Statement createStatement() throws SQLException {
 	    return new StandardXAStatement(this, 0, 0);
     } // method createStatement
      
     public Statement createStatement(int resultSetType, int resultSetConcurrency) throws SQLException {
 	    return new StandardXAStatement(this, resultSetType, resultSetConcurrency);
    } // method createStatement

} // class StandardXAConnectionHandle
