package org.activityinfo.legacy.shared.adapter;
import com.google.common.base.Function;
import com.google.common.base.Preconditions;
import org.activityinfo.legacy.shared.model.PartnerDTO;
import org.activityinfo.model.form.FormInstance;
import org.activityinfo.legacy.client.Dispatcher;
import org.activityinfo.legacy.shared.adapter.bindings.SiteBinding;
import org.activityinfo.legacy.shared.adapter.bindings.SiteBindingFactory;
import org.activityinfo.legacy.shared.command.*;
import org.activityinfo.legacy.shared.command.result.BatchResult;
import org.activityinfo.legacy.shared.command.result.CommandResult;
import org.activityinfo.legacy.shared.model.AdminEntityDTO;
import org.activityinfo.legacy.shared.model.AdminLevelDTO;
import org.activityinfo.legacy.shared.model.LocationTypeDTO;
import org.activityinfo.model.legacy.CuidAdapter;
import org.activityinfo.model.legacy.KeyGenerator;
import org.activityinfo.promise.Promise;
import javax.annotation.Nullable;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* Persists a FormInstance as a Site
*/
public class SitePersister {
private final Dispatcher dispatcher;
public SitePersister(Dispatcher dispatcher) {
this.dispatcher = dispatcher;
}
public Promise<Void> persist(final FormInstance siteInstance) {
int activityId = CuidAdapter.getLegacyIdFromCuid(siteInstance.getClassId());
return dispatcher.execute(new GetActivityForm(activityId))
.then(new SiteBindingFactory())
.join(new Function<SiteBinding, Promise<Void>>() {
@Nullable @Override
public Promise<Void> apply(@Nullable SiteBinding binding) {
return persist(binding, siteInstance).thenDiscardResult();
}
});
}
private Promise<? extends CommandResult> persist(SiteBinding siteBinding, FormInstance instance) {
Map<String, Object> siteProperties = siteBinding.toChangePropertyMap(instance);
siteProperties.put("activityId", siteBinding.getActivity().getId());
if (siteProperties.get("reportingPeriodId") == null) { // indicators are not saved if report id is not set
siteProperties.put("reportingPeriodId", new KeyGenerator().generateInt());
}
if (siteProperties.get("partnerId") == null) {
siteProperties.put("partnerId", siteBinding.getDefaultPartnerId());
}
if (siteBinding.getLocationType().isNationwide()) {
siteProperties.put("locationId", siteBinding.getLocationType().getId());
}
final CreateSite createSite = new CreateSite(siteProperties);
if (siteBinding.getLocationType().isAdminLevel()) {
// we need to create the dummy location as well
Promise<Command> createLocation = Promise.resolved(siteBinding.getAdminEntityId(instance))
.join(new FetchEntityFunction())
.then(new CreateDummyLocation(createSite.getLocationId(),
siteBinding.getLocationType()));
return createLocation.join(new Function<Command, Promise<BatchResult>>() {
@Nullable @Override
public Promise<BatchResult> apply(@Nullable Command createLocation) {
return dispatcher.execute(new BatchCommand(createLocation, createSite));
}
});
} else {
return dispatcher.execute(createSite);
}
}
private class FetchEntityFunction implements Function<Integer, Promise<List<AdminEntityDTO>>> {
@Nullable @Override
public Promise<List<AdminEntityDTO>> apply(@Nullable Integer input) {
GetAdminEntities query = new GetAdminEntities().setEntityId(input);
Promise<AdminEntityDTO> entity = dispatcher.execute(query)
.then(new SingleListResultAdapter<AdminEntityDTO>());
Promise<List<AdminEntityDTO>> parents = entity.join(new FetchParentsFunction());
return Promise.prepend(entity, parents);
}
}
private class FetchParentsFunction implements Function<AdminEntityDTO, Promise<List<AdminEntityDTO>>> {
@Override
public Promise<List<AdminEntityDTO>> apply(AdminEntityDTO input) {
if (input.getParentId() == null) {
return Promise.resolved(Collections.<AdminEntityDTO>emptyList());
} else {
return Promise.resolved(input.getParentId()).join(new FetchEntityFunction());
}
}
}
private class CreateDummyLocation implements Function<List<AdminEntityDTO>, Command> {
private final LocationTypeDTO locationType;
private int locationId;
private CreateDummyLocation(int locationId, LocationTypeDTO locationType) {
this.locationType = locationType;
this.locationId = locationId;
}
@Override
public CreateLocation apply(List<AdminEntityDTO> entities) {
AdminEntityDTO entity = entities.get(0);
Preconditions.checkState(entity.getLevelId() == locationType.getBoundAdminLevelId());
Map<String, Object> properties = new HashMap<>();
properties.put("id", locationId);
properties.put("locationTypeId", locationType.getId());
properties.put("name", entity.getName());
for (AdminEntityDTO parent : entities) {
properties.put(AdminLevelDTO.getPropertyName(parent.getLevelId()), parent.getId());
}
return new CreateLocation(properties);
}
}
}