/**
   * Checks the form object for any inconsistencies/errors
   *
   * @see org.springframework.validation.Validator#validate(java.lang.Object,
   *     org.springframework.validation.Errors)
   * @should fail validation if retired and retireReason is null or empty or whitespace
   * @should pass validation if all required fields have proper values
   */
  public void validate(Object obj, Errors errors) {
    User user = (User) obj;
    if (user == null) {
      errors.rejectValue("user", "error.general");
    } else {
      if (user.isRetired() && !StringUtils.hasText(user.getRetireReason()))
        errors.rejectValue("retireReason", "error.null");
      if (user.getPerson() == null) {
        errors.rejectValue("person", "error.null");
      } else {
        // check that required person details are filled out
        Person person = user.getPerson();
        if (person.getGender() == null) errors.rejectValue("person.gender", "error.null");
        if (person.getDead() == null) errors.rejectValue("person.dead", "error.null");
        if (person.getVoided() == null) errors.rejectValue("person.voided", "error.null");
        if (person.getPersonName() == null
            || !StringUtils.hasText(person.getPersonName().toString()))
          errors.rejectValue("person", "Person.names.length");
      }
    }

    if (!isUserNameValid(user.getUsername()))
      errors.rejectValue("username", "error.username.pattern");
  }
  /**
   * 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");
    }
  }