/*
 * JBoss, the OpenSource J2EE webOS
 *
 * Distributable under LGPL license.
 * See terms of license at gnu.org.
 */
package org.jboss.ejb3.entity;

import java.io.Externalizable;
import java.io.IOException;
import java.io.ObjectInput;
import java.io.ObjectOutput;
import java.util.Hashtable;
import javax.naming.InitialContext;
import javax.naming.NamingException;
import javax.persistence.EntityManager;
import javax.persistence.EntityTransaction;
import javax.persistence.FlushModeType;
import javax.persistence.PersistenceContextType;
import org.hibernate.Session;
import org.hibernate.ejb.HibernateEntityManager;
import org.jboss.ejb3.Container;
import org.jboss.ejb3.EJB3Util;
import org.jboss.ejb3.EJBContainer;

/**
 * EntityManager a managed extended persistence context.
 *
 * @author <a href="mailto:bill@jboss.org">Bill Burke</a>
 */
public class ExtendedEntityManager implements EntityManager, HibernateSession, Externalizable, ExtendedPersistenceContext
{
   private EntityManager persistenceContext;
   private transient ManagedEntityManagerFactory factory;
   private boolean isTransient = false;
   private Hashtable initialContextProperties;

   public ExtendedEntityManager(boolean isTransient, ManagedEntityManagerFactory factory, Hashtable initialContextProperties)
   {
      this(isTransient, factory, initialContextProperties, factory.createEntityManager(PersistenceContextType.EXTENDED));
   }

   public ExtendedEntityManager(boolean isTransient, ManagedEntityManagerFactory factory, Hashtable initialContextProperties, EntityManager pc)
   {
      this.isTransient = isTransient;
      this.factory = factory;
      persistenceContext = pc;
      this.initialContextProperties = initialContextProperties;
   }

   public ExtendedEntityManager()
   {
   }

   public ManagedEntityManagerFactory getFactory()
   {
      return factory;
   }

   public EntityManager getPersistenceContext()
   {
      return persistenceContext;
   }

   public void writeExternal(ObjectOutput out) throws IOException
   {
      out.writeBoolean(isTransient);
      out.writeObject(initialContextProperties);
      if (!isTransient)
      {
         out.writeObject(persistenceContext);
      }
   }

   public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException
   {
      isTransient = in.readBoolean();
      try
      {
         initialContextProperties = (Hashtable)in.readObject();
         InitialContext ctx = EJB3Util.getInitialContext(initialContextProperties);
         factory = (ManagedEntityManagerFactory) ctx.lookup(Container.ENC_CTX_NAME + "/env/" + EJBContainer.ENTITY_MANAGER_FACTORY);
      }
      catch (NamingException e)
      {
         throw new RuntimeException(e);
      }
      if (!isTransient)
      {
         persistenceContext = (EntityManager) in.readObject();
      }
      else
      {
         persistenceContext = factory.createEntityManager(PersistenceContextType.EXTENDED);
      }
   }

   // delegates

   public Session getHibernateSession()
   {
      if (persistenceContext instanceof HibernateEntityManager)
      {
         return ((HibernateEntityManager) persistenceContext).getSession();
      }
      throw new RuntimeException("ILLEGAL ACTION:  Not a Hibernate persistence provider");
   }

   public <T> T getReference(Class<T> entityClass, Object primaryKey)
   {
      return persistenceContext.getReference(entityClass, primaryKey);
   }

   public void persist(Object entity)
   {
      persistenceContext.persist(entity);
   }

   public <T> T merge(T entity)
   {
      return persistenceContext.merge(entity);
   }

   public void remove(Object entity)
   {
      persistenceContext.remove(entity);
   }

   public <T> T find(Class<T> entityClass, Object primaryKey)
   {
      return persistenceContext.find(entityClass, primaryKey);
   }

   public void flush()
   {
      persistenceContext.flush();
   }

   public javax.persistence.Query createQuery(String ejbqlString)
   {
      return persistenceContext.createQuery(ejbqlString);
   }

   public javax.persistence.Query createNamedQuery(String name)
   {
      return persistenceContext.createNamedQuery(name);
   }

   public javax.persistence.Query createNativeQuery(String sqlString)
   {
      return persistenceContext.createNativeQuery(sqlString);
   }

   public javax.persistence.Query createNativeQuery(String sqlString, Class resultClass)
   {
      return persistenceContext.createNativeQuery(sqlString, resultClass);
   }

   public javax.persistence.Query createNativeQuery(String sqlString, String resultSetMapping)
   {
      return persistenceContext.createNativeQuery(sqlString, resultSetMapping);
   }

   public void refresh(Object entity)
   {
      persistenceContext.refresh(entity);
   }

   public boolean contains(Object entity)
   {
      return persistenceContext.contains(entity);
   }

   public void close()
   {
      throw new IllegalStateException("It is illegal to close an injected EntityManager");
   }

   public boolean isOpen()
   {
      return persistenceContext.isOpen();
   }

   public EntityTransaction getTransaction()
   {
      return persistenceContext.getTransaction();
   }

   public void setFlushMode(FlushModeType flushMode)
   {
      persistenceContext.setFlushMode(flushMode);
   }

   public void closeExtendedPersistenceContext()
   {
      persistenceContext.close();
   }


}

