package online.jpa.extension;

import java.awt.List;
import java.util.Map;

import javax.persistence.EntityGraph;
import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.EntityTransaction;
import javax.persistence.FlushModeType;
import javax.persistence.LockModeType;
import javax.persistence.Query;
import javax.persistence.StoredProcedureQuery;
import javax.persistence.TypedQuery;
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaDelete;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.CriteriaUpdate;
import javax.persistence.metamodel.Metamodel;

import org.hibernate.transform.Transformers;

/**
 * ProxyEntityManager
 *
 * @author Tadashi Nakayama
 */
public final class ProxyEntityManager implements ExEntityManager {

    /** EntityManager */
    private final EntityManager manager;

    /**
     * Constructor
     * @param entityManager EntityManager
     */
    public ProxyEntityManager(final EntityManager entityManager) {
        this.manager = entityManager;
    }

    /**
     * @see online.jpa.extension.ExEntityManager
     * #createNativeQuery(java.lang.String, java.lang.String)
     */
    @Override
    public ExQuery createNativeQuery(final String arg0, final String arg1) {
        return new ProxyQuery(this.manager.createNativeQuery(arg0, arg1));
    }

    /**
     * @see online.jpa.extension.ExEntityManager#createNativeQuery(java.lang.String)
     */
    @Override
    public ExQuery createNativeQuery(final String qry) {
        return new ProxyQuery(this.manager.createNativeQuery(qry));
    }

    /**
     * @see online.jpa.extension.ExEntityManager
     * #createNativeQuery(java.lang.String, java.lang.Class)
     */
    @Override
    public ExQuery createNativeQuery(final String qry, final Class cls) {
        if (Map.class.equals(cls) || List.class.equals(cls)) {
            final var query = this.manager.createNativeQuery(qry);
            if (org.hibernate.query.Query.class.isInstance(query)) {
                final org.hibernate.query.Query<?> nq = org.hibernate.query.Query.class.cast(query);
                if (Map.class.equals(cls)) {
                    nq.setResultTransformer(Transformers.ALIAS_TO_ENTITY_MAP);
                } else if (List.class.equals(cls)) {
                    nq.setResultTransformer(Transformers.TO_LIST);
                }
            }
            return new ProxyQuery(query);
        }
        return new ProxyQuery(this.manager.createNativeQuery(qry, cls));
    }

    /**
     * @see javax.persistence.EntityManager#createQuery(java.lang.String)
     */
    @Override
    public Query createQuery(final String qry) {
        return this.manager.createQuery(qry);
    }

    /**
     * @see javax.persistence.EntityManager#createNamedQuery(java.lang.String)
     */
    @Override
    public Query createNamedQuery(final String val) {
        return this.manager.createNamedQuery(val);
    }

    /**
     * @see javax.persistence.EntityManager#clear()
     */
    @Override
    public void clear() {
        this.manager.clear();
    }

    /**
     * @see javax.persistence.EntityManager#close()
     */
    @Override
    public void close() {
        this.manager.close();
    }

    /**
     * @see javax.persistence.EntityManager#contains(java.lang.Object)
     */
    @Override
    public boolean contains(final Object obj) {
        return this.manager.contains(obj);
    }

    /**
     * @see javax.persistence.EntityManager#find(java.lang.Class, java.lang.Object)
     */
    @Override
    public <T> T find(final Class<T> arg0, final Object arg1) {
        return this.manager.find(arg0, arg1);
    }

    /**
     * @see javax.persistence.EntityManager#flush()
     */
    @Override
    public void flush() {
        this.manager.flush();
    }

    /**
     * @see javax.persistence.EntityManager#getDelegate()
     */
    @Override
    public Object getDelegate() {
        return this.manager.getDelegate();
    }

    /**
     * @see javax.persistence.EntityManager#getFlushMode()
     */
    @Override
    public FlushModeType getFlushMode() {
        return this.manager.getFlushMode();
    }

    /**
     * @see javax.persistence.EntityManager#getReference(java.lang.Class, java.lang.Object)
     */
    @Override
    public <T> T getReference(final Class<T> arg0, final Object arg1) {
        return this.manager.getReference(arg0, arg1);
    }

    /**
     * @see javax.persistence.EntityManager#getTransaction()
     */
    @Override
    public EntityTransaction getTransaction() {
        return this.manager.getTransaction();
    }

    /**
     * @see javax.persistence.EntityManager#isOpen()
     */
    @Override
    public boolean isOpen() {
        return this.manager.isOpen();
    }

    /**
     * @see javax.persistence.EntityManager#joinTransaction()
     */
    @Override
    public void joinTransaction() {
        this.manager.joinTransaction();
    }

    /**
     * @see javax.persistence.EntityManager#lock(java.lang.Object, javax.persistence.LockModeType)
     */
    @Override
    public void lock(final Object arg0, final LockModeType arg1) {
        this.manager.lock(arg0, arg1);
    }

    /**
     * @see javax.persistence.EntityManager#merge(java.lang.Object)
     */
    @Override
    public <T> T merge(final T arg0) {
        return this.manager.merge(arg0);
    }

    /**
     * @see javax.persistence.EntityManager#persist(java.lang.Object)
     */
    @Override
    public void persist(final Object arg0) {
        this.manager.persist(arg0);
    }

    /**
     * @see javax.persistence.EntityManager#refresh(java.lang.Object)
     */
    @Override
    public void refresh(final Object arg0) {
        this.manager.refresh(arg0);
    }

    /**
     * @see javax.persistence.EntityManager#remove(java.lang.Object)
     */
    @Override
    public void remove(final Object arg0) {
        this.manager.remove(arg0);
    }

    /**
     * @see javax.persistence.EntityManager#setFlushMode(javax.persistence.FlushModeType)
     */
    @Override
    public void setFlushMode(final FlushModeType arg0) {
        this.manager.setFlushMode(arg0);
    }

    /**
     * @see javax.persistence.EntityManager#createEntityGraph(java.lang.Class)
     */
    @Override
    public <T> EntityGraph<T> createEntityGraph(final Class<T> arg0) {
        return this.manager.createEntityGraph(arg0);
    }

    /**
     * @see javax.persistence.EntityManager#createEntityGraph(java.lang.String)
     */
    @Override
    public EntityGraph<?> createEntityGraph(final String arg0) {
        return this.manager.createEntityGraph(arg0);
    }

    /**
     * @see javax.persistence.EntityManager#createNamedQuery(java.lang.String, java.lang.Class)
     */
    @Override
    public <T> TypedQuery<T> createNamedQuery(final String arg0, final Class<T> arg1) {
        return this.manager.createNamedQuery(arg0, arg1);
    }

    /**
     * @see javax.persistence.EntityManager#createNamedStoredProcedureQuery(java.lang.String)
     */
    @Override
    public StoredProcedureQuery createNamedStoredProcedureQuery(final String arg0) {
        return this.manager.createNamedStoredProcedureQuery(arg0);
    }

    /**
     * @see javax.persistence.EntityManager#createQuery(javax.persistence.criteria.CriteriaQuery)
     */
    @Override
    public <T> TypedQuery<T> createQuery(final CriteriaQuery<T> arg0) {
        return this.manager.createQuery(arg0);
    }

    /**
     * @see javax.persistence.EntityManager#createQuery(javax.persistence.criteria.CriteriaUpdate)
     */
    @Override
    public Query createQuery(final CriteriaUpdate arg0) {
        return this.manager.createQuery(arg0);
    }

    /**
     * @see javax.persistence.EntityManager#createQuery(javax.persistence.criteria.CriteriaDelete)
     */
    @Override
    public Query createQuery(final CriteriaDelete arg0) {
        return this.manager.createQuery(arg0);
    }

    /**
     * @see javax.persistence.EntityManager#createQuery(java.lang.String, java.lang.Class)
     */
    @Override
    public <T> TypedQuery<T> createQuery(final String arg0, final Class<T> arg1) {
        return this.manager.createQuery(arg0, arg1);
    }

    /**
     * @see javax.persistence.EntityManager#createStoredProcedureQuery(java.lang.String)
     */
    @Override
    public StoredProcedureQuery createStoredProcedureQuery(final String arg0) {
        return this.manager.createStoredProcedureQuery(arg0);
    }

    /**
     * @see javax.persistence.EntityManager
     * #createStoredProcedureQuery(java.lang.String, java.lang.Class[])
     */
    @Override
    public StoredProcedureQuery createStoredProcedureQuery(final String arg0, final Class... arg1) {
        return this.manager.createStoredProcedureQuery(arg0, arg1);
    }

    /**
     * @see javax.persistence.EntityManager
     * #createStoredProcedureQuery(java.lang.String, java.lang.String[])
     */
    @Override
    public StoredProcedureQuery createStoredProcedureQuery(
            final String arg0, final String... arg1) {
        return this.manager.createStoredProcedureQuery(arg0, arg1);
    }

    /**
     * @see javax.persistence.EntityManager#detach(java.lang.Object)
     */
    @Override
    public void detach(final Object arg0) {
        this.manager.detach(arg0);
    }

    /**
     * @see javax.persistence.EntityManager#find(java.lang.Class, java.lang.Object, java.util.Map)
     */
    @Override
    public <T> T find(final Class<T> arg0, final Object arg1, final Map<String, Object> arg2) {
        return this.manager.find(arg0, arg1, arg2);
    }

    /**
     * @see javax.persistence.EntityManager
     * #find(java.lang.Class, java.lang.Object, javax.persistence.LockModeType)
     */
    @Override
    public <T> T find(final Class<T> arg0, final Object arg1, final LockModeType arg2) {
        return this.manager.find(arg0, arg1, arg2);
    }

    /**
     * @see javax.persistence.EntityManager
     * #find(java.lang.Class, java.lang.Object, javax.persistence.LockModeType, java.util.Map)
     */
    @Override
    public <T> T find(final Class<T> arg0, final Object arg1,
            final LockModeType arg2, final Map<String, Object> arg3) {
        return this.manager.find(arg0, arg1, arg2, arg3);
    }

    /**
     * @see javax.persistence.EntityManager#getCriteriaBuilder()
     */
    @Override
    public CriteriaBuilder getCriteriaBuilder() {
        return this.manager.getCriteriaBuilder();
    }

    /**
     * @see javax.persistence.EntityManager#getEntityGraph(java.lang.String)
     */
    @Override
    public EntityGraph<?> getEntityGraph(final String arg0) {
        return this.manager.getEntityGraph(arg0);
    }

    /**
     * @see javax.persistence.EntityManager#getEntityGraphs(java.lang.Class)
     */
    @Override
    public <T> java.util.List<EntityGraph<? super T>> getEntityGraphs(final Class<T> arg0) {
        return this.manager.getEntityGraphs(arg0);
    }

    /**
     * @see javax.persistence.EntityManager#getEntityManagerFactory()
     */
    @Override
    public EntityManagerFactory getEntityManagerFactory() {
        return this.manager.getEntityManagerFactory();
    }

    /**
     * @see javax.persistence.EntityManager#getLockMode(java.lang.Object)
     */
    @Override
    public LockModeType getLockMode(final Object arg0) {
        return this.manager.getLockMode(arg0);
    }

    /**
     * @see javax.persistence.EntityManager#getMetamodel()
     */
    @Override
    public Metamodel getMetamodel() {
        return this.manager.getMetamodel();
    }

    /**
     * @see javax.persistence.EntityManager#getProperties()
     */
    @Override
    public Map<String, Object> getProperties() {
        return this.manager.getProperties();
    }

    /**
     * @see javax.persistence.EntityManager#isJoinedToTransaction()
     */
    @Override
    public boolean isJoinedToTransaction() {
        return this.manager.isJoinedToTransaction();
    }

    /**
     * @see javax.persistence.EntityManager
     * #lock(java.lang.Object, javax.persistence.LockModeType, java.util.Map)
     */
    @Override
    public void lock(final Object arg0, final LockModeType arg1, final Map<String, Object> arg2) {
        this.manager.lock(arg0, arg1, arg2);
    }

    /**
     * @see javax.persistence.EntityManager#refresh(java.lang.Object, java.util.Map)
     */
    @Override
    public void refresh(final Object arg0, final Map<String, Object> arg1) {
        this.manager.refresh(arg0, arg1);
    }

    /**
     * @see javax.persistence.EntityManager
     * #refresh(java.lang.Object, javax.persistence.LockModeType)
     */
    @Override
    public void refresh(final Object arg0, final LockModeType arg1) {
        this.manager.refresh(arg0, arg1);
    }

    /**
     * @see javax.persistence.EntityManager
     * #refresh(java.lang.Object, javax.persistence.LockModeType, java.util.Map)
     */
    @Override
    public void refresh(final Object arg0,
            final LockModeType arg1, final Map<String, Object> arg2) {
        this.manager.refresh(arg0, arg1, arg2);
    }

    /**
     * @see javax.persistence.EntityManager#setProperty(java.lang.String, java.lang.Object)
     */
    @Override
    public void setProperty(final String arg0, final Object arg1) {
        this.manager.setProperty(arg0, arg1);
    }

    /**
     * @see javax.persistence.EntityManager#unwrap(java.lang.Class)
     */
    @Override
    public <T> T unwrap(final Class<T> arg0) {
        return this.manager.unwrap(arg0);
    }
}
