/**
   * @param ownerIdentifier
   * @param model
   * @param fbo
   * @param bindingResult
   * @return
   * @throws NotAVisitorException
   * @throws OwnerNotFoundException
   * @throws SchedulingException
   */
  @RequestMapping(method = RequestMethod.POST)
  protected String createAppointment(
      final ModelMap model,
      @PathVariable("ownerIdentifier") final String ownerIdentifier,
      @Valid @ModelAttribute(COMMAND_ATTR_NAME) final CreateAppointmentFormBackingObject fbo,
      BindingResult bindingResult)
      throws NotAVisitorException, OwnerNotFoundException, SchedulingException {
    CalendarAccountUserDetailsImpl currentUser =
        (CalendarAccountUserDetailsImpl)
            SecurityContextHolder.getContext().getAuthentication().getPrincipal();
    IScheduleVisitor visitor = currentUser.getScheduleVisitor();

    if (bindingResult.hasErrors()) {
      return "visitor/create-appointment-form";
    }

    IScheduleOwner selectedOwner = null;
    if (StringUtils.isNumeric(ownerIdentifier)) {
      Long ownerId = Long.parseLong(ownerIdentifier);
      selectedOwner = findOwnerForVisitor(visitor, ownerId);
    } else {
      PublicProfile profile = publicProfileDao.locatePublicProfileByKey(ownerIdentifier);
      if (null != profile) {
        selectedOwner = ownerDao.locateOwnerByAvailableId(profile.getOwnerId());
      }
    }

    if (null == selectedOwner) {
      throw new OwnerNotFoundException("no owner found for " + ownerIdentifier);
    }

    validateChosenStartTime(
        selectedOwner.getPreferredVisibleWindow(), fbo.getTargetBlock().getStartTime());

    AvailableBlock finalAppointmentBlock = fbo.getTargetBlock();
    if (fbo.isDoubleLengthAvailable()) {
      // check if selected meeting duration matches meeting durations maxLength
      // if it's greater, then we need to look up the next block in the schedule and attempt to
      // combine
      if (fbo.getSelectedDuration() == fbo.getMeetingDurations().getMaxLength()) {
        finalAppointmentBlock =
            availableScheduleDao.retrieveTargetDoubleLengthBlock(
                selectedOwner, finalAppointmentBlock.getStartTime());
      }
    }
    if (null == finalAppointmentBlock) {
      throw new SchedulingException("requested time is not available");
    }

    VEvent event =
        schedulingAssistantService.scheduleAppointment(
            visitor, selectedOwner, finalAppointmentBlock, fbo.getReason());
    model.put("event", event);
    model.put("owner", selectedOwner);
    model.put("ownerRemindersPreference", selectedOwner.getRemindersPreference());
    return "visitor/create-appointment-success";
  }
  /**
   * @param model
   * @param startTimePhrase
   * @param ownerId
   * @return
   * @throws InputFormatException
   * @throws SchedulingException
   * @throws OwnerNotFoundException
   * @throws NotAVisitorException
   */
  @RequestMapping(method = RequestMethod.GET)
  protected String setupForm(
      final ModelMap model,
      @RequestParam(value = "startTime", required = true) final String startTimePhrase,
      @PathVariable("ownerIdentifier") final String ownerIdentifier)
      throws InputFormatException, SchedulingException, OwnerNotFoundException,
          NotAVisitorException {
    CalendarAccountUserDetailsImpl currentUser =
        (CalendarAccountUserDetailsImpl)
            SecurityContextHolder.getContext().getAuthentication().getPrincipal();
    IScheduleVisitor visitor = currentUser.getScheduleVisitor();

    IScheduleOwner selectedOwner = null;
    if (StringUtils.isNumeric(ownerIdentifier)) {
      Long ownerId = Long.parseLong(ownerIdentifier);
      selectedOwner = findOwnerForVisitor(visitor, ownerId);
    } else {
      PublicProfile profile = publicProfileDao.locatePublicProfileByKey(ownerIdentifier);
      if (null != profile) {
        selectedOwner = ownerDao.locateOwnerByAvailableId(profile.getOwnerId());
      }
    }

    if (null == selectedOwner) {
      throw new OwnerNotFoundException("no owner found for " + ownerIdentifier);
    }
    model.put("owner", selectedOwner);
    Date startTime = CommonDateOperations.parseDateTimePhrase(startTimePhrase);
    validateChosenStartTime(selectedOwner.getPreferredVisibleWindow(), startTime);

    AvailableBlock targetBlock = availableScheduleDao.retrieveTargetBlock(selectedOwner, startTime);
    if (null == targetBlock) {
      throw new SchedulingException("requested time is not available");
    }

    if (selectedOwner.hasMeetingLimit()) {
      VisibleSchedule sched = schedulingAssistantService.getVisibleSchedule(visitor, selectedOwner);
      int attendingCount = sched.getAttendingCount();
      if (selectedOwner.isExceedingMeetingLimit(attendingCount)) {
        // visitor has already matched owner's appointment limit
        LOG.warn(
            "blocked attempt to use create form by visitor: "
                + visitor
                + ", target owner: "
                + selectedOwner);
        return "redirect:view.html";
      }
    }

    VEvent event = schedulingAssistantService.getExistingAppointment(targetBlock, selectedOwner);
    if (event != null) {
      model.put("event", event);
      if (this.eventUtils.isAttendingAsVisitor(event, visitor.getCalendarAccount())) {
        // redirect the visitor to the cancel form
        StringBuilder redirect = new StringBuilder("redirect:cancel.html?r=true&startTime=");
        SimpleDateFormat dateFormat = CommonDateOperations.getDateTimeFormat();
        redirect.append(startTimePhrase);
        redirect.append("&endTime=");
        redirect.append(dateFormat.format(targetBlock.getEndTime()));
        return redirect.toString();
      }

      Integer visitorLimit = this.eventUtils.getEventVisitorLimit(event);
      model.put("visitorLimit", visitorLimit);
      if (this.eventUtils.getScheduleVisitorCount(event) >= visitorLimit) {
        return "visitor/appointment-full";
      }
    }
    CreateAppointmentFormBackingObject fbo =
        new CreateAppointmentFormBackingObject(
            targetBlock, selectedOwner.getPreferredMeetingDurations());
    model.addAttribute(COMMAND_ATTR_NAME, fbo);
    return "visitor/create-appointment-form";
  }