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

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

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.hibernate.HibernateException;
import org.hibernate.engine.EntityEntry;
import org.hibernate.engine.EntityKey;
import org.hibernate.engine.ForeignKeys;
import org.hibernate.engine.SessionImplementor;
import org.hibernate.event.EventSource;
import org.hibernate.event.SaveOrUpdateEvent;
import org.hibernate.event.def.DefaultSaveOrUpdateEventListener;
import org.hibernate.persister.entity.EntityPersister;

/**
 * Created by IntelliJ IDEA.
 * User: zzy9v4
 * Date: 22-feb-2007
 * Time: 14.02.38
 * To change this template use File | Settings | File Templates.
 */
public class SaveOrUpdateEventListener extends DefaultSaveOrUpdateEventListener {
    private static final Log log = LogFactory.getLog(SaveOrUpdateEventListener.class);

    /**
     * Determine whether the entity is persistent, detached, or transient
     *
     * @param entity     The entity to check
     * @param entityName The name of the entity
     * @param entry      The entity's entry in the persistence context
     * @param source     The originating session.
     * @return The state.
     */
    protected int getEntityState(
            Object entity,
            String entityName,
            EntityEntry entry, //pass this as an argument only to avoid double looking
            SessionImplementor source) {

        if (entry != null) { // the object is persistent
            return super.getEntityState(entity, entityName, entry, source);
        } else { //The object is not in the session cache !!!
            return getInternalEntityState(entityName, entity, source);
        }
    }

    private int getInternalEntityState(String entityName, Object entity, SessionImplementor source) {
        //the object is transient or detached
        //the entity is not associated with the session, so
        //try interceptor and unsaved-value
    	if (entity instanceof Entity) {
    		Entity wrappedEntity = (Entity)entity;
    		if (wrappedEntity.isNewEntity()) {
    			return TRANSIENT;
    		} else {
    			return DETACHED;
    		}
    	}
    	
        if (ForeignKeys.isTransient(entityName, entity, getAssumedUnsavedForEntity(entity), source)) {
            if (log.isTraceEnabled()) {
                log.trace(
                        "transient instance of: " +
                                getLoggableName(entityName, entity)
                );
            }
            return TRANSIENT;
        } else {
            if (log.isTraceEnabled()) {
                log.trace(
                        "detached instance of: " +
                                getLoggableName(entityName, entity)
                );
            }
            return DETACHED;
        }
    }

    private Boolean getAssumedUnsavedForEntity(Object entity) {
        if (entity instanceof Entity) {
            if (((Entity) entity).isNewEntity()) {
                return Boolean.TRUE;
            } else {
                return Boolean.FALSE;
            }
        }
        return null;
    }

	public void onSaveOrUpdate(SaveOrUpdateEvent event) {
        Entity entity = (Entity)event.getObject();
        if (entity.isNewEntity()) {
            if (entity instanceof TrackAttributeAwareEntity) {
                ((TrackAttributeAwareEntity) entity).trackInsertAttribute();
            }
        }
        if (entity.isNewEntity() || entity.isChangedEntity()) {
            if (entity instanceof TrackAttributeAwareEntity) {
                ((TrackAttributeAwareEntity) entity).trackUpdateAttribute();
            }
        }

		super.onSaveOrUpdate(event);
	}

	/* (non-Javadoc)
	 * @see org.hibernate.event.def.DefaultSaveOrUpdateEventListener#performUpdate(org.hibernate.event.SaveOrUpdateEvent, java.lang.Object, org.hibernate.persister.entity.EntityPersister)
	 */
	@Override
	protected void performUpdate(SaveOrUpdateEvent event, Object entity,
			EntityPersister persister) throws HibernateException {
		
		final EventSource source = event.getSession();
		EntityKey key = new EntityKey( event.getRequestedId(), persister, source.getEntityMode() );
		if (source.getPersistenceContext().getEntity(key) != null) {
			source.getPersistenceContext().removeEntity(key);
		}

		super.performUpdate(event, entity, persister);
	}
}
