package cz.cuni.amis.utils.collections;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;

/**
 * Wrapper for collections. It raises events when elements are added/removed 
 * from the collection.
 * @author Ik
 * @param <E>
 */
public class ObservableCollection<E> implements Collection<E> {

    protected Collection<E> col = null;
    protected List<CollectionEventListener<E>> eventListeners = new ArrayList<CollectionEventListener<E>>();

    public void addCollectionListener(CollectionEventListener listener) {
        eventListeners.add(listener);
    }

    public boolean removeCollectionListener(CollectionEventListener listener) {
        return eventListeners.remove(listener);
    }

    public ObservableCollection(Collection c) {
        col = c;
    }

    @Override
    public int size() {
        return col.size();
    }

    @Override
    public boolean isEmpty() {
        return col.isEmpty();
    }

    @Override
    public boolean contains(Object o) {
        return col.contains(o);
    }

    @Override
    public Iterator<E> iterator() {
        return col.iterator();
    }

    @Override
    public Object[] toArray() {
        return col.toArray();
    }

    @Override
    public <T> T[] toArray(T[] a) {
        return col.toArray(a);
    }

    @Override
    public boolean add(E e) {
        Collection add = Collections.singletonList(e);
        notifyPreAdd(add);
        boolean ret = col.add(e);
        notifyPostAdd(add);
        return ret;
    }

    @Override
    public boolean remove(Object o) {
        Collection rem = Collections.singletonList(o);
        notifyPreRemove(rem);
        boolean ret = col.remove(o);
        notifyPostRemove(rem);
        return ret;
    }

    @Override
    public boolean containsAll(Collection<?> c) {
        return col.containsAll(c);
    }

    @Override
    public boolean addAll(Collection<? extends E> c) {
        notifyPreAdd(c);
        boolean ret = col.addAll(c);
        notifyPostAdd(c);
        return ret;

    }

    @Override
    public boolean removeAll(Collection<?> c) {
        notifyPreRemove(c);
        boolean ret = col.removeAll(c);
        notifyPostRemove(c);
        return ret;
    }

    @Override
    public boolean retainAll(Collection<?> c) {
        // TODO compute differences
        return col.retainAll(c);
    }

    @Override
    public void clear() {
        notifyPreRemove(col);
        col.clear();
        notifyPostRemove(col);
    }

    protected void notifyPreAdd(Collection<? extends E> add) {
        for (CollectionEventListener l : eventListeners) {
            l.preAddEvent(add, this);
        }
    }

    protected void notifyPostAdd(Collection<? extends E> add) {
        for (CollectionEventListener l : eventListeners) {
            l.postAddEvent(add, this);
        }
    }

    protected void notifyPreRemove(Collection<?> remove) {
        for (CollectionEventListener l : eventListeners) {
            l.preRemoveEvent(remove, this);
        }
    }

    protected void notifyPostRemove(Collection<?> remove) {
        for (CollectionEventListener l : eventListeners) {
            l.postRemoveEvent(remove, this);
        }
    }
}