/*
 * Hibernate, Relational Persistence for Idiomatic Java
 *
 * Copyright (c) 2008, Red Hat Middleware LLC or third-party contributors as
 * indicated by the @author tags or express copyright attribution
 * statements applied by the authors.  All third-party contributions are
 * distributed under license by Red Hat Middleware LLC.
 *
 * This copyrighted material is made available to anyone wishing to use, modify,
 * copy, or redistribute it subject to the terms and conditions of the GNU
 * Lesser General Public License, as published by the Free Software Foundation.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License
 * for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this distribution; if not, write to:
 * Free Software Foundation, Inc.
 * 51 Franklin Street, Fifth Floor
 * Boston, MA  02110-1301  USA
 *
 */
package org.hibernate.action;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;

import org.hibernate.HibernateException;
import org.hibernate.engine.SessionFactoryImplementor;
import org.hibernate.engine.SessionImplementor;
import org.hibernate.persister.entity.EntityPersister;
import org.hibernate.persister.entity.Queryable;

/**
 * Implementation of BulkOperationCleanupAction.
 *
 * @author Steve Ebersole
 */
public class BulkOperationCleanupAction implements Executable, Serializable {

	private final SessionImplementor session;

	private final Set affectedEntityNames = new HashSet();
	private final Set affectedCollectionRoles = new HashSet();
	private final Serializable[] spaces;

	public BulkOperationCleanupAction(SessionImplementor session, Queryable[] affectedQueryables) {
		this.session = session;
		// TODO : probably better to calculate these and pass them in, as it'll be more performant
		ArrayList tmpSpaces = new ArrayList();
		for ( int i = 0; i < affectedQueryables.length; i++ ) {
			if ( affectedQueryables[i].hasCache() ) {
				affectedEntityNames.add( affectedQueryables[i].getEntityName() );
			}
			Set roles = session.getFactory().getCollectionRolesByEntityParticipant( affectedQueryables[i].getEntityName() );
			if ( roles != null ) {
				affectedCollectionRoles.addAll( roles );
			}
			for ( int y = 0; y < affectedQueryables[i].getQuerySpaces().length; y++ ) {
				tmpSpaces.add( affectedQueryables[i].getQuerySpaces()[y] );
			}
		}
		this.spaces = new Serializable[ tmpSpaces.size() ];
		for ( int i = 0; i < tmpSpaces.size(); i++ ) {
			this.spaces[i] = ( Serializable ) tmpSpaces.get( i );
		}
	}
	
	/** Create an action that will evict collection and entity regions based on queryspaces (table names).
	 *  TODO: cache the autodetected information and pass it in instead.
	 **/
	public BulkOperationCleanupAction(SessionImplementor session, Set querySpaces) {
		this.session = session;

		Set tmpSpaces = new HashSet(querySpaces);
		SessionFactoryImplementor factory = session.getFactory();
		Iterator iterator = factory.getAllClassMetadata().entrySet().iterator();
		while ( iterator.hasNext() ) {
			Map.Entry entry = (Map.Entry) iterator.next();
			String entityName = (String) entry.getKey();
			EntityPersister persister = factory.getEntityPersister( entityName );
			Serializable[] entitySpaces = persister.getQuerySpaces();

			if (affectedEntity( querySpaces, entitySpaces )) {
				if ( persister.hasCache() ) {
					affectedEntityNames.add( persister.getEntityName() );
				}
				Set roles = session.getFactory().getCollectionRolesByEntityParticipant( persister.getEntityName() );
				if ( roles != null ) {
					affectedCollectionRoles.addAll( roles );
				}
				for ( int y = 0; y < entitySpaces.length; y++ ) {
					tmpSpaces.add( entitySpaces[y] );
				}
			}

		}
		this.spaces = (Serializable[]) tmpSpaces.toArray( new Serializable[tmpSpaces.size()] );		
	}


	/** returns true if no queryspaces or if there are a match */
	private boolean affectedEntity(Set querySpaces, Serializable[] entitySpaces) {
		if(querySpaces==null || querySpaces.isEmpty()) {
			return true;
		}
		
		for ( int i = 0; i < entitySpaces.length; i++ ) {
			if ( querySpaces.contains( entitySpaces[i] ) ) {
				return true;
			}
		}
		return false;
	}

	public void init() {
		evictEntityRegions();
		evictCollectionRegions();
	}

	public boolean hasAfterTransactionCompletion() {
		return true;
	}

	public void afterTransactionCompletion(boolean success) throws HibernateException {
		evictEntityRegions();
		evictCollectionRegions();
	}

	public Serializable[] getPropertySpaces() {
		return spaces;
	}

	public void beforeExecutions() throws HibernateException {
		// nothing to do
	}

	public void execute() throws HibernateException {
		// nothing to do		
	}

	private void evictEntityRegions() {
		if ( affectedEntityNames != null ) {
			Iterator itr = affectedEntityNames.iterator();
			while ( itr.hasNext() ) {
				final String entityName = ( String ) itr.next();
				session.getFactory().evictEntity( entityName );
			}
		}
	}

	private void evictCollectionRegions() {
		if ( affectedCollectionRoles != null ) {
			Iterator itr = affectedCollectionRoles.iterator();
			while ( itr.hasNext() ) {
				final String roleName = ( String ) itr.next();
				session.getFactory().evictCollection( roleName );
			}
		}
	}
}
