package org.activityinfo.server.endpoint.odk;
import com.google.common.collect.Iterables;
import com.google.common.collect.Lists;
import com.google.inject.Inject;
import org.activityinfo.model.legacy.CuidAdapter;
import org.activityinfo.model.resource.ResourceId;
import org.activityinfo.model.type.*;
import org.activityinfo.model.type.barcode.BarcodeType;
import org.activityinfo.model.type.enumerated.EnumItem;
import org.activityinfo.model.type.enumerated.EnumType;
import org.activityinfo.model.type.geo.GeoPointType;
import org.activityinfo.model.type.image.ImageType;
import org.activityinfo.model.type.number.QuantityType;
import org.activityinfo.model.type.primitive.BooleanType;
import org.activityinfo.model.type.primitive.TextType;
import org.activityinfo.model.type.time.LocalDateType;
import org.activityinfo.server.command.ResourceLocatorSync;
import org.activityinfo.io.xform.form.BindingType;
import org.activityinfo.io.xform.form.Item;
import org.activityinfo.service.lookup.ReferenceChoice;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Set;
import java.util.logging.Logger;
public class OdkFormFieldBuilderFactory {
private static final Logger LOGGER = Logger.getLogger(OdkFormFieldBuilderFactory.class.getName());
final private ResourceLocatorSync locator;
@Inject
public OdkFormFieldBuilderFactory(ResourceLocatorSync table) {
this.locator = table;
}
public OdkFormFieldBuilder get(FieldType fieldType) {
if (fieldType instanceof ParametrizedFieldType) {
ParametrizedFieldType parametrizedFieldType = (ParametrizedFieldType) fieldType;
if (!parametrizedFieldType.isValid()) return null;
}
if (fieldType instanceof BarcodeType) {
return new SimpleInputBuilder(BindingType.BARCODE);
}
if (fieldType instanceof BooleanType) {
return new SelectBuilder(BindingType.BOOLEAN, booleanOptions());
}
if (fieldType instanceof EnumType) {
SelectOptions options = enumOptions((EnumType) fieldType);
if(options.isEmpty()) {
return null;
} else {
return new SelectBuilder(BindingType.STRING, options);
}
}
if (fieldType instanceof GeoPointType) {
return new SimpleInputBuilder(BindingType.GEOPOINT);
}
if (fieldType instanceof ImageType) {
return new UploadBuilder("image/*");
}
if (fieldType instanceof LocalDateType) {
return new SimpleInputBuilder(BindingType.DATE);
}
if (fieldType instanceof NarrativeType) {
return new SimpleInputBuilder(BindingType.STRING);
}
if (fieldType instanceof QuantityType) {
return new QuantityFieldBuilder((QuantityType) fieldType);
}
if (fieldType instanceof ReferenceType) {
Set<ResourceId> range = ((ReferenceType) fieldType).getRange();
if(isSmallSet(range)) {
return new SelectBuilder(BindingType.STRING, referenceOptions((ReferenceType) fieldType));
} else {
return new ReferenceBuilder(range);
}
}
if (fieldType instanceof TextType) {
return new SimpleInputBuilder(BindingType.STRING);
}
// If this happens, it means this class needs to be expanded to support the new FieldType class.
LOGGER.warning("Unknown FieldType: " + fieldType.getClass().getName());
return null;
}
private boolean isSmallSet(Set<ResourceId> range) {
// TODO: hardcoded to include partner/projects in form
if(range.size() == 1) {
ResourceId formClassId = Iterables.getOnlyElement(range);
if(formClassId.getDomain() == CuidAdapter.PARTNER_FORM_CLASS_DOMAIN ||
formClassId.getDomain() == CuidAdapter.PROJECT_CLASS_DOMAIN) {
return true;
}
}
return false;
}
private SelectOptions enumOptions(EnumType enumType) {
Cardinality cardinality = enumType.getCardinality();
List<Item> items = Lists.newArrayListWithCapacity(enumType.getValues().size());
for (EnumItem enumItem : enumType.getValues()) {
Item item = new Item();
item.setLabel(enumItem.getLabel());
item.setValue(enumItem.getId().asString());
items.add(item);
}
return new SelectOptions(cardinality, items);
}
private SelectOptions booleanOptions() {
Item no = new Item();
no.setLabel("no");
no.setValue("FALSE");
Item yes = new Item();
yes.setLabel("yes");
yes.setValue("TRUE");
return new SelectOptions(Cardinality.SINGLE, Arrays.asList(yes, no));
}
private SelectOptions referenceOptions(ReferenceType referenceType) {
ArrayList<Item> items = Lists.newArrayList();
for (ReferenceChoice choice : locator.getReferenceChoices(referenceType.getRange())) {
Item item = new Item();
item.setLabel(choice.getLabel());
item.setValue(choice.getId().asString());
items.add(item);
}
return new SelectOptions(referenceType.getCardinality(), items);
}
}