/**
 * This action demonstrates the usage of project milestone service and project responsible person
 * service in Cockpit.
 *
 * <p>Version 1.1 (Module Assembly - TC Cockpit Project Milestones Management Front End) update: -
 * Update {@link #updateProjectMilestone()} according to the new milestone service update. - Fix the
 * error handling of ajax based operation methods.
 *
 * @author GreatKevin
 * @version 1.1
 */
public class ProjectMilestoneDemoAction extends BaseDirectStrutsAction {

  /** All the avaible milestone status. */
  private static final List ALL_MILESTONE_STATUS = Arrays.asList(MilestoneStatus.values());

  /** The date format used for display the milestone due date. */
  private static final DateFormat dateFormat = new SimpleDateFormat("MM/dd/yyyy");

  /** The view data which stores all the projects of the user. */
  private DashboardSearchResultsDTO viewData = new DashboardSearchResultsDTO();

  /** The direct project id. */
  private long directProjectId;

  /** The milestone id. */
  private long milestoneId;

  /** The single milestone instance. */
  private Milestone milestone;

  /** A list of project milestones. */
  private List<Milestone> milestones;

  /** A list of milestone ids. */
  private List<Long> milestoneIds;

  /**
   * The main execution, get all the user's projects.
   *
   * @throws Exception if error.
   */
  @Override
  protected void executeAction() throws Exception {
    HttpServletRequest request = DirectUtils.getServletRequest();
    SessionData sessionData = new SessionData(request.getSession());
    TCSubject currentUser = getCurrentUser();

    // Set projects data
    List<ProjectBriefDTO> projects = DataProvider.getUserProjects(currentUser.getUserId());
    UserProjectsDTO userProjectsDTO = new UserProjectsDTO();
    userProjectsDTO.setProjects(projects);
    getViewData().setUserProjects(userProjectsDTO);

    // Set current project contests
    ProjectBriefDTO currentProject = sessionData.getCurrentProjectContext();
    if (currentProject != null) {
      List<TypedContestBriefDTO> contests =
          DataProvider.getProjectTypedContests(currentUser.getUserId(), currentProject.getId());
      sessionData.setCurrentProjectContests(contests);
    }

    viewData.setProjects(DataProvider.searchUserProjects(currentUser, ""));
  }

  /**
   * Method to handle the ajax operation getProjectMilestones. Gets all the milestones of the
   * project and return as json.
   *
   * @return the result code.
   * @throws Exception if error.
   */
  public String getProjectMilestones() throws Exception {
    List<Map<String, Object>> result = new LinkedList<Map<String, Object>>();

    try {
      List<Milestone> all =
          getMilestoneService()
              .getAll(getDirectProjectId(), ALL_MILESTONE_STATUS, SortOrder.DESCENDING);

      for (Milestone m : all) {

        Map<String, Object> milestoneData = new HashMap<String, Object>();
        milestoneData.put("id", m.getId());
        milestoneData.put("name", m.getName());
        milestoneData.put("description", m.getDescription());
        milestoneData.put("dueDate", dateFormat.format(m.getDueDate()));

        String ownerName = "none";
        Long ownerId = -1L;

        if (m.getOwners() != null && m.getOwners().size() > 0) {
          ownerName = m.getOwners().get(0).getName();
          ownerId = m.getOwners().get(0).getUserId();
        }

        milestoneData.put("ownerName", ownerName);
        milestoneData.put("ownerUserId", ownerId);
        milestoneData.put("notification", m.isSendNotifications());
        milestoneData.put("completed", m.isCompleted());
        milestoneData.put("status", m.getStatus().toString());

        result.add(milestoneData);
      }

      setResult(result);

    } catch (Throwable e) {
      // set the error message into the ajax response
      if (getModel() != null) {
        setResult(e.getMessage());
      }

      return ERROR;
    }

    return SUCCESS;
  }

  /**
   * Method to handle the ajax operation getProjectResponsiblePerson.
   *
   * <p>Gets all the responsible person of the project and return as json.
   *
   * @return the result code.
   * @throws Exception if error.
   */
  public String getProjectResponsiblePerson() throws Exception {
    List<Map<String, Object>> result = new LinkedList<Map<String, Object>>();

    try {
      List<ResponsiblePerson> allResponsiblePeople =
          getMilestoneResponsiblePersonService().getAllResponsiblePeople(getDirectProjectId());

      for (ResponsiblePerson p : allResponsiblePeople) {

        Map<String, Object> data = new HashMap<String, Object>();
        data.put("userId", p.getUserId());
        data.put("name", p.getName());
        result.add(data);
      }

      setResult(result);

    } catch (Throwable e) {
      // set the error message into the ajax response
      if (getModel() != null) {
        setResult(e);
      }

      return ERROR;
    }

    return SUCCESS;
  }

  /**
   * Method to handle the ajax operation removeProjectMilestone.
   *
   * <p>It removes the project mielstone specified by the milestone id.
   *
   * @return the result code.
   * @throws Exception if error.
   */
  public String removeProjectMilestone() throws Exception {
    Map<String, Object> result = new HashMap<String, Object>();

    try {

      getMilestoneService().delete(getMilestoneId());

      result.put("operation", "remove");
      result.put("milestoneId", getMilestoneId());

      setResult(result);

    } catch (Throwable e) {
      // set the error message into the ajax response
      if (getModel() != null) {
        setResult(e.getMessage());
      }

      return ERROR;
    }

    return SUCCESS;
  }

  /**
   * Method to handle the ajax operation addProjectMilestone.
   *
   * <p>It adds the new milestone specified by the Milestone object.
   *
   * @return the result code.
   * @throws Exception if error.
   */
  public String addProjectMilestone() throws Exception {
    Map<String, Object> result = new HashMap<String, Object>();

    try {

      long newMilestoneId = getMilestoneService().add(getMilestone());

      result.put("operation", "add");
      result.put("milestoneId", newMilestoneId);

      setResult(result);

    } catch (Throwable e) {
      // set the error message into the ajax response
      if (getModel() != null) {
        setResult(e.getMessage());
      }

      return ERROR;
    }

    return SUCCESS;
  }

  /**
   * Method to handle the ajax operation updateProjectMilestone.
   *
   * <p>It updates the existing milestone specified by the Milestone object.
   *
   * @return the result code.
   * @throws Exception if error.
   */
  public String updateProjectMilestone() throws Exception {
    Map<String, Object> result = new HashMap<String, Object>();

    try {
      Milestone m = getMilestone();

      getMilestoneService().update(m);

      result.put("operation", "update");
      result.put("milestoneId", getMilestone().getId());

      setResult(result);

    } catch (Throwable e) {
      // set the error message into the ajax response
      if (getModel() != null) {
        setResult(e);
      }

      return ERROR;
    }

    return SUCCESS;
  }

  /**
   * Method to handle the ajax operation addProjectMilestones.
   *
   * <p>It adds a list of project milestones in one batch.
   *
   * @return the result code.
   * @throws Exception if error.
   */
  public String addProjectMilestones() throws Exception {
    Map<String, Object> result = new HashMap<String, Object>();

    try {
      getMilestoneService().add(getMilestones());

      result.put("operation", "addAll");

      setResult(result);

    } catch (Throwable e) {
      // set the error message into the ajax response
      if (getModel() != null) {
        setResult(e);
      }

      return ERROR;
    }

    return SUCCESS;
  }

  /**
   * Removes multiple project milestones at one time.
   *
   * @return the result code.
   * @throws Exception if error.
   */
  public String removeProjectMilestones() throws Exception {
    Map<String, Object> result = new HashMap<String, Object>();

    try {
      getMilestoneService().delete(getMilestoneIds());

      result.put("operation", "deleteAll");

      setResult(result);

    } catch (Throwable e) {
      // set the error message into the ajax response
      if (getModel() != null) {
        setResult(e.getMessage());
      }

      return ERROR;
    }

    return SUCCESS;
  }

  /**
   * Gets the view data.
   *
   * @return the view data.
   */
  public DashboardSearchResultsDTO getViewData() {
    return viewData;
  }

  /**
   * Sets the view data.
   *
   * @param viewData the view data.
   */
  public void setViewData(DashboardSearchResultsDTO viewData) {
    this.viewData = viewData;
  }

  /**
   * Gets the direct project id.
   *
   * @return the direct project id.
   */
  public long getDirectProjectId() {
    return directProjectId;
  }

  /**
   * Sets the direct project id.
   *
   * @param directProjectId the direct project id.
   */
  public void setDirectProjectId(long directProjectId) {
    this.directProjectId = directProjectId;
  }

  /**
   * Gets the project milestone.
   *
   * @return the project milestone.
   */
  public Milestone getMilestone() {
    return milestone;
  }

  /**
   * Sets the project milestone.
   *
   * @param milestone the project milestone
   */
  public void setMilestone(Milestone milestone) {
    this.milestone = milestone;
  }

  /**
   * Gets the list of project milestones.
   *
   * @return the list of project milestones.
   */
  public List<Milestone> getMilestones() {
    return milestones;
  }

  /**
   * Sets the list of project milestones.
   *
   * @param milestones the list of project milestones.
   */
  public void setMilestones(List<Milestone> milestones) {
    this.milestones = milestones;
  }

  /**
   * Gets the id of the milestone.
   *
   * @return the id of the milestone.
   */
  public long getMilestoneId() {
    return milestoneId;
  }

  /**
   * Sets the id of the milestone.
   *
   * @param milestoneId the id of the milestone.
   */
  public void setMilestoneId(long milestoneId) {
    this.milestoneId = milestoneId;
  }

  /**
   * Gets the list of milestone ids.
   *
   * @return the list of milestone ids.
   */
  public List<Long> getMilestoneIds() {
    return milestoneIds;
  }

  /**
   * Sets the list of milestone ids.
   *
   * @param milestoneIds the list of milestone ids.
   */
  public void setMilestoneIds(List<Long> milestoneIds) {
    this.milestoneIds = milestoneIds;
  }
}
/**
 * This action class handles all the views for project milestone management: project milestone list
 * view, project milestone calendar view and multiple project milestones batch creation view.
 *
 * <p>Version 1.1 (Module Assembly - TC Cockpit Contest Milestone Association Milestone Page Update)
 *
 * <ul>
 *   <li>Updated {@link #executeAction()} to remove the codes to get milestone list view data, it's
 *       changed to get through ajax request
 *   <li>Added method {@link #getProjectMilestoneListData()} to get the project milestone list data
 * </ul>
 *
 * <p>Version 1.2 (TopCoder Direct - Quick Bug Fixes 09.23)
 *
 * <ul>
 *   <li>Updated {@link #getProjectResponsiblePerson()} to only retrieve for positive (valid)
 *       project ID
 * </ul>
 *
 * @author GreatKevin, Veve
 * @version 1.2
 */
public class ProjectMilestoneViewAction extends BaseDirectStrutsAction
    implements FormAction<ProjectMilestoneViewForm> {

  /** The default user handle color to display for the responsible person of the milestone. */
  private static final String DEFAULT_USER_HANDLE_CLASS = "coderTextOrange";

  /** The default user handle link for the responsible person of the milestone. */
  private static final String DEFAULT_USER_HANDLE_URL = "javascript:;";

  /** List of all the available milestone status. */
  private static final List ALL_MILESTONE_STATUS = Arrays.asList(MilestoneStatus.values());

  /** The date string format of milestone due date and completion date. */
  private final DateFormat CALENDAR_DATE_FORMAT = new SimpleDateFormat("yyyy-MM-dd");

  /** The form data. */
  private ProjectMilestoneViewForm formData = new ProjectMilestoneViewForm();

  /** The view data. */
  private ProjectMilestoneViewDTO viewData = new ProjectMilestoneViewDTO();

  /**
   * Redirects to different view by checking the view type.
   *
   * @return the result code.
   * @throws Exception if there is error.
   */
  @Override
  public String execute() throws Exception {
    String resultCode = super.execute();
    if (SUCCESS.equals(resultCode)) {
      if (formData.getViewType().equals(ProjectMilestoneViewForm.LIST_VIEW)) {
        return ProjectMilestoneViewForm.LIST_VIEW;
      }
      if (formData.getViewType().equals(ProjectMilestoneViewForm.CALENDAR_VIEW)) {
        return ProjectMilestoneViewForm.CALENDAR_VIEW;
      }
      if (formData.getViewType().equals(ProjectMilestoneViewForm.MULTIPLE_CREATION_VIEW)) {
        return ProjectMilestoneViewForm.MULTIPLE_CREATION_VIEW;
      }
    }

    return resultCode;
  }

  /**
   * Main execution for the action, prepare different set of data according to the view type.
   *
   * @throws Exception if there is any error.
   */
  @Override
  protected void executeAction() throws Exception {

    // set responsible person data
    final List<ResponsiblePerson> allResponsiblePeople =
        getMilestoneResponsiblePersonService()
            .getAllResponsiblePeople(getFormData().getProjectId());
    getViewData().setResponsiblePersons(allResponsiblePeople);

    // right side bar data
    List<ProjectBriefDTO> projects =
        DataProvider.getUserProjects(getSessionData().getCurrentUserId());
    List<TypedContestBriefDTO> contests =
        DataProvider.getProjectTypedContests(
            getSessionData().getCurrentUserId(), formData.getProjectId());
    UserProjectsDTO userProjectsDTO = new UserProjectsDTO();
    userProjectsDTO.setProjects(projects);

    viewData.setUserProjects(userProjectsDTO);

    // put project into the session
    if (contests.size() > 0) {
      getSessionData().setCurrentProjectContext(contests.get(0).getProject());
    } else {
      for (ProjectBriefDTO p : projects) {
        if (p.getId() == getFormData().getProjectId()) {
          getSessionData().setCurrentProjectContext(p);
          break;
        }
      }
    }
    getSessionData().setCurrentSelectDirectProjectID(getFormData().getProjectId());

    // set project contests
    getSessionData().setCurrentProjectContests(contests);
  }

  /**
   * Gets the responsible person list for the project.
   *
   * @return the result code.
   * @throws Exception if there is any error.
   */
  public String getProjectResponsiblePerson() throws Exception {
    List<Map<String, Object>> result = new LinkedList<Map<String, Object>>();

    try {

      if (getFormData().getProjectId() > 0) {
        List<Permission> permissionsByProject =
            getPermissionServiceFacade()
                .getPermissionsByProject(
                    DirectUtils.getTCSubjectFromSession(), getFormData().getProjectId());

        for (Permission p : permissionsByProject) {
          Map<String, Object> data = new HashMap<String, Object>();
          data.put("userId", p.getUserId());
          data.put("name", p.getUserHandle());
          result.add(data);
        }
      }

      setResult(result);

    } catch (Throwable e) {
      // set the error message into the ajax response
      if (getModel() != null) {
        setResult(e);
      }
      return ERROR;
    }
    return SUCCESS;
  }

  /**
   * Gets the project milestones calendar dat via ajax.
   *
   * @return the result code.
   * @throws Exception if there is any error.
   */
  public String getProjectMilestoneCalendarData() throws Exception {
    List<Map<String, Object>> result = new LinkedList<Map<String, Object>>();

    try {
      final List<Milestone> allMilestones =
          this.getMilestoneService()
              .getAll(getFormData().getProjectId(), ALL_MILESTONE_STATUS, SortOrder.ASCENDING);

      for (Milestone m : allMilestones) {
        Map<String, Object> data = new HashMap<String, Object>();

        data.put("title", m.getName());
        data.put("status", m.getStatus().toString().toLowerCase());
        if (m.isCompleted()) {
          data.put("start", CALENDAR_DATE_FORMAT.format(m.getCompletionDate()));
        } else {
          data.put("start", CALENDAR_DATE_FORMAT.format(m.getDueDate()));
        }

        data.put("description", m.getDescription());

        if (m.getOwners() != null && m.getOwners().size() > 0) {
          ResponsiblePerson rp = m.getOwners().get(0);

          if (rp != null) {
            Map<String, Object> person = new HashMap<String, Object>();
            person.put("name", rp.getName());
            person.put("color", DEFAULT_USER_HANDLE_CLASS);
            person.put("url", DEFAULT_USER_HANDLE_URL);

            data.put("person", person);
          }
        }

        result.add(data);
      }
      setResult(result);

    } catch (Throwable e) {
      // set the error message into the ajax response
      if (getModel() != null) {
        setResult(e);
      }
      return ERROR;
    }
    return SUCCESS;
  }

  /**
   * Gets the project milestone list view data with ajax.
   *
   * @return the result code.
   * @throws Exception if there is any error.
   * @since 1.1
   */
  public String getProjectMilestoneListData() throws Exception {
    try {

      List<Milestone> overdueMilestones =
          getMilestoneService()
              .getAll(
                  formData.getProjectId(),
                  Arrays.asList(new MilestoneStatus[] {MilestoneStatus.OVERDUE}),
                  SortOrder.ASCENDING);
      List<Milestone> upcomingMilestones =
          getMilestoneService()
              .getAll(
                  formData.getProjectId(),
                  Arrays.asList(new MilestoneStatus[] {MilestoneStatus.UPCOMING}),
                  SortOrder.ASCENDING);
      List<Milestone> completedMilestones =
          getMilestoneService()
              .getAll(
                  formData.getProjectId(),
                  Arrays.asList(new MilestoneStatus[] {MilestoneStatus.COMPLETED}),
                  SortOrder.DESCENDING);

      List<MilestoneContestDTO> milestoneContestAssociations =
          DataProvider.getMilestoneContestAssociations(
              formData.getProjectId(), 0, DirectUtils.getTCSubjectFromSession().getUserId());

      Map<Long, List<MilestoneContestDTO>> tempMapping =
          new HashMap<Long, List<MilestoneContestDTO>>();

      for (MilestoneContestDTO mcd : milestoneContestAssociations) {
        if (tempMapping.get(mcd.getMilestoneId()) == null) {
          tempMapping.put(mcd.getMilestoneId(), new ArrayList<MilestoneContestDTO>());
        }

        tempMapping.get(mcd.getMilestoneId()).add(mcd);
      }

      Map<String, List<ProjectMilestoneDTO>> result =
          new HashMap<String, List<ProjectMilestoneDTO>>();

      result.put("overdue", new ArrayList<ProjectMilestoneDTO>());
      result.put("upcoming", new ArrayList<ProjectMilestoneDTO>());
      result.put("completed", new ArrayList<ProjectMilestoneDTO>());

      for (Milestone m : overdueMilestones) {
        ProjectMilestoneDTO pmd = new ProjectMilestoneDTO();
        pmd.setMilestone(m);
        pmd.setContests(tempMapping.get(m.getId()));
        result.get("overdue").add(pmd);
      }

      for (Milestone m : upcomingMilestones) {
        ProjectMilestoneDTO pmd = new ProjectMilestoneDTO();
        pmd.setMilestone(m);
        pmd.setContests(tempMapping.get(m.getId()));
        result.get("upcoming").add(pmd);
      }

      for (Milestone m : completedMilestones) {
        ProjectMilestoneDTO pmd = new ProjectMilestoneDTO();
        pmd.setMilestone(m);
        pmd.setContests(tempMapping.get(m.getId()));
        result.get("completed").add(pmd);
      }

      ObjectMapper m = new ObjectMapper();

      setResult(m.convertValue(result, Map.class));

    } catch (Throwable e) {
      // set the error message into the ajax response
      if (getModel() != null) {
        setResult(e);
      }
      return ERROR;
    }
    return SUCCESS;
  }

  /**
   * Gets the form data.
   *
   * @return the form data.
   */
  public ProjectMilestoneViewForm getFormData() {
    return formData;
  }

  /**
   * Sets the form data.
   *
   * @param formData the form data to set.
   */
  public void setFormData(ProjectMilestoneViewForm formData) {
    this.formData = formData;
  }

  /**
   * Gets the view data.
   *
   * @return the view data.
   */
  public ProjectMilestoneViewDTO getViewData() {
    return viewData;
  }

  /**
   * Sets the view data.
   *
   * @param viewData the view data to set.
   */
  public void setViewData(ProjectMilestoneViewDTO viewData) {
    this.viewData = viewData;
  }
}
/**
 * The action handles the ajax requests for roadmap part of the enterprise dashboard.
 *
 * <p>Version 1.1 (Release Assembly - TC Cockpit New Enterprise Dashboard Release 1)
 *
 * <ul>
 *   <li>Add method {@link #getRoadmapCalendar()} to get roadmap calendar data via ajax.
 * </ul>
 *
 * @author GreatKevin
 * @version 1.1
 */
public class DashboardRoadmapAction extends BaseDirectStrutsAction
    implements FormAction<EnterpriseDashboardFilterForm> {

  /** The date format to parse the date in the request. */
  private static final DateFormat SOURCE_DATE_FORMAT = new SimpleDateFormat("MMM''yy");

  /** The date format to format the milestone due date and completion date in response. */
  private static final DateFormat MILESTONE_DATE_FORMAT = new SimpleDateFormat("MMMMM dd, yyyy");

  /** The status and order filter used to filter and sort the project milestones. */
  private static final Map<MilestoneStatus, SortOrder> filters =
      new HashMap<MilestoneStatus, SortOrder>();

  /**
   * List of all the available milestone status.
   *
   * @since 1.1
   */
  private static final List ALL_MILESTONE_STATUS = Arrays.asList(MilestoneStatus.values());

  /**
   * Logger for this class.
   *
   * @since 1.10.8
   */
  private static final Logger logger = Logger.getLogger(DashboardRoadmapAction.class);

  /** Static constructor */
  static {
    // create the filters to send to milestone service
    filters.put(MilestoneStatus.OVERDUE, SortOrder.ASCENDING);
    filters.put(MilestoneStatus.UPCOMING, SortOrder.ASCENDING);
    filters.put(MilestoneStatus.COMPLETED, SortOrder.DESCENDING);
  }

  /**
   * The view data of the action.
   *
   * @since 1.1
   */
  private DashboardMilestoneCalendarDTO viewData = new DashboardMilestoneCalendarDTO();

  /** The form data of the action. */
  private EnterpriseDashboardFilterForm formData = new EnterpriseDashboardFilterForm();

  /**
   * Gets the form data of the action.
   *
   * @return the form data of the action.
   */
  public EnterpriseDashboardFilterForm getFormData() {
    return this.formData;
  }

  /**
   * Sets the form data of the action.
   *
   * @param formData the form data of the action.
   */
  public void setFormData(EnterpriseDashboardFilterForm formData) {
    this.formData = formData;
  }

  /**
   * Gets view data for the roadmap calendar.
   *
   * @return the view data.
   * @since 1.1
   */
  public DashboardMilestoneCalendarDTO getViewData() {
    return viewData;
  }

  /**
   * Sets view data for the roadmap calendar
   *
   * @param viewData the view data.
   * @since 1.1
   */
  public void setViewData(DashboardMilestoneCalendarDTO viewData) {
    this.viewData = viewData;
  }

  /**
   * Empty, ajax requests are handled via action methods invocation.
   *
   * @throws Exception if there is any error.
   */
  @Override
  protected void executeAction() throws Exception {
    // do nothing
  }

  /**
   * Handles the ajax request to get the data for enterprise dashboard road map.
   *
   * @return the result code.
   */
  public String getProjectsMilestones() {
    try {
      Map<String, List<Map<String, String>>> result =
          new HashMap<String, List<Map<String, String>>>();

      final Map<Long, String> projects =
          DataProvider.getEnterpriseDashboardFilteredProjects(getFormData());

      if (projects == null || projects.size() == 0) {
        // if no projects, return empty result
        List<Map<String, String>> emptyList = new ArrayList<Map<String, String>>();
        result.put("overdue", emptyList);
        result.put("upcoming", emptyList);
        result.put("completed", emptyList);
        setResult(result);
        return SUCCESS;
      }

      if (getMilestoneService() == null) {
        throw new IllegalStateException("The project milestone service is not initialized");
      }

      Calendar calendar = Calendar.getInstance();
      // get start date
      calendar.setTime(SOURCE_DATE_FORMAT.parse(getFormData().getStartMonth()));
      Date startDate = calendar.getTime();
      // get end date
      calendar.setTime(SOURCE_DATE_FORMAT.parse(getFormData().getEndMonth()));
      calendar.add(Calendar.MONTH, 1);
      calendar.add(Calendar.SECOND, -1);
      Date endDate = calendar.getTime();

      // get the milestones with milestone service
      final Map<MilestoneStatus, List<Milestone>> allForProjectsGroupedByStatus =
          getMilestoneService()
              .getAllForProjectsGroupedByStatus(
                  new ArrayList<Long>(projects.keySet()), filters, startDate, endDate);

      // add overdue
      List<Map<String, String>> overdueList = new ArrayList<Map<String, String>>();
      if (allForProjectsGroupedByStatus.get(MilestoneStatus.OVERDUE) != null
          && allForProjectsGroupedByStatus.get(MilestoneStatus.OVERDUE).size() > 0) {
        List<Milestone> overdueMilestones =
            allForProjectsGroupedByStatus.get(MilestoneStatus.OVERDUE);

        for (Milestone m : overdueMilestones) {
          overdueList.add(getMilestoneJsonData(m, projects));
        }
      }
      result.put("overdue", overdueList);

      // add upcoming
      List<Map<String, String>> upcomingList = new ArrayList<Map<String, String>>();
      if (allForProjectsGroupedByStatus.get(MilestoneStatus.UPCOMING) != null
          && allForProjectsGroupedByStatus.get(MilestoneStatus.UPCOMING).size() > 0) {
        List<Milestone> upcomingMilestones =
            allForProjectsGroupedByStatus.get(MilestoneStatus.UPCOMING);
        for (Milestone m : upcomingMilestones) {
          upcomingList.add(getMilestoneJsonData(m, projects));
        }
      }
      result.put("upcoming", upcomingList);

      // add completed
      List<Map<String, String>> completedList = new ArrayList<Map<String, String>>();
      if (allForProjectsGroupedByStatus.get(MilestoneStatus.COMPLETED) != null
          && allForProjectsGroupedByStatus.get(MilestoneStatus.COMPLETED).size() > 0) {
        List<Milestone> completedMilestones =
            allForProjectsGroupedByStatus.get(MilestoneStatus.COMPLETED);

        for (Milestone m : completedMilestones) {
          completedList.add(getMilestoneJsonData(m, projects));
        }
      }
      result.put("completed", completedList);

      setResult(result);
    } catch (Throwable e) {

      e.printStackTrace(System.err);

      if (getModel() != null) {
        setResult(e);
      }
    }

    return SUCCESS;
  }

  /**
   * Gets the roadmap calendar json data via ajax.
   *
   * @return the result code.
   * @throws Exception if there is any error.
   * @since 1.1
   */
  public String getRoadmapCalendar() throws Exception {
    Map<String, List<Map<String, String>>> result =
        new HashMap<String, List<Map<String, String>>>();

    final Map<Long, String> projects =
        DataProvider.getEnterpriseDashboardFilteredProjects(getFormData());

    viewData.setProjects(projects);

    getViewData().setResponsiblePersonIds(new HashSet<Long>());
    final List<Milestone> milestones;

    if (projects == null || projects.size() == 0) {
      milestones = new ArrayList<Milestone>();
    } else {
      milestones =
          getMilestoneService()
              .getAllForProjects(
                  new ArrayList<Long>(projects.keySet()),
                  ALL_MILESTONE_STATUS,
                  SortOrder.ASCENDING);
    }

    // extra all the responsible person user id from the milestone
    for (Milestone m : milestones) {
      m.setName(StringEscapeUtils.escapeHtml4(StringEscapeUtils.escapeJson(m.getName())));
      m.setDescription(
          StringEscapeUtils.escapeHtml4(StringEscapeUtils.escapeJson(m.getDescription())));
      if (m.getOwners() != null && m.getOwners().size() > 0) {
        getViewData().getResponsiblePersonIds().add(m.getOwners().get(0).getUserId());
      }
    }

    // set the milestones
    viewData.setMilestones(milestones);

    return SUCCESS;
  }

  /**
   * Gets the map to represent a single milestone.
   *
   * @param m the milestone
   * @param projects the projects cache
   * @return the map representing a single milestone.
   */
  private static Map<String, String> getMilestoneJsonData(Milestone m, Map<Long, String> projects) {
    Map<String, String> item = new HashMap<String, String>();
    item.put("title", StringEscapeUtils.escapeHtml4(m.getName()));
    item.put("projectId", String.valueOf(m.getProjectId()));
    item.put("projectName", StringEscapeUtils.escapeHtml4(projects.get(m.getProjectId())));
    item.put("description", StringEscapeUtils.escapeHtml4(m.getDescription()));
    item.put(
        "date",
        MILESTONE_DATE_FORMAT.format(m.isCompleted() ? m.getCompletionDate() : m.getDueDate()));

    return item;
  }
}