package org.seasar.extension.jta;

import javax.transaction.HeuristicMixedException;
import javax.transaction.HeuristicRollbackException;
import javax.transaction.InvalidTransactionException;
import javax.transaction.NotSupportedException;
import javax.transaction.RollbackException;
import javax.transaction.Status;
import javax.transaction.SystemException;
import javax.transaction.Transaction;
import javax.transaction.TransactionManager;

/**
 * ExtendedTransactionManager
 *
 */
public interface ExtendedTransactionManager extends TransactionManager {

    /**
     * @see javax.transaction.TransactionManager#begin()
     */
    @Override
    default void begin() throws NotSupportedException, SystemException {
        ExtendedTransaction tx = getCurrent();
        if (tx != null) {
            throw new NotSupportedException("tx is not null.");
        }
        tx = attachNewTransaction();
        tx.begin();
    }

    /**
     * @see javax.transaction.TransactionManager#commit()
     */
    @Override
    default void commit() throws RollbackException, HeuristicMixedException,
            HeuristicRollbackException, SecurityException,
            IllegalStateException, SystemException {
        final ExtendedTransaction tx = getCurrent();
        if (tx == null) {
            throw new IllegalStateException("tx is null.");
        }
        try {
            tx.commit();
        } finally {
            setCurrent(null);
        }
    }

    /**
     * @see javax.transaction.TransactionManager#suspend()
     */
    @Override
    default Transaction suspend() throws SystemException {
        final ExtendedTransaction tx = getCurrent();
        if (tx == null) {
            throw new IllegalStateException("tx is null.");
        }
        try {
            tx.suspend();
        } finally {
            setCurrent(null);
        }
        return tx;
    }

    /**
     * @see javax.transaction.TransactionManager#resume(javax.transaction.Transaction)
     */
    @Override
    default void resume(final Transaction resumeTx) throws InvalidTransactionException,
            SystemException {
        final ExtendedTransaction tx = getCurrent();
        if (tx != null) {
            throw new IllegalStateException("tx is not null.");
        }
        if (ExtendedTransaction.class.isInstance(resumeTx)) {
            ((ExtendedTransaction) resumeTx).resume();
            setCurrent((ExtendedTransaction) resumeTx);
        }
    }

    /**
     * @see javax.transaction.TransactionManager#rollback()
     */
    @Override
    default void rollback() throws SystemException {

        final ExtendedTransaction tx = getCurrent();
        if (tx == null) {
            throw new IllegalStateException("tx is null.");
        }
        try {
            tx.rollback();
        } finally {
            setCurrent(null);
        }
    }

    /**
     * @see javax.transaction.TransactionManager#setRollbackOnly()
     */
    @Override
    default void setRollbackOnly() throws SystemException {
        final ExtendedTransaction tx = getCurrent();
        if (tx == null) {
            throw new IllegalStateException("tx is null.");
        }
        tx.setRollbackOnly();
    }

    /**
     * @see javax.transaction.TransactionManager#setTransactionTimeout(int)
     */
    @Override
    default void setTransactionTimeout(final int timeout) throws SystemException {
        return;
    }

    /**
     * @see javax.transaction.TransactionManager#getStatus()
     */
    @Override
    default int getStatus() {
        final ExtendedTransaction tx = getCurrent();
        if (tx != null) {
            try {
                return tx.getStatus();
            } catch (final SystemException e) {
                throw new RuntimeException(e);
            }
        }
        return Status.STATUS_NO_TRANSACTION;
    }

    /**
     * @see javax.transaction.TransactionManager#getTransaction()
     */
    @Override
    default Transaction getTransaction() {
        return getCurrent();
    }

    /**
     * 現在のスレッドに関連づけられているトランザクションを返します。
     *
     * @return 現在のスレッドに関連づけられているトランザクション
     */
    ExtendedTransaction getCurrent();

    /**
     * トランザクションを現在のスレッドに関連づけます。
     *
     * @param current 現在のスレッドに関連づけるトランザクション
     */
    void setCurrent(ExtendedTransaction current);

    /**
     * 新しいトランザクションを作成して現在のスレッドに関連づけます。
     *
     * @return 現在のスレッドに関連づけられたトランザクション
     */
    ExtendedTransaction attachNewTransaction();

    /**
     * トランザクションを作成して返します。
     *
     * @return トランザクション
     */
    ExtendedTransaction createTransaction();

}
