package edu.ualberta.med.biobank.mvp.presenter.state;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import com.google.gwt.user.client.ui.HasValue;
import com.pietschy.gwt.pectin.client.binding.Disposable;
import com.pietschy.gwt.pectin.client.condition.OrFunction;
import com.pietschy.gwt.pectin.client.condition.ReducingCondition;
import com.pietschy.gwt.pectin.client.value.ValueModel;
import edu.ualberta.med.biobank.mvp.presenter.HasState;
import edu.ualberta.med.biobank.mvp.presenter.IStatefulPresenter;
import edu.ualberta.med.biobank.mvp.user.ui.HasList;
import edu.ualberta.med.biobank.mvp.view.IView;
public class ModelState implements HasState, Disposable {
private final ReducingCondition dirty = new ReducingCondition(
new OrFunction(), new ArrayList<ValueModel<Boolean>>());
private final Set<HasState> states = new HashSet<HasState>();
private final List<Disposable> disposables = new ArrayList<Disposable>();
public void add(IView view) {
Class<?>[] interfaces = view.getClass().getInterfaces();
for (Class<?> klazz : interfaces) {
if (IView.class.isAssignableFrom(klazz)) {
@SuppressWarnings("unchecked")
Class<? extends IView> iView = (Class<? extends IView>) klazz;
add(view, iView);
}
}
}
public void add(IStatefulPresenter presenter) {
HasState state = presenter.getState();
addState(state);
}
public <T> void add(HasValue<T> source) {
ValueState<T> valueState = new ValueState<T>(source);
addAbstractState(valueState);
}
public <E> void add(HasList<E> source) {
ListState<E> listState = new ListState<E>(source);
addAbstractState(listState);
}
@Override
public ValueModel<Boolean> dirty() {
return dirty;
}
@Override
public void checkpoint() {
dirty.recomputeAfterRunning(new CheckpointRunnable());
}
@Override
public void revert() {
dirty.recomputeAfterRunning(new RevertRunnable());
}
@Override
public void dispose() {
for (Disposable disposable : disposables) {
disposable.dispose();
}
}
private void add(IView view, Class<? extends IView> klazz) {
for (Method method : klazz.getMethods()) {
Class<?> returnType = method.getReturnType();
try {
if (HasValue.class.isAssignableFrom(returnType)) {
HasValue<?> hasValue = (HasValue<?>) method.invoke(view);
add(hasValue);
} else if (HasList.class.isAssignableFrom(returnType)) {
HasList<?> hasList = (HasList<?>) method.invoke(view);
add(hasList);
}
} catch (Exception caught) {
// TODO: need a logger (for non-user errors).
}
}
}
private void addState(HasState state) {
states.add(state);
dirty.addSourceModel(state.dirty());
}
private void addAbstractState(AbstractState state) {
addState(state);
disposables.add(state);
}
private class CheckpointRunnable implements Runnable {
@Override
public void run() {
for (HasState state : states) {
state.checkpoint();
}
}
}
private class RevertRunnable implements Runnable {
@Override
public void run() {
for (HasState state : states) {
state.revert();
}
}
}
}