package net.sourceforge.javautil.database.jpa;
import java.util.LinkedHashMap;
import java.util.Map;
import javax.persistence.EntityManagerFactory;
import javax.persistence.spi.PersistenceProvider;
import javax.persistence.spi.PersistenceUnitInfo;
import javax.persistence.spi.PersistenceUnitTransactionType;
import net.sourceforge.javautil.common.ReflectionUtil;
import net.sourceforge.javautil.common.io.IInputSource;
import net.sourceforge.javautil.common.io.IVirtualDirectory;
import net.sourceforge.javautil.common.io.IVirtualFile;
import net.sourceforge.javautil.common.xml.XMLDocument;
import net.sourceforge.javautil.database.jpa.descriptor.IPersistenceUnit;
import net.sourceforge.javautil.database.jpa.descriptor.IPersistenceXML;
import net.sourceforge.javautil.database.jpa.descriptor.impl.PersistenceUnit;
import net.sourceforge.javautil.database.jpa.descriptor.impl.PersistenceXML;
import net.sourceforge.javautil.datasource.IDataSourceFactory;
/**
 * Utilities related to JPA (Java Persistence Architecture).
 *
 * @author elponderador
 * @author $Author$
 * @version $Id$
 */
public class JPAUtil {
  
  /**
   * @param persistenceXMLInput The file for reading the persistence xml data
   * 
   * @see #load(PersistenceXML)
   */
  public static PersistenceSetup load (IVirtualFile persistenceXMLInput, IDataSourceFactory dsFactory) {
    return load(XMLDocument.read(persistenceXMLInput, PersistenceXML.class), persistenceXMLInput.getOwner(), dsFactory);
  }
  
  /**
   * @param xml The loaded XML persistence configuration
   * @return The setup related to the persistence xml, or null if no persistence units were defined
   */
  public static PersistenceSetup load (IPersistenceXML xml, IVirtualDirectory root, IDataSourceFactory dsFactory) {
    if (xml.getUnits().size() == 0) return null;
    
    Map<String, EntityManagerFactory> factories = new LinkedHashMap<String, EntityManagerFactory>();
    
    for (IPersistenceUnit unit : xml.getUnits()) {
      String dataSource = unit.getNonJTADataSource();
      if (dataSource != null) {
        factories.put(unit.getName(), createFactory(
          SimplePersistenceUnitInfo.getFrom(
            unit, root.getURL(), PersistenceUnitTransactionType.RESOURCE_LOCAL, 
            dsFactory.getNonJTADataSource(unit.getNonJTADataSource()))
          )
        );
      } else {
        factories.put(unit.getName(), createFactory(
          SimplePersistenceUnitInfo.getFrom(
            unit, root.getURL(), PersistenceUnitTransactionType.JTA, 
            dsFactory.getNonJTADataSource(unit.getJTADataSource()))
          )
        );
      }
    }
    
    return new PersistenceSetup(factories);
  }
  
  /**
   * @param info The info to use for creating the EMF
   * @return A new EMF based on the unit setup
   */
  public static EntityManagerFactory createFactory (PersistenceUnitInfo info) {
    Class<? extends PersistenceProvider> ppc = ReflectionUtil.getClass( info.getPersistenceProviderClassName() );
    PersistenceProvider provider = ReflectionUtil.newInstance(ppc, new Class[0]);
    return provider.createContainerEntityManagerFactory(info, new LinkedHashMap( info.getProperties() ));
  }
  
  /**
   * This provides extended functionality with automatically started/committed transactions.
   * 
   * @param factory The factory to use in order to create the extended manager
   * @return An extended manager implementation
   */
  public static IEntityManagerExtended createExtendedManager (EntityManagerFactory factory) {
    return EntityManagerExtendedHandler.createEMProxy(new EntityManagerExtendedImpl(factory.createEntityManager()), IEntityManagerExtended.class);
  }
  
}