package gueei.binding;

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

public abstract class DependentObservable<T> extends Observable<T> implements Observer{

	protected IObservable<?>[] mDependents;
	
	public DependentObservable(Class<T> type, IObservable<?>... dependents) {
		super(type);
		for(IObservable<?> o : dependents){
			o.subscribe(this);
		}
		this.mDependents = dependents;
		this.onPropertyChanged(null, new ArrayList<Object>());
	}
	
	// This is provided in case the constructor can't be used. 
	// Not intended for normal usage
	public void addDependents(IObservable<?>... dependents){
		IObservable<?>[] temp = mDependents;
		mDependents = new IObservable<?>[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(this);
		}
		this.onPropertyChanged(null, new ArrayList<Object>());
	}

	public abstract T calculateValue(Object... args) throws Exception;
	
	public final void onPropertyChanged(IObservable<?> prop,
			Collection<Object> initiators) {
		dirty = true;
		initiators.add(this);
		this.notifyChanged(initiators);
	}

	private boolean dirty = false;
	
	@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(values);
				this.setWithoutNotify(value);
			}catch(Exception e){
				BindingLog.exception
					("DependentObservable.CalculateValue()", e);
			}
			dirty = false;
		}
		return super.get();
	}

	public boolean isDirty() {
		return dirty;
	}

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