package gueei.binding.collections;

import java.util.ArrayList;
import java.util.Collection;

import gueei.binding.BindingLog;
import gueei.binding.CollectionChangedEventArg;
import gueei.binding.CollectionObserver;
import gueei.binding.IObservableCollection;
import gueei.binding.Observable;
import gueei.binding.collections.ArrayListObservable;

public abstract class DependentCollectionObservable<T> extends Observable<T> implements CollectionObserver {

	protected IObservableCollection<?>[] mDependents;
	
	public DependentCollectionObservable(Class<T> type, IObservableCollection<?>... dependents) {
		super(type);
		for(IObservableCollection<?> o : dependents){
			o.subscribe((CollectionObserver)this);
		}
		this.mDependents = dependents;
		ArrayList<Object> initiators = new ArrayList<Object>();
		initiators.add(this);
		this.onCollectionChanged(new ArrayListObservable<Object>(null), null, initiators);
	}
	
	// This is provided in case the constructor can't be used. 
	// Not intended for normal usage
	public void addDependents(IObservableCollection<?>... dependents){
		IObservableCollection<?>[] temp = mDependents;
		mDependents = new IObservableCollection<?>[temp.length + dependents.length];
		int len = temp.length;
		for(int i=0; i<len; i++){
			mDependents[i] = temp[i];
		}
		int len2 = dependents.length;
		for(int i=0; i<len2; i++){
			mDependents[i+len] = dependents[i];
			dependents[i].subscribe((CollectionObserver)this);
		}
		ArrayList<Object> initiators = new ArrayList<Object>();
		initiators.add(this);
		this.onCollectionChanged(new ArrayListObservable<Object>(null), null, initiators);
	}
	
	public abstract T calculateValue(CollectionChangedEventArg e, Object... args) throws Exception;

	@Override
	public final void onCollectionChanged
		(IObservableCollection<?> collection, CollectionChangedEventArg args, Collection<Object> initiators) {
		dirty = true;
		changedArgs = args;		
		initiators.add(collection);
		this.notifyChanged(initiators);		
	}
	
	private boolean dirty = false;	
	private CollectionChangedEventArg changedArgs = null;

	@Override
	public T get() {
		if (dirty){
			int len = mDependents.length;
			Object[] values = new Object[len];
			for(int i=0; i<len; i++){
				values[i] = mDependents[i].get();
			}
			try{
				T value = this.calculateValue(changedArgs, values);
				this.setWithoutNotify(value);
			}catch(Exception e){
				BindingLog.exception
					("DependentCollectionObservable.CalculateValue()", e);
			}
			dirty = false;
			changedArgs = null;
		}
		return super.get();
	}

	public boolean isDirty() {
		return dirty;
	}

	public void setDirty(boolean dirty) {
		this.dirty = dirty;
	}
}
