/**
   * @see
   *     org.openmrs.module.htmlformentry.element.HtmlGeneratorElement#generateHtml(org.openmrs.module.htmlformentry.FormEntryContext)
   */
  @Override
  public String generateHtml(FormEntryContext context) {
    if (context.getExistingPatient() == null) {
      return "";
    }

    KenyaUiUtils kenyaui = Context.getRegisteredComponents(KenyaUiUtils.class).get(0);

    PatientWrapper patient = new PatientWrapper(context.getExistingPatient());

    Obs obs = patient.lastObs(MetadataUtils.getConcept(conceptId));

    StringBuilder sb = new StringBuilder("<span>");

    if (obs != null) {
      sb.append(kenyaui.formatObsValue(obs));

      if (showDate) {
        sb.append(" <small>(" + kenyaui.formatDate(obs.getObsDatetime()) + ")</small>");
      }
    } else if (noneMessage != null) {
      sb.append(noneMessage);
    }

    sb.append("</span>");
    return sb.toString();
  }
  /**
   * @see FormSubmissionControllerAction#validateSubmission(FormEntryContext, HttpServletRequest)
   */
  @Override
  public Collection<FormSubmissionError> validateSubmission(
      FormEntryContext context, HttpServletRequest submission) {
    List<FormSubmissionError> ret = new ArrayList<FormSubmissionError>();

    try {
      if (dateWidget != null) {
        Date date = (Date) dateWidget.getValue(context, submission);
        if (timeWidget != null) {
          Date time = (Date) timeWidget.getValue(context, submission);
          date = HtmlFormEntryUtil.combineDateAndTime(date, time);
        }
        if (date == null) throw new Exception("htmlformentry.error.required");
        if (OpenmrsUtil.compare((Date) date, new Date()) > 0)
          throw new Exception("htmlformentry.error.cannotBeInFuture");
      }
    } catch (Exception ex) {
      ret.add(
          new FormSubmissionError(
              context.getFieldName(dateErrorWidget),
              Context.getMessageSourceService().getMessage(ex.getMessage())));
    }

    try {
      if (providerWidget != null) {
        Object value = providerWidget.getValue(context, submission);
        Person provider = (Person) convertValueToProvider(value);
        if (provider == null) throw new Exception("required");
      }
    } catch (Exception ex) {
      ret.add(
          new FormSubmissionError(
              context.getFieldName(providerErrorWidget),
              Context.getMessageSourceService().getMessage(ex.getMessage())));
    }

    try {
      if (locationWidget != null) {
        Object value = locationWidget.getValue(context, submission);
        Location location =
            (Location) HtmlFormEntryUtil.convertToType(value.toString().trim(), Location.class);
        if (location == null) throw new Exception("required");
      }
    } catch (Exception ex) {
      ret.add(
          new FormSubmissionError(
              context.getFieldName(locationErrorWidget),
              Context.getMessageSourceService().getMessage(ex.getMessage())));
    }
    try {
      if (encounterTypeWidget != null) {
        Object encounterType = encounterTypeWidget.getValue(context, submission);
        if (encounterType == null) throw new Exception("required");
      }
    } catch (Exception ex) {
      ret.add(
          new FormSubmissionError(
              context.getFieldName(encounterTypeErrorWidget),
              Context.getMessageSourceService().getMessage(ex.getMessage())));
    }
    return ret;
  }
  /**
   * Construct a new EncounterDetailSubmissionElement
   *
   * @param context
   * @param parameters
   * @should display 'Enter' option if 'type' is set to Autocomplete
   */
  public EncounterDetailSubmissionElement(
      FormEntryContext context, Map<String, Object> parameters) {

    // Register Date and Time widgets, if appropriate
    if (Boolean.TRUE.equals(parameters.get("date"))) {

      dateWidget = new DateWidget();
      dateErrorWidget = new ErrorWidget();

      if (context.getExistingEncounter() != null) {
        dateWidget.setInitialValue(context.getExistingEncounter().getEncounterDatetime());
      } else if (parameters.get("defaultDate") != null) {
        dateWidget.setInitialValue(parameters.get("defaultDate"));
      }

      if (parameters.get("disallowMultipleEncountersOnDate") != null
          && StringUtils.hasText((String) parameters.get("disallowMultipleEncountersOnDate"))) {
        dateWidget.setOnChangeFunction(
            "existingEncounterOnDate(this, '"
                + parameters.get("disallowMultipleEncountersOnDate")
                + "') ");
      }

      if ("true".equals(parameters.get("showTime"))) {
        timeWidget = new TimeWidget();
        timeErrorWidget = new ErrorWidget();
        if (context.getExistingEncounter() != null) {
          timeWidget.setInitialValue(context.getExistingEncounter().getEncounterDatetime());
        } else if (parameters.get("defaultDate") != null) {
          timeWidget.setInitialValue(parameters.get("defaultDate"));
        }
        context.registerWidget(timeWidget);
        context.registerErrorWidget(timeWidget, timeErrorWidget);
      }
      context.registerWidget(dateWidget);
      context.registerErrorWidget(dateWidget, dateErrorWidget);
    }

    // Register Provider widgets, if appropriate
    if (Boolean.TRUE.equals(parameters.get("provider"))) {

      if ("autocomplete".equals(parameters.get("type"))) {
        providerWidget = new AutocompleteWidget(Person.class);
      } else {
        providerWidget = new DropdownWidget();
      }
      providerErrorWidget = new ErrorWidget();

      List<Option> providerOptions = new ArrayList<Option>();
      // If specific persons are specified, display only those persons in order
      String personsParam = (String) parameters.get("persons");
      if (personsParam != null) {
        for (String s : personsParam.split(",")) {
          Person p = HtmlFormEntryUtil.getPerson(s);
          if (p == null) {
            throw new RuntimeException("Cannot find Person: " + s);
          }
          String label = p.getPersonName().getFullName();
          providerOptions.add(new Option(label, p.getId().toString(), false));
        }
        removeNonProviders(providerOptions);
      }

      // Only if specific person ids are not passed in do we get by user Role
      if (providerOptions.isEmpty()) {

        List<PersonStub> users = new ArrayList<PersonStub>();
        List<Option> providerUsers = new ArrayList<Option>();

        // If the "role" attribute is passed in, limit to users with this role
        if (parameters.get("role") != null) {
          Role role = Context.getUserService().getRole((String) parameters.get("role"));
          if (role == null) {
            throw new RuntimeException("Cannot find role: " + parameters.get("role"));
          } else {
            users =
                Context.getService(HtmlFormEntryService.class)
                    .getUsersAsPersonStubs(role.getRole());
          }
        }

        // Otherwise, use default options appropriate to the underlying OpenMRS version
        else {
          if (openmrsVersionDoesNotSupportProviders()) {
            // limit to users with the default OpenMRS PROVIDER role,
            String defaultRole = OpenmrsConstants.PROVIDER_ROLE;
            Role role = Context.getUserService().getRole(defaultRole);
            if (role != null) {
              users =
                  Context.getService(HtmlFormEntryService.class)
                      .getUsersAsPersonStubs(role.getRole());
            }
            // If this role isn't used, default to all Users
            if (users.isEmpty()) {
              users = Context.getService(HtmlFormEntryService.class).getUsersAsPersonStubs(null);
            }
          } else {
            // in OpenMRS 1.9+, get all suitable providers
            users = getAllProvidersThatArePersonsAsPersonStubs();
          }
        }

        for (PersonStub personStub : users) {

          Option option = new Option(personStub.toString(), personStub.getId().toString(), false);
          providerUsers.add(option);
        }
        providerOptions.addAll(providerUsers);
      }

      // Set default values as appropriate
      Person defaultProvider = null;
      Option defProviderOption;
      if (context.getExistingEncounter() != null) {
        defaultProvider = context.getExistingEncounter().getProvider();
        // this is done to avoid default provider being added twice due to that it can be added from
        // the
        // users = getAllProvidersThatArePersonsAsPersonStubs(); section with selected="false",
        // therefore this can't be caught when
        // searching whether the options list contains the 'defaultProvider'
        boolean defaultOptionPresent = false;
        if (defaultProvider != null) {
          for (Option option : providerOptions) {
            if (option.getValue().equals(defaultProvider.getId().toString())) {
              defaultOptionPresent = true;
              providerOptions.remove(option);
              break;
            }
          }
        }
        if (defaultOptionPresent) {
          defProviderOption =
              new Option(
                  defaultProvider.getPersonName().getFullName(),
                  defaultProvider.getId().toString(),
                  true);
          providerOptions.add(defProviderOption);
        }

      } else {
        String defParam = (String) parameters.get("default");
        if (StringUtils.hasText(defParam)) {
          if ("currentuser".equalsIgnoreCase(defParam)) {
            defaultProvider = Context.getAuthenticatedUser().getPerson();
          } else {
            defaultProvider = HtmlFormEntryUtil.getPerson(defParam);
          }
          if (defaultProvider == null) {
            throw new IllegalArgumentException(
                "Invalid default provider specified for encounter: " + defParam);
          } else {
            defProviderOption =
                new Option(
                    defaultProvider.getPersonName().getFullName(),
                    defaultProvider.getId().toString(),
                    true);
            for (Option option : providerOptions) {
              if (option.getValue().equals(defProviderOption.getValue())) {
                providerOptions.remove(option);
                break;
              }
            }
            providerOptions.add(defProviderOption);
          }
        }
      }
      if (defaultProvider != null) {
        providerWidget.setInitialValue(new PersonStub(defaultProvider));
      }
      Collections.sort(providerOptions, new OptionComparator());

      if (("autocomplete").equals(parameters.get("type"))) {
        providerWidget.addOption(new Option());
        if (!providerOptions.isEmpty()) {
          providerWidget.setOptions(providerOptions);
        }

      } else {
        // if initialValueIsSet=false, no initial/default provider, hence this shows the 'select
        // input' field as first option
        boolean initialValueIsSet = !(providerWidget.getInitialValue() == null);
        providerWidget.addOption(
            new Option(
                Context.getMessageSourceService().getMessage("htmlformentry.chooseAProvider"),
                "",
                !initialValueIsSet)); // if no initial or default value

        if (!providerOptions.isEmpty()) {
          for (Option option : providerOptions) {
            providerWidget.addOption(option);
          }
        }
      }
      context.registerWidget(providerWidget);
      context.registerErrorWidget(providerWidget, providerErrorWidget);
    }

    if (Boolean.TRUE.equals(parameters.get("encounterType"))) {
      encounterTypeWidget = new EncounterTypeWidget();
      encounterTypeErrorWidget = new ErrorWidget();
      if (parameters.get("types") != null) {
        List<EncounterType> encounterTypes = new ArrayList<EncounterType>();
        String[] temp = ((String) parameters.get("types")).split(",");
        for (String s : temp) {
          EncounterType type = HtmlFormEntryUtil.getEncounterType(s);
          if (type == null) {
            throw new RuntimeException("Cannot find encounter type: " + s);
          }
          encounterTypes.add(type);
        }

        encounterTypeWidget.setOptions(encounterTypes);
      }
      // Set default values

      EncounterType defaultEncounterType = null;
      if (context.getExistingEncounter() != null) {
        defaultEncounterType = context.getExistingEncounter().getEncounterType();
      } else {
        String defaultTypeId = (String) parameters.get("default");
        if (StringUtils.hasText(defaultTypeId)) {
          defaultEncounterType = HtmlFormEntryUtil.getEncounterType(defaultTypeId);
        }
      }

      encounterTypeWidget.setInitialValue(defaultEncounterType);
      context.registerWidget(encounterTypeWidget);
      context.registerErrorWidget(encounterTypeWidget, encounterTypeErrorWidget);
    }

    // Register Location widgets, if appropriate
    if (Boolean.TRUE.equals(parameters.get("location"))) {

      locationErrorWidget = new ErrorWidget();
      List<Location> locations = new ArrayList<Location>();
      List<Option> locationOptions = new ArrayList<Option>();

      if ("autocomplete".equals(parameters.get("type"))) {
        locationWidget = new AutocompleteWidget(Location.class);
      } else {
        locationWidget = new DropdownWidget();
      }

      // If the "order" attribute is passed in, limit to the specified locations in order
      if (parameters.get("order") != null) {

        String[] temp = ((String) parameters.get("order")).split(",");
        for (String s : temp) {
          Location loc = HtmlFormEntryUtil.getLocation(s);
          if (loc == null) {
            throw new RuntimeException("Cannot find location: " + loc);
          }
          locations.add(loc);
        }
      }

      // Set default values
      Location defaultLocation = null;
      if (context.getExistingEncounter() != null) {
        defaultLocation = context.getExistingEncounter().getLocation();
      } else {
        String defaultLocId = (String) parameters.get("default");
        if (StringUtils.hasText(defaultLocId)) {
          defaultLocation = HtmlFormEntryUtil.getLocation(defaultLocId);
        }
      }
      defaultLocation = defaultLocation == null ? context.getDefaultLocation() : defaultLocation;
      locationWidget.setInitialValue(defaultLocation);

      if (!locations.isEmpty()) {
        for (Location location : locations) {
          String label = location.getName();
          Option option =
              new Option(label, location.getId().toString(), location.equals(defaultLocation));
          locationOptions.add(option);
        }
      } else {
        locations = Context.getLocationService().getAllLocations();
        for (Location location : locations) {
          String label = location.getName();
          Option option =
              new Option(label, location.getId().toString(), location.equals(defaultLocation));
          locationOptions.add(option);
        }
        Collections.sort(locationOptions, new OptionComparator());
      }

      if ("autocomplete".equals(parameters.get("type"))) {
        locationWidget.addOption(new Option());
        if (!locationOptions.isEmpty()) {
          locationWidget.setOptions(locationOptions);
        }
      } else {
        boolean initialValueIsSet = !(locationWidget.getInitialValue() == null);
        locationWidget.addOption(
            new Option(
                Context.getMessageSourceService().getMessage("htmlformentry.chooseALocation"),
                "",
                !initialValueIsSet));
        if (!locationOptions.isEmpty()) {
          for (Option option : locationOptions) locationWidget.addOption(option);
        }
      }
      context.registerWidget(locationWidget);
      context.registerErrorWidget(locationWidget, locationErrorWidget);
    }

    if (Boolean.TRUE.equals(parameters.get("showVoidEncounter"))
        && context.getMode()
            == Mode
                .EDIT) { // only show void option if the encounter already exists.  And VIEW implies
                         // not voided.
      if (parameters.get("toggle") != null) {
        ToggleWidget toggleWidget = new ToggleWidget((String) parameters.get("toggle"));
        voidWidget =
            new CheckboxWidget(
                " " + Context.getMessageSourceService().getMessage("general.voided"),
                (context.getExistingEncounter() != null
                        && context.getExistingEncounter().isVoided().equals(true))
                    ? "true"
                    : "false",
                toggleWidget.getTargetId(),
                toggleWidget.isToggleDim());
      } else {
        voidWidget = new CheckboxWidget();
      }
      voidWidget.setLabel(" " + Context.getMessageSourceService().getMessage("general.voided"));
      voidErrorWidget = new ErrorWidget();
      if (context.getExistingEncounter() != null
          && context.getExistingEncounter().isVoided().equals(true))
        voidWidget.setInitialValue("true");
      context.registerWidget(voidWidget);
      context.registerErrorWidget(voidWidget, voidErrorWidget);
    }

    // set the id, if it has been specified
    if (parameters.get("id") != null) {
      id = (String) parameters.get("id");
    }
  }
  /** @see HtmlGeneratorElement#generateHtml(FormEntryContext) */
  @Override
  public String generateHtml(FormEntryContext context) {
    StringBuilder ret = new StringBuilder();

    // if an id has been specified, wrap the whole encounter element in a span tag so that we access
    // property values via javascript
    // also register property accessors for all the widgets
    if (id != null) {
      ret.append("<span id='" + id + "'>");

      // note that if this element ever handles multiple widgets, the names of the provider and
      // location accessors will need unique names
      if (dateWidget != null) {
        context.registerPropertyAccessorInfo(
            id + ".value",
            context.getFieldNameIfRegistered(dateWidget),
            "dateFieldGetterFunction",
            null,
            "dateSetterFunction");
        context.registerPropertyAccessorInfo(
            id + ".error", context.getFieldNameIfRegistered(dateErrorWidget), null, null, null);
      } else if (providerWidget != null) {
        context.registerPropertyAccessorInfo(
            id + ".value", context.getFieldNameIfRegistered(providerWidget), null, null, null);
        context.registerPropertyAccessorInfo(
            id + ".error", context.getFieldNameIfRegistered(providerErrorWidget), null, null, null);
      } else if (locationWidget != null) {
        context.registerPropertyAccessorInfo(
            id + ".value", context.getFieldNameIfRegistered(locationWidget), null, null, null);
        context.registerPropertyAccessorInfo(
            id + ".error", context.getFieldNameIfRegistered(locationErrorWidget), null, null, null);
      } else if (encounterTypeWidget != null) {
        context.registerPropertyAccessorInfo(
            id + ".value", context.getFieldNameIfRegistered(encounterTypeWidget), null, null, null);
        context.registerPropertyAccessorInfo(
            id + ".error",
            context.getFieldNameIfRegistered(encounterTypeErrorWidget),
            null,
            null,
            null);
      }
    }

    if (dateWidget != null) {
      ret.append(dateWidget.generateHtml(context));
      if (context.getMode() != Mode.VIEW) ret.append(dateErrorWidget.generateHtml(context));
    }
    if (timeWidget != null) {
      ret.append("&#160;");
      ret.append(timeWidget.generateHtml(context));
      if (context.getMode() != Mode.VIEW) ret.append(timeErrorWidget.generateHtml(context));
    }
    if (providerWidget != null) {
      ret.append(providerWidget.generateHtml(context));
      if (context.getMode() != Mode.VIEW) ret.append(providerErrorWidget.generateHtml(context));
    }
    if (locationWidget != null) {
      ret.append(locationWidget.generateHtml(context));
      if (context.getMode() != Mode.VIEW) ret.append(locationErrorWidget.generateHtml(context));
    }
    if (encounterTypeWidget != null) {
      ret.append(encounterTypeWidget.generateHtml(context));
      if (context.getMode() != Mode.VIEW)
        ret.append(encounterTypeErrorWidget.generateHtml(context));
    }
    if (voidWidget != null) {
      if (context.getMode() == Mode.EDIT) // only show void option if the encounter already exists.
      ret.append(voidWidget.generateHtml(context));
    }

    // close out the span if we have an id tag
    if (id != null) {
      ret.append("</span>");
    }

    return ret.toString();
  }