package it.mice.voila.runtime.hibernate.interceptor;

import it.mice.voila.runtime.entity.Entity;
import it.mice.voila.runtime.entity.EntityGeneric;

import java.io.Serializable;
import java.util.Iterator;

import org.hibernate.EmptyInterceptor;
import org.hibernate.SessionFactory;
import org.hibernate.cache.CacheKey;
import org.hibernate.impl.SessionImpl;
import org.hibernate.persister.entity.AbstractEntityPersister;
import org.hibernate.persister.entity.EntityPersister;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;


/**
 * Created by IntelliJ IDEA.
 * User: zzy9v4
 * Date: 23-feb-2007
 * Time: 10.39.27
 * To change this template use File | Settings | File Templates.
 */
public class CustomInterceptor extends EmptyInterceptor implements BeanFactoryAware {
    /**
	 * 
	 */
	private static final long serialVersionUID = -8731771375437963623L;
	private BeanFactory beanFactory;
    private SessionFactory sessionFactory;

    public SessionFactory getSessionFactory() {
        if (sessionFactory == null) {
            sessionFactory = beanFactory.getBean(SessionFactory.class);
        }
        return sessionFactory;
    }

    /**
     * Called to distinguish between transient and detached entities. The return value determines the
     * state of the entity with respect to the current session.
     * <ul>
     * <li><tt>Boolean.TRUE</tt> - the entity is transient
     * <li><tt>Boolean.FALSE</tt> - the entity is detached
     * <li><tt>null</tt> - Hibernate uses the <tt>unsaved-value</tt> mapping and other heuristics to
     * determine if the object is unsaved
     * </ul>
     *
     * @param entity a transient or detached entity
     * @return Boolean or <tt>null</tt> to choose default behaviour
     */
    public Boolean isTransient(Object entity) {
        if (entity instanceof Entity) {
            Entity jfEntity = (Entity) entity;
            if (jfEntity.isNewEntity()) {
            	if (getSessionFactory() != null) {
                	SessionImpl simpl = (SessionImpl) getSessionFactory().getCurrentSession();
                	AbstractEntityPersister ep = (AbstractEntityPersister)simpl.getEntityPersister(entity.getClass().getName(), entity);
//                	if (ep.isVersioned()) {
//                		final Object version = ep.getVersion( entity, simpl.getEntityMode() );
//                    	if (version != null) {
//                            return Boolean.FALSE;
//                    	} else {
//                    		Object id = simpl.getEntityPersister( entity.getClass().getName(), entity ).getIdentifier( entity, simpl.getEntityMode() );
//                            return id == null;
//                    	}
//                	}
                	return isTransient(entity, ep, simpl);
            	}
                return null;
            } else {
                return Boolean.FALSE;
            }
        }
        return super.isTransient(entity);
    }

    public Boolean isTransient(Object entity, AbstractEntityPersister entityPersister, SessionImpl session) {
		final Serializable id;
		if ( entityPersister.canExtractIdOutOfEntity() ) {
			id = entityPersister.getIdentifier( entity, session.getEntityMode() );
		}
		else {
			id = null;
		}
		// we *always* assume an instance with a null
		// identifier or no identifier property is unsaved!
		if ( id == null ) {
			return Boolean.TRUE;
		}

		if (id instanceof EntityGeneric) {
			EntityGeneric egid = (EntityGeneric)id;
			if (egid.isPkNotNull()) {
				return Boolean.FALSE;
			}
		}
		// check the version unsaved-value, if appropriate
//		final Object version = entityPersister.getVersion( entity, session.getEntityMode() );
//		if ( entityPersister.isVersioned() ) {
//			// let this take precedence if defined, since it works for
//			// assigned identifiers
//			Boolean result = entityPersister.getEntityMetamodel().getVersionProperty()
//					.getUnsavedValue().isUnsaved( version );
//			if ( result != null ) {
//				return result;
//			}
//		}

		// check the id unsaved-value
		Boolean result = entityPersister.getEntityMetamodel().getIdentifierProperty()
				.getUnsavedValue().isUnsaved( id );
		if ( result != null ) {
			return result;
		}

		// check to see if it is in the second-level cache
		if ( entityPersister.hasCache() ) {
			CacheKey ck = new CacheKey(
					id,
					entityPersister.getIdentifierType(),
					entityPersister.getRootEntityName(),
					session.getEntityMode(),
					session.getFactory()
				);
			if ( entityPersister.getCache().get( ck, session.getTimestamp() ) != null ) {
				return Boolean.FALSE;
			}
		}

		return null;
    }
    
    public void postFlush(Iterator iterator) {
    	while (iterator.hasNext()) {
			Object element = (Object) iterator.next();
			if (element instanceof Entity) {
	            Entity jfEntity = (Entity) element;
	            if (jfEntity.isChangedEntity() || jfEntity.isNewEntity()) {
		            jfEntity.setInternalEntityStateToLoaded();
	            }
			}
		}
		super.postFlush(iterator);
	}

	public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
        this.beanFactory = beanFactory;
    }
}
