package org.activityinfo.ui.client.component.filter;
import com.extjs.gxt.ui.client.widget.ContentPanel;
import com.google.common.collect.ArrayListMultimap;
import com.google.common.collect.Lists;
import com.google.common.collect.Multimap;
import com.google.gwt.core.client.GWT;
import com.google.gwt.event.logical.shared.ValueChangeHandler;
import com.google.gwt.event.shared.GwtEvent;
import com.google.gwt.event.shared.HandlerRegistration;
import com.google.gwt.user.client.rpc.AsyncCallback;
import org.activityinfo.legacy.client.Dispatcher;
import org.activityinfo.legacy.client.callback.SuccessCallback;
import org.activityinfo.legacy.shared.Log;
import org.activityinfo.legacy.shared.command.Filter;
import org.activityinfo.legacy.shared.command.GetActivityForms;
import org.activityinfo.legacy.shared.command.GetAttributeGroupsDimension;
import org.activityinfo.legacy.shared.command.GetSchema;
import org.activityinfo.legacy.shared.command.result.ActivityFormResults;
import org.activityinfo.legacy.shared.command.result.AttributeGroupResult;
import org.activityinfo.legacy.shared.model.AttributeGroupDTO;
import org.activityinfo.legacy.shared.model.SchemaDTO;
import org.activityinfo.legacy.shared.util.CollectionUtil;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Set;
import static org.activityinfo.ui.client.component.filter.AttributeGroupFilterWidget.DIMENSION_TYPE;
public class AttributeGroupFilterWidgets implements FilterPanel {
private final Dispatcher service;
private final ContentPanel panel;
private ValueChangeHandler<Filter> valueChangeHandler;
private SuccessCallback<Void> drawCallback;
private Filter prevFilter = new Filter();
;
private AttributeGroupResult prevResult;
private List<AttributeGroupDTO> groups;
private List<AttributeGroupFilterWidget> widgets;
private Multimap<String, AttributeGroupFilterWidget> duplicates;
public AttributeGroupFilterWidgets(ContentPanel panel,
Dispatcher service,
ValueChangeHandler<Filter> valueChangeHandler,
SuccessCallback<Void> drawCallback) {
this.service = service;
this.panel = panel;
this.groups = Lists.newArrayList();
this.widgets = Lists.newArrayList();
this.duplicates = ArrayListMultimap.create();
this.valueChangeHandler = valueChangeHandler;
this.drawCallback = drawCallback;
}
public void draw(final Filter filter) {
final Filter value = new Filter(filter);
value.clearRestrictions(DIMENSION_TYPE);
// prevents executing the same command needlessly
if (prevFilter == null || !prevFilter.equals(filter)) {
prevFilter = filter;
Log.debug("AttributeGroupFilterWidgets called for filter " + filter);
// retrieve all attribute groups for the current filter
service.execute(new GetAttributeGroupsDimension(value), new AsyncCallback<AttributeGroupResult>() {
@Override
public void onFailure(Throwable caught) {
GWT.log("Failed to load attributes", caught);
}
@Override
public void onSuccess(final AttributeGroupResult attributeGroupResult) {
// result should be different from the last time, we checked the filter earlier, but we could have
// an exceptional case of the same result with a different filter, so check for equality again.
// if the result is indeed different from the last time, (re)draw the widgets
if (prevResult == null || !prevResult.equals(attributeGroupResult)) {
prevResult = attributeGroupResult;
Log.debug("AttributeGroupFilterWidgets drawing widgets for result: " + attributeGroupResult);
service.execute(new GetActivityForms(filter), new AsyncCallback<ActivityFormResults>() {
@Override
public void onFailure(Throwable caught) {
GWT.log("Failed to load schema", caught);
}
@Override
public void onSuccess(final ActivityFormResults schema) {
// clean up old widgets
for (AttributeGroupFilterWidget widget : widgets) {
panel.remove(widget);
}
duplicates.clear();
// decorate result list from schema
List<AttributeGroupDTO> pivotData = attributeGroupResult.getData();
groups = new ArrayList<AttributeGroupDTO>();
if (CollectionUtil.isNotEmpty(pivotData)) {
for (AttributeGroupDTO pivotGroup : pivotData) {
AttributeGroupDTO schemaGroup = schema.getAttributeGroupById(pivotGroup.getId
());
if (schemaGroup != null) {
groups.add(schemaGroup);
}
}
}
// create new widgets, one for each attributegroup.
// remember the old selection
List<Integer> selection = getSelectedIds();
widgets = new ArrayList<AttributeGroupFilterWidget>();
for (AttributeGroupDTO group : groups) {
// create
AttributeGroupFilterWidget widget = new AttributeGroupFilterWidget(group);
// set old selection
widget.setSelection(selection);
// what to do when value changes
if (valueChangeHandler != null) {
widget.addValueChangeHandler(valueChangeHandler);
}
// add widget to panel if a widget with the same name (ignoring case) hasn't
// already been added
if (isNoDuplicate(widget)) {
widgets.add(widget);
panel.add(widget);
} else {
// otherwise add to collection of duplicates
duplicates.put(group.getName().toLowerCase(), widget);
}
}
if (drawCallback != null) {
drawCallback.onSuccess(null);
}
}
});
}
}
});
}
}
private boolean isNoDuplicate(AttributeGroupFilterWidget widget) {
for (AttributeGroupFilterWidget alreadyAdded : widgets) {
if (alreadyAdded.getGroup().getName().toLowerCase().equals(widget.getGroup().getName().toLowerCase())) {
return false;
}
}
return true;
}
private List<Integer> getSelectedIds() {
List<Integer> list = new ArrayList<Integer>();
for (AttributeGroupFilterWidget widget : widgets) {
Set<Integer> selection = widget.getValue().getRestrictions(DIMENSION_TYPE);
if (CollectionUtil.isNotEmpty(selection)) {
// if the widget has at least one selection, check the duplicates if we need to add some more ids
Collection<AttributeGroupFilterWidget> hiddenWidgets = duplicates.get(widget.getGroup()
.getName()
.toLowerCase());
if (CollectionUtil.isNotEmpty(hiddenWidgets)) {
// has duplicates, so collect the attribute-ids from the hidden widgets by the
// selected attribute-names of the visible widget
List<String> selectedAttributeNames = widget.getSelectedAttributeNames();
for (AttributeGroupFilterWidget hiddenWidget : hiddenWidgets) {
List<Integer> hiddenIds = hiddenWidget.getAttributeIdsByName(selectedAttributeNames);
if (CollectionUtil.isNotEmpty(hiddenIds)) {
selection.addAll(hiddenIds);
}
}
}
list.addAll(selection);
}
}
return list;
}
public void clearFilter() {
for (AttributeGroupFilterWidget widget : widgets) {
widget.clear();
}
}
@Override
public Filter getValue() {
Filter filter = new Filter();
List<Integer> selectedIds = getSelectedIds();
if (selectedIds.size() > 0) {
filter.addRestriction(DIMENSION_TYPE, selectedIds);
}
return filter;
}
@Override
public void applyBaseFilter(Filter filter) {
draw(filter);
}
@Override
/** only sets the selection. To (re)draw the widgets based on the possibly new filter,
* call applyBaseFilter or draw */
public void setValue(Filter value) {
setValue(value, false);
}
@Override
/** only sets the selection. To (re)draw the widgets based on the possibly new filter,
* call applyBaseFilter or draw */
public void setValue(Filter value, boolean fireEvents) {
if (value.isRestricted(DIMENSION_TYPE)) {
Collection<Integer> restriction = value.getRestrictions(DIMENSION_TYPE);
for (AttributeGroupFilterWidget widget : widgets) {
widget.setSelection(restriction);
}
}
}
@Override
/** not implemented */
public HandlerRegistration addValueChangeHandler(ValueChangeHandler<Filter> handler) {
return null;
}
@Override
/** not implemented */
public void fireEvent(GwtEvent<?> event) {
}
}