/*
* Copyright (c) 2011, 2012, Oracle and/or its affiliates. All rights reserved.
* ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
*/
package com.sun.ts.tests.jpa.common.pluggability.altprovider.implementation;

import javax.persistence.EntityManagerFactory;
import javax.persistence.spi.*;
import java.net.URL;
import java.util.*;

public class PersistenceProvider implements
        javax.persistence.spi.PersistenceProvider, javax.persistence.spi.ProviderUtil {

    //Used to store static PersistenceUnitInfo where this is the provider
    static Map<String, PersistenceUnitInfoImpl> puInfoMap;

    static private PersistenceProvider provider = new PersistenceProvider();

    protected TSLogger logger;

    public PersistenceProvider() {
        //logger = TSLogger.getInstance();
    }

    private void callLogger(String s) {
        if (logger == null) {
            logger = TSLogger.getInstance();
        }
        logger.log(s);
    }

    public void generateSchema(PersistenceUnitInfo info, Map map) {
        System.out.println("Called ALTERNATE_PROVIDER: PersistenceProvider.generateSchema(PersistenceUnitInfo, Map)");
        callLogger("Called generateSchema(PersistenceUnitInfo, Map)");
    }

    public EntityManagerFactory createContainerEntityManagerFactory(
            PersistenceUnitInfo puInfo, Map properties) {
        //
        // Since this provider is packaged in the EAR and we are not overriding
        // the provider (specified in the persistence.xml file) via
        // javax.persistence.provider, therefore when this
        // method is called it is ok to return an instance of EMF. The only
        // case we won't is if the unitName passed in doesn't contain "CTS".
        //
        System.out.println("Called ALTERNATE_PROVIDER: PersistenceProvider.createContainerEntityManagerFactory(PersistenceUnitInfo, Map)");

        PersistenceProviderResolver pr = PersistenceProviderResolverHolder.getPersistenceProviderResolver();
        List list = pr.getPersistenceProviders();
        System.out.println("LIST OF PROVIDERS:");
        System.out.println("------------------");
        for (Object o : list) {
            System.out.println("PROVIDER:" + o.getClass().getName());
        }
        System.out.println("------------------");


        String unitName = puInfo.getPersistenceUnitName();
        System.out.println("ALTERNATE_PROVIDER: createContainerEntityManagerFactory - unitName:" + unitName);

        if (unitName != null) {
            if (!unitName.contains("CTS")) {
                return null;
            }
        }
        displayMap(properties);

        callLogger("Called createContainerEntityManagerFactory(PersistenceUnitInfo, Map)");
        EntityManagerFactoryImpl emf = new EntityManagerFactoryImpl(true);
        emf.properties = properties;
        ClassTransformerImpl transformer = new ClassTransformerImpl();
        emf.transformer = transformer;
        puInfo.addTransformer(transformer);
        emf.puInfo = puInfo;
        emf.newTempClassloader = puInfo.getNewTempClassLoader();
        System.out.println("returning ALTERNATE_PROVIDER factory:" + emf.toString());
        return emf;
    }

    public EntityManagerFactory createEntityManagerFactory(String puName, Map properties) {
        System.out.println("Called ALTERNATE_PROVIDER: PersistenceProvider.createEntityManagerFactory(String, Map)");

        if (!puName.equals("ALTPROVIDERPU")) {
            System.out.println("returning null from ALTERNATE_PROVIDER: PersistenceProvider.createEntityManagerFactory(String, Map) PU Names don't match");

            return null;
        }
        if (properties != null) {
            if (properties.size() > 0) {
                String pp = (String) properties.get("javax.persistence.provider");
                if (pp == null) {
                    System.out.println("returning null from ALTERNATE_PROVIDER: PersistenceProvider.createEntityManagerFactory(String, Map) no provider specified");
                    return null;
                }
                if (!pp.equals("com.sun.ts.tests.jpa.common.pluggability.altprovider.implementation.PersistenceProvider")) {
                    System.out.println("returning null from ALTERNATE_PROVIDER: PersistenceProvider.createEntityManagerFactory(String, Map) different provider specified");
                    return null;

                }
            }
        }

        PersistenceProviderResolver pr = PersistenceProviderResolverHolder.getPersistenceProviderResolver();
        List list = pr.getPersistenceProviders();
        System.out.println("LIST OF PROVIDERS:");
        System.out.println("------------------");
        for (Object o : list) {
            System.out.println("PROVIDER:" + o.getClass().getName());
        }
        System.out.println("------------------");

        callLogger("Called createEntityManagerFactory(String, Map");
        PersistenceUnitInfoImpl puInfo = getPersistenceUnitInfoMap().get(puName);
        EntityManagerFactoryImpl emf = null;
        if (puInfo != null) {
            puInfo = (PersistenceUnitInfoImpl) puInfo.clone();
            puInfo.classLoader = Thread.currentThread().getContextClassLoader();
            emf = new EntityManagerFactoryImpl();
            emf.properties = properties;
            emf.puInfo = puInfo;
        }
        System.out.println("returning ALTERNATE_PROVIDER factory:" + emf.toString());
        return emf;
    }


    public ProviderUtil getProviderUtil() {
        callLogger("Called getProviderUtil()");
        return provider;
    }

    public LoadState isLoaded(Object entity) {
        callLogger("Called isLoaded()");
        return LoadState.UNKNOWN;
    }

    public LoadState isLoadedWithReference(Object entity, String attributeName) {
        callLogger("Called isLoadedWithReference()");
        return LoadState.UNKNOWN;
    }

    public LoadState isLoadedWithoutReference(Object entity, String attributeName) {
        callLogger("Called isLoadedWithoutReference()");
        return LoadState.UNKNOWN;
    }

    /**
     * Builds static PersistenceUnitInfo instead searching for and parsing the persistence.xml to build them
     * It might be better to make this generic and pull values from a property file so that it an be changed through
     * configuration.  For now, it builds 3 PersistenceUnitInfo's based on what was provided in the persistence.xml and
     * persistence_ee.xml files.
     *
     * @return a Map of PersistenceUnitInfo objects keyed on the name of the persistence unit
     */
    private static Map<String, PersistenceUnitInfoImpl> getPersistenceUnitInfoMap() {
        if (puInfoMap == null) {
            Map<String, PersistenceUnitInfoImpl> m = new Hashtable<String, PersistenceUnitInfoImpl>();

            //PU 1: ALTPROVIDERPU, resource_local,
            PersistenceUnitInfoImpl puinfo = new PersistenceUnitInfoImpl();
            puinfo.puName = "ALTPROVIDERPU";
            puinfo.persistenceProviderClassName = PersistenceProvider.class.getName();
            puinfo.managedClassNames = new ArrayList<String>();
            puinfo.managedClassNames.add("com.foo");
            puinfo.transactionType = PersistenceUnitTransactionType.RESOURCE_LOCAL;
            puinfo.excludeUnlistedClasses = false;
            URL url = null;
            try {
                url = new URL("file:///pluggability_contracts_resource_local.jar");
            } catch (Exception ex) {
            }
            List<URL> lURL = new ArrayList<URL>();
            lURL.add(url);
            puinfo.jarFileUrls = lURL;

            List<String> lMappingFileNames = new ArrayList<String>();
            lMappingFileNames.add("META-INF/myMappingFile1.xml");
            lMappingFileNames.add("META-INF/myMappingFile2.xml");
            puinfo.mappingFileNames = lMappingFileNames;

            try {
                url = new URL("pluggability_contracts_resource_local.jar");
            } catch (Exception ex) {
            }

            puinfo.persistenceUnitRootUrl = url;

            puinfo.persistenceXMLSchemaVersion = "2.1";


            //properties:
            Properties properties = new Properties();
            properties.setProperty("javax.persistence.provider", "foobar");

            puinfo.properties = properties;
            m.put(puinfo.puName, puinfo);


            puInfoMap = m;
        }
        return puInfoMap;

    }

    public boolean generateSchema(String persistenceUnitName, Map map) {
        return false;
    }

    private void displayMap(Map map) {

        if (map != null) {
            Set<Map.Entry<String, Object>> set = map.entrySet();

            for (Map.Entry<String, Object> me : set) {
                if (me.getValue() instanceof String) {
                    System.out.println("PersistenceProvider.displayMap() - name:" + me.getKey() + ", value:" + me.getValue());
                } else {
                    System.out.println("PersistenceProvider.displayMap() - name:" + me.getKey() + ", value:" + me.getValue().getClass().getName());
                }
            }
        } else {
            System.out.println("PersistenceProvider.displayMap() - Map passed in to displayMap was null");
        }
    }
}
