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

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

import java.io.Serializable;
import java.lang.reflect.Method;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.ListIterator;

import org.apache.commons.lang.StringUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.hibernate.HibernateException;
import org.hibernate.LazyInitializationException;
import org.hibernate.collection.internal.PersistentBag;
import org.hibernate.collection.internal.PersistentList;
import org.hibernate.engine.spi.SessionImplementor;
import org.hibernate.engine.spi.SharedSessionContractImplementor;
import org.hibernate.persister.collection.CollectionPersister;
import org.springframework.beans.BeanUtils;


/**
 * Created by IntelliJ IDEA.
 * User: zzy9v4
 * Date: 20-feb-2007
 * Time: 13.48.54
 * To change this template use File | Settings | File Templates.
 */
public class CustomPersistentBag extends PersistentBag {
    /**
     * Default logger
     */
    private static Log logger = LogFactory.getLog(CustomPersistentBag.class);

    public CustomPersistentBag(SharedSessionContractImplementor session, CollectionPersister persister) {
        super(session);
    }

    public CustomPersistentBag(SessionImplementor session, Collection coll) {
        super(session, coll);
    }

    public CustomPersistentBag() {
    }

    public CustomPersistentBag(SharedSessionContractImplementor session, PersistentList collection) {
    	 super(session, collection);
	}

	public int size() {
        try {
            return super.size();
        } catch (LazyInitializationException e) {
            resolveLazyInitializationException(e);
            return super.size();
        }
    }

    public boolean isEmpty() {
        try {
            return super.isEmpty();
        } catch (LazyInitializationException e) {
            resolveLazyInitializationException(e);
            return super.isEmpty();
        }
    }

    public Iterator iterator() {
        try {
            return super.iterator();
        } catch (LazyInitializationException e) {
            resolveLazyInitializationException(e);
            return super.iterator();
        }
    }

    public Object[] toArray() {
        try {
            return super.toArray();
        } catch (LazyInitializationException e) {
            resolveLazyInitializationException(e);
            return super.toArray();
        }
    }

    public Object[] toArray(Object[] a) {
        try {
            return super.toArray(a);
        } catch (LazyInitializationException e) {
            resolveLazyInitializationException(e);
            return super.toArray(a);
        }
    }

    public boolean contains(Object object) {
        try {
            return super.contains(object);
        } catch (LazyInitializationException e) {
            resolveLazyInitializationException(e);
            return super.contains(object);
        }
    }

    public boolean add(Object object) {
        try {
            return super.add(object);
        } catch (LazyInitializationException e) {
            resolveLazyInitializationException(e);
            return super.add(object);
        }
    }

    public boolean remove(Object o) {
        try {
            return super.remove(o);
        } catch (LazyInitializationException e) {
            resolveLazyInitializationException(e);
            return super.remove(o);
        }
    }

    public boolean containsAll(Collection c) {
        try {
            return super.containsAll(c);
        } catch (LazyInitializationException e) {
            resolveLazyInitializationException(e);
            return super.containsAll(c);
        }
    }

    public boolean addAll(Collection values) {
        try {
            return super.addAll(values);
        } catch (LazyInitializationException e) {
            resolveLazyInitializationException(e);
            return super.addAll(values);
        }
    }

    public boolean removeAll(Collection c) {
        try {
            return super.removeAll(c);
        } catch (LazyInitializationException e) {
            resolveLazyInitializationException(e);
            return super.removeAll(c);
        }
    }

    public boolean retainAll(Collection c) {
        try {
            return super.retainAll(c);
        } catch (LazyInitializationException e) {
            resolveLazyInitializationException(e);
            return super.retainAll(c);
        }
    }

    public void clear() {
        try {
            super.clear();
        } catch (LazyInitializationException e) {
            resolveLazyInitializationException(e);
            super.clear();
        }
    }

    public void add(int i, Object o) {
        try {
            super.add(i, o);
        } catch (LazyInitializationException e) {
            resolveLazyInitializationException(e);
            super.add(i, o);
        }
    }

    public boolean addAll(int i, Collection c) {
        try {
            return super.addAll(i, c);
        } catch (LazyInitializationException e) {
            resolveLazyInitializationException(e);
            return super.addAll(i, c);
        }
    }

    public Object get(int i) {
        try {
            return super.get(i);
        } catch (LazyInitializationException e) {
            resolveLazyInitializationException(e);
            return super.get(i);
        }
    }

    public int indexOf(Object o) {
        try {
            return super.indexOf(o);
        } catch (LazyInitializationException e) {
            resolveLazyInitializationException(e);
            return super.indexOf(o);
        }
    }

    public int lastIndexOf(Object o) {
        try {
            return super.lastIndexOf(o);
        } catch (LazyInitializationException e) {
            resolveLazyInitializationException(e);
            return super.lastIndexOf(o);
        }
    }

    public ListIterator listIterator() {
        try {
            return super.listIterator();
        } catch (LazyInitializationException e) {
            resolveLazyInitializationException(e);
            return super.listIterator();
        }
    }

    public ListIterator listIterator(int i) {
        try {
            return super.listIterator(i);
        } catch (LazyInitializationException e) {
            resolveLazyInitializationException(e);
            return super.listIterator(i);
        }
    }

    public Object remove(int i) {
        try {
            return super.remove(i);
        } catch (LazyInitializationException e) {
            resolveLazyInitializationException(e);
            return super.remove(i);
        }
    }

    public Object set(int i, Object o) {
        try {
            return super.set(i, o);
        } catch (LazyInitializationException e) {
            resolveLazyInitializationException(e);
            return super.set(i, o);
        }
    }

    public List subList(int start, int end) {
        try {
            return super.subList(start, end);
        } catch (LazyInitializationException e) {
            resolveLazyInitializationException(e);
            return super.subList(start, end);
        }
    }

    public String toString() {
        try {
            return super.toString();
        } catch (LazyInitializationException e) {
            resolveLazyInitializationException(e);
            return super.toString();
        }
    }

    private void resolveLazyInitializationException(LazyInitializationException e) {
        if (logger.isDebugEnabled()) {
            logger.debug("LazyInitializationException got during collection access...resolve by using lazy approach on role:" + getRole());
        }
        String getterMethod = getRole().substring(getRole().lastIndexOf(".") + 1) + "WithNoCache";
        getterMethod = "get" + StringUtils.capitalize(getterMethod);
        Method method = BeanUtils.findDeclaredMethodWithMinimalParameters(getOwner().getClass(), getterMethod);
        if (method != null) {
            try {
                bag = (List) method.invoke(getOwner(), new Object[]{});
                setInitialized();
                setDirectlyAccessible(true);
            } catch (Exception e1) {
                e1.printStackTrace();
            }
        } else {
        	logger.error("Unable to find the method name: " + getterMethod + " for entity: " + getOwner().getClass());
        }
    }

    public void removeDeletedEntities() {
        if (bag != null) {
            for (int i = 0; i < bag.size();) {
                Entity e = (Entity) bag.get(i);
                if (e.isDeletedEntity()) {
                    if (logger.isDebugEnabled()) {
                        logger.debug("Removing deleted entity from the collection. Entity: " + e);
                    }
                    bag.remove(i);
                } else {
                    i++;
                }
            }
        }
    }

	public boolean equalsSnapshot(CollectionPersister persister) throws HibernateException {
		List sn = (List) getSnapshot();
		if (sn == null) {
			return true;
		}
		return super.equalsSnapshot(persister);
	}
	
	public boolean isSnapshotEmpty(Serializable snapshot) {
		return snapshot == null || ((Collection) snapshot ).isEmpty();
	}

}
