package org.mockejb;

import java.io.Serializable;
import java.security.Identity;
import java.security.Principal;

import java.util.Properties;

import javax.ejb.*;
import javax.transaction.Status;
import javax.transaction.SystemException;
import javax.transaction.UserTransaction;

/**
 * Provides implementation of <code>javax.ejb.SessionContext</code>,
 * <code>javax.ejb.MessageDrivenContext</code> and <code>javax.ejb.EntityContext</code>
 * as well as some extra convenience methods.
 * 
 * @author Alexander Ananiev
 */
public class MockEjbContext implements SessionContext, MessageDrivenContext, EntityContext, Serializable {

    private Object homeProxy;
    private Object ejbObjectProxy;
    private Object primaryKey;
    
    
    MockEjbContext( final Object homeProxy ){
        this.homeProxy = homeProxy;
    }


    void setEjbObjectProxy( final Object ejbObjectProxy ){
        this.ejbObjectProxy = ejbObjectProxy;
    }

    /**
     * Tests if the business interface for this context's bean is remote interface 
     * meaning that it extends <code>EJBObject</code>.
     * <br>This is useful for exception handling and in other places where local/remote
     * interfaces behave differently. 
     * @return true if the business interface of this context's EJB implements remote 
     * interface. Always returns false for message-driven beans.
     */
    public boolean isRemote(){
        
        if ( ejbObjectProxy == null )
            throw new IllegalStateException( "Can't determine the type. Most likely this EJB has not bean created yet");
        
        return ( EJBObject.class.isAssignableFrom( ejbObjectProxy.getClass() ) );       
    }
    
    
    /** 
     * @see javax.ejb.EJBContext#getEJBHome()
     */
    public EJBHome getEJBHome() {
        if ( homeProxy == null )
            throw new IllegalStateException( "This EJB does not have Home interface");

        return (EJBHome) homeProxy;
    }

    /**
     *
     * @see javax.ejb.EJBContext#getEJBLocalHome()
     */
    public EJBLocalHome getEJBLocalHome() {
 
        if ( homeProxy == null )
            throw new IllegalStateException( "This EJB does not have Local Home interface");
 
        return (EJBLocalHome) homeProxy;
    }

    /**
     * Always returns empty Properties object. Bean-scoped environment 
     * is not supported directly.
     * @see javax.ejb.EJBContext#getEnvironment()
     */
    public Properties getEnvironment() {
        
        return new Properties();
    }

    /**
     * This method is not supported
     * @see javax.ejb.EJBContext#getCallerIdentity()
     */
    public Identity getCallerIdentity() {
        throwMethodNotImplemented( "getCallerIdentity");
        return null;
    }

    /**
     * Returns the principal that was logged in using 
     * MockContainer.login. Returns the anonymous principal if 
     * login was not called. 
     *  
     * @return principal of the logged in user or anonymous principal
     * 
     * @see javax.ejb.EJBContext#getCallerPrincipal()
     */
    public Principal getCallerPrincipal() {
        return MockContainer.getUser();
    }

    /** 
     * This method is not supported
     * @see javax.ejb.EJBContext#isCallerInRole(java.security.Identity)
     */
    public boolean isCallerInRole(Identity arg0) {
        throwMethodNotImplemented( "isCallerInRole");
        return false;
    }

    /**
     * @see javax.ejb.EJBContext#isCallerInRole(java.lang.String)
     */
    public boolean isCallerInRole(String role) {
        return MockContainer.getUser().hasRole( role );
    }

    /**
     * Calls {@link TransactionManager} to get the
     * <code>javax.transaction.UserTransaction</code> object.
     * @return <code>javax.transaction.UserTransaction</code> object  
     * @see javax.ejb.EJBContext#getUserTransaction()
     */
    public UserTransaction getUserTransaction() throws IllegalStateException {

        UserTransaction tran = TransactionManager.getUserTransaction();
        
        return tran;
    }

    /**
     * @see javax.ejb.EJBContext#setRollbackOnly()
     */
    public void setRollbackOnly() throws IllegalStateException {
        UserTransaction tran = getUserTransaction();
        try {
            tran.setRollbackOnly();
        }
        catch( SystemException se ){
            throw new EJBException("Error trying to call setRollbackOnly on transaction", se); 
        }

    }

    /**
     * @see javax.ejb.EJBContext#getRollbackOnly()
     */
    public boolean getRollbackOnly() throws IllegalStateException {
        
        int status = Status.STATUS_UNKNOWN;
        UserTransaction tran = getUserTransaction();
        try {
            status = tran.getStatus();   
        }
        catch( SystemException se ){
            throw new EJBException("Error trying to call getStatus on transaction", se); 
        }
        
        return( status == Status.STATUS_MARKED_ROLLBACK );
    }

    /**
     * Obtains a reference to the EJB local object that is currently associated with the instance.
     * @return the EJB object currently associated with the instance
     */
    public EJBLocalObject getEJBLocalObject() throws IllegalStateException {
        return (EJBLocalObject)ejbObjectProxy;
    }

    /**
     * Obtains a reference to the EJB object that is currently associated with the instance.
     * @return the EJB object currently associated with the instance
     */
    public EJBObject getEJBObject() throws IllegalStateException {
        return (EJBObject)ejbObjectProxy;
    }
    
    // EJB 2.1 methods for the future 
    /*
    public javax.xml.rpc.handler.MessageContext getMessageContext() throws IllegalStateException{
        throwMethodNotImplemented( "getMessageContext");
        return null;
    }
    
    public TimerService getTimerService() throws IllegalStateException {    
        throwMethodNotImplemented( "getMessageContext");
        return null;
    }
    */
    
    /**
      * Helper method to throw NotImplementedException for this class
      * @param methodName
      */
     private void throwMethodNotImplemented( String methodName ){

         throw new MethodNotImplementedException( methodName, 
             this.getClass().getName() );

     }


    /**
     * Returns the primary key for entity beans. If the context is assotiated with 
     * Session bean or MDB, returns null. 
     * MockEJB does not automatically handles primary keys for 
     * entity beans. Since "create" returns null, you need to intercept "create" methods
     * of your entity bean and return the real PK. 
     * 
     * @see javax.ejb.EntityContext#getPrimaryKey()
     */
    public Object getPrimaryKey() throws IllegalStateException {
        return primaryKey;
    }
    
    public void setPrimaryKey( Object primaryKey ){
        this.primaryKey = primaryKey;
    }



}