package org.activityinfo.legacy.shared.adapter;
import com.google.common.base.Functions;
import com.google.common.collect.Maps;
import com.google.gwt.user.client.rpc.AsyncCallback;
import org.activityinfo.model.form.FormInstance;
import org.activityinfo.legacy.client.Dispatcher;
import org.activityinfo.legacy.shared.command.CreateLocation;
import org.activityinfo.legacy.shared.command.GetAdminEntities;
import org.activityinfo.legacy.shared.command.result.AdminEntityResult;
import org.activityinfo.legacy.shared.model.AdminEntityDTO;
import org.activityinfo.legacy.shared.model.AdminLevelDTO;
import org.activityinfo.model.legacy.CuidAdapter;
import org.activityinfo.model.resource.ResourceId;
import org.activityinfo.model.type.FieldValue;
import org.activityinfo.model.type.geo.GeoPoint;
import org.activityinfo.model.type.primitive.TextValue;
import org.activityinfo.promise.Promise;
import java.util.LinkedList;
import java.util.Map;
import java.util.Queue;
import java.util.Set;
import static org.activityinfo.model.legacy.CuidAdapter.*;
/**
* Updates/creates a location object.
* <p/>
* <p>The legacy api expects the location to be denormalized with
* ALL ancestors, so we have to fetch them before we're able to persist</p>
*/
public class LocationPersister {
private final Dispatcher dispatcher;
private final FormInstance instance;
private Promise<Void> callback;
private Map<String, Object> properties;
private Queue<Integer> parents = new LinkedList<>();
private ResourceId classId;
public LocationPersister(Dispatcher dispatcher, FormInstance instance) {
this.dispatcher = dispatcher;
this.instance = instance;
this.classId = instance.getClassId();
}
public Promise<Void> persist() {
callback = new Promise<Void>();
properties = Maps.newHashMap();
properties.put("id", CuidAdapter.getLegacyIdFromCuid(instance.getId()));
properties.put("locationTypeId", getLegacyIdFromCuid(classId));
properties.put("name", unwrapText(instance.get(field(classId, NAME_FIELD))));
properties.put("axe", unwrapText(instance.get(field(classId, AXE_FIELD))));
GeoPoint point = (GeoPoint) instance.get(field(classId, GEOMETRY_FIELD));
if (point != null) {
properties.put("latitude", point.getLatitude());
properties.put("longitude", point.getLongitude());
}
Set<ResourceId> adminEntities = instance.getReferences(field(classId, ADMIN_FIELD));
if (adminEntities != null) {
for (ResourceId adminEntityResourceId : adminEntities) {
parents.add(getLegacyIdFromCuid(adminEntityResourceId));
}
}
resolveNextParent();
return callback;
}
private Object unwrapText(FieldValue fieldValue) {
if(fieldValue instanceof TextValue) {
return fieldValue.toString();
} else {
return null;
}
}
private void resolveNextParent() {
if (parents.isEmpty()) {
persistLocation();
return;
}
final GetAdminEntities query = new GetAdminEntities();
query.setEntityId(parents.poll());
dispatcher.execute(query, new AsyncCallback<AdminEntityResult>() {
@Override
public void onFailure(Throwable caught) {
callback.onFailure(caught);
}
@Override
public void onSuccess(AdminEntityResult result) {
if (result.getData().isEmpty()) {
callback.onFailure(new IllegalStateException("No entity with id = " + query.getEntityIds()));
}
AdminEntityDTO entity = result.getData().get(0);
properties.put(AdminLevelDTO.getPropertyName(entity.getLevelId()), entity.getId());
if (entity.getParentId() != null && !parents.contains(entity.getParentId())) {
parents.add(entity.getParentId());
}
resolveNextParent();
}
});
}
private void persistLocation() {
CreateLocation command = new CreateLocation(properties);
dispatcher.execute(command).then(Functions.<Void>constant(null)).then(callback);
}
}