/** 
 * 
 * Copyright 2004 Hiram Chirino
 * 
 * Licensed under the Apache License, Version 2.0 (the "License"); 
 * you may not use this file except in compliance with the License. 
 * You may obtain a copy of the License at 
 * 
 * http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, 
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 
 * See the License for the specific language governing permissions and 
 * limitations under the License. 
 * 
 **/

package org.activemq.store.journal;

import java.util.Iterator;

import javax.jms.JMSException;
import javax.transaction.xa.XAException;

import org.activeio.journal.RecordLocation;
import org.activemq.message.ActiveMQXid;
import org.activemq.store.TransactionStore;
import org.activemq.util.TransactionTemplate;

import EDU.oswego.cs.dl.util.concurrent.ConcurrentHashMap;

/**
 */
public class JournalTransactionStore implements TransactionStore {

    private final JournalPersistenceAdapter peristenceAdapter;
    final TransactionStore checkpointStore;
    
    ConcurrentHashMap inflightTransactions = new ConcurrentHashMap();
    ConcurrentHashMap preparedTransactions = new ConcurrentHashMap();
    private TransactionTemplate transactionTemplate;

    public JournalTransactionStore(JournalPersistenceAdapter adapter, TransactionStore checkpointStore) {
        this.peristenceAdapter = adapter;
        this.checkpointStore = checkpointStore;
		this.transactionTemplate = new TransactionTemplate(adapter);        
    }
    
	public void transactionStarted(Object txid, RecordLocation txStartLocation) {
	    if( inflightTransactions.get(txid)==null ) {
	        inflightTransactions.put(txid, txStartLocation);
	    }
    }

    public void prepare(Object txid) throws XAException {

		Object location = inflightTransactions.remove(txid);
        if (location == null)
            return;

        peristenceAdapter.writeTxCommand(new TxCommand(TxCommand.XA_PREPARE,txid,false), true);
        preparedTransactions.put(txid, location);
        
        checkpointStore.prepare(txid);
    }

    public void commit(Object txid, boolean wasPrepared) throws XAException {
        Object location;
        if( wasPrepared ) {
            location = preparedTransactions.remove(txid);
        } else {
            location = inflightTransactions.remove(txid);
        }        
        if( location == null )
            return;
        
        if(txid.getClass()==ActiveMQXid.class) {
            peristenceAdapter.writeTxCommand(new TxCommand(TxCommand.XA_COMMIT,txid,wasPrepared), true);
        } else {
            peristenceAdapter.writeTxCommand(new TxCommand(TxCommand.LOCAL_COMMIT,txid,wasPrepared), true);
        }
        
        checkpointStore.commit(txid, wasPrepared);
    }

    public void rollback(Object txid) throws XAException {
        if(txid.getClass()==ActiveMQXid.class) {
            peristenceAdapter.writeTxCommand(new TxCommand(TxCommand.XA_ROLLBACK,txid,false), true);
        } else {
            peristenceAdapter.writeTxCommand(new TxCommand(TxCommand.LOCAL_ROLLBACK,txid,false), true);
        }
        
        preparedTransactions.remove(txid);
        inflightTransactions.remove(txid);        

        checkpointStore.rollback(txid);
    }

    public void recover(RecoveryListener listener) throws XAException {
        checkpointStore.recover(listener);
    }

    public void start() throws JMSException {
    }

    public void stop() throws JMSException {
    }
    
	/**
	 * @return
	 * @throws JMSException
	 */
	public RecordLocation checkpoint() throws JMSException {
		// Nothing really to checkpoint.. since, we don't
	    // buffer tx opperations in the journal to do a batch 
	    // apply to the checkpointStore.
	    
	    // But we keep track of the first location of an operation 
	    // that was associated with an active tx.  The journal can not
	    // roll over active tx records.
	    RecordLocation rc=null;
	    for (Iterator iter = inflightTransactions.values().iterator(); iter.hasNext();) {
            RecordLocation location = (RecordLocation) iter.next();
			if( rc==null || rc.compareTo(location)<0 ) {
			    rc = location;
			}
        }
	    for (Iterator iter = preparedTransactions.values().iterator(); iter.hasNext();) {
            RecordLocation location = (RecordLocation) iter.next();
			if( rc==null || rc.compareTo(location)<0 ) {
			    rc = location;
			}
        }
	    return rc;
	}
    

}
