package org.openmrs.module.reporting.web.controller.portlet;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.servlet.http.HttpServletRequest;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.openmrs.module.htmlwidgets.web.WidgetUtil;
import org.openmrs.module.reporting.common.ReflectionUtil;
import org.openmrs.module.reporting.data.patient.definition.PersonToPatientDataDefinition;
import org.openmrs.module.reporting.data.patient.definition.ScriptedCompositionPatientDataDefinition;
import org.openmrs.module.reporting.data.person.definition.PersonDataDefinition;
import org.openmrs.module.reporting.evaluation.parameter.Mapped;
import org.openmrs.module.reporting.evaluation.parameter.Parameter;
import org.openmrs.module.reporting.evaluation.parameter.Parameterizable;
import org.openmrs.module.reporting.evaluation.parameter.ParameterizableUtil;
import org.openmrs.util.OpenmrsUtil;
import org.openmrs.web.WebConstants;
import org.springframework.stereotype.Controller;
import org.springframework.ui.ModelMap;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
@Controller
public class MappedPropertyPortletFormController {
protected static Log log = LogFactory.getLog(MappedPropertyPortletFormController.class);
/**
* Default Constructor
*/
public MappedPropertyPortletFormController() { }
/**
* Saves mapped parameters
*/
@RequestMapping("/module/reporting/reports/saveMappedProperty")
@SuppressWarnings("unchecked")
public String saveMappedProperty(ModelMap model, HttpServletRequest request,
@RequestParam(required=true, value="type") Class<? extends Parameterizable> type,
@RequestParam(required=true, value="uuid") String uuid,
@RequestParam(required=true, value="property") String property,
@RequestParam(required=false, value="currentKey") String currentKey,
@RequestParam(required=false, value="newKey") String newKey,
@RequestParam(required=false, value="mappedUuid") String mappedUuid) {
Parameterizable parent = ParameterizableUtil.getParameterizable(uuid, type);
Field f = ReflectionUtil.getField(type, property);
Class<?> fieldType = ReflectionUtil.getFieldType(f);
Class<? extends Parameterizable> mappedType = null;
if (StringUtils.isNotEmpty(property)) {
mappedType = ParameterizableUtil.getMappedType(type, property);
}
Mapped m = null;
Object previousValue = ReflectionUtil.getPropertyValue(parent, property);
if (StringUtils.isNotEmpty(mappedUuid)) {
Parameterizable valToSet = ParameterizableUtil.getParameterizable(mappedUuid, mappedType);
// TODO We need to find a more generic way of converting data definitions.
// If the definition being mapped is of unsupported type, the code should
// be able to find a proper adapter class and convert the definition to
// the supported type.
if(parent instanceof ScriptedCompositionPatientDataDefinition && valToSet instanceof PersonDataDefinition)
valToSet = new PersonToPatientDataDefinition((PersonDataDefinition) valToSet);
m = new Mapped();
m.setParameterizable(valToSet);
for (Parameter p : valToSet.getParameters()) {
String valueType = request.getParameterValues("valueType_"+p.getName())[0];
String[] value = request.getParameterValues(valueType+"Value_"+p.getName());
if (value != null && value.length > 0) {
Object paramValue = null;
if (StringUtils.isEmpty(valueType) || valueType.equals("fixed")) {
String fixedValueString = OpenmrsUtil.join(Arrays.asList(value), ",");
paramValue = WidgetUtil.parseInput(fixedValueString, p.getType());
}
else {
paramValue = "${"+value[0]+"}";
}
if (paramValue != null) {
m.addParameterMapping(p.getName(), paramValue);
}
}
}
}
if (previousValue != null || m != null) {
if (List.class.isAssignableFrom(fieldType)) {
List newValue = null;
if (previousValue == null) {
newValue = new ArrayList();
newValue.add(m);
}
else if (m != null) {
newValue = (List)previousValue;
if (StringUtils.isEmpty(newKey)) {
newValue.add(m);
}
else {
int listIndex = Integer.parseInt(newKey);
newValue.set(listIndex, m);
}
}
ReflectionUtil.setPropertyValue(parent, f, newValue);
}
else if (Map.class.isAssignableFrom(fieldType)) {
if (m != null) {
Map newValue = (previousValue == null ? new HashMap() : (Map)previousValue);
newValue.put(newKey, m);
if (!newKey.equals(currentKey) && currentKey != null) {
newValue.remove(currentKey);
}
ReflectionUtil.setPropertyValue(parent, f, newValue);
}
}
else if (Mapped.class.isAssignableFrom(fieldType)) {
ReflectionUtil.setPropertyValue(parent, f, m);
}
else {
throw new IllegalArgumentException("Cannot set property of type: " + fieldType + " to " + m);
}
}
ParameterizableUtil.saveParameterizable(parent);
return "redirect:/module/reporting/closeWindow.htm";
}
/**
* Remove mapped property
*/
@RequestMapping("/module/reporting/reports/removeMappedProperty")
@SuppressWarnings("unchecked")
public String removeMappedProperty(ModelMap model, HttpServletRequest request,
@RequestParam(required=true, value="type") Class<? extends Parameterizable> type,
@RequestParam(required=true, value="uuid") String uuid,
@RequestParam(required=true, value="property") String property,
@RequestParam(required=true, value="currentKey") String currentKey,
@RequestParam(required=true, value="returnUrl") String returnUrl) {
Parameterizable parent = ParameterizableUtil.getParameterizable(uuid, type);
Field f = ReflectionUtil.getField(type, property);
Class<?> fieldType = ReflectionUtil.getFieldType(f);
Object previousValue = ReflectionUtil.getPropertyValue(parent, property);
if (List.class.isAssignableFrom(fieldType)) {
List v = (List)previousValue;
int listIndex = Integer.parseInt(currentKey);
v.remove(listIndex);
}
else if (Map.class.isAssignableFrom(fieldType)) {
Map v = (Map)previousValue;
v.remove(currentKey);
}
else {
throw new IllegalArgumentException("Cannot remove property in fieldType: " + fieldType + " with key " + currentKey);
}
ReflectionUtil.setPropertyValue(parent, f, previousValue);
ParameterizableUtil.saveParameterizable(parent);
String pathToRemove = "/" + WebConstants.WEBAPP_NAME;
if (returnUrl.startsWith(pathToRemove)) {
returnUrl = returnUrl.substring(pathToRemove.length());
}
return "redirect:"+returnUrl;
}
}