/**
  * Failure test case for checkNull.The argument is null so <code>IllegalArgumentException</code>
  * should be thrown.
  */
 @Test(expected = IllegalArgumentException.class)
 public void testCheckNullWithLoggingFailure() {
   Helper.checkNullWithLogging(null, "obj", "method", LogManager.getLog());
 }
 /**
  * Accuracy test case for checkNull.The argument is not null so <code>IllegalArgumentException
  * </code> should not be thrown.
  */
 @Test
 public void testCheckNullWithLogging() {
   Helper.checkNullWithLogging("obj", "obj", "method", LogManager.getLog());
 }
 /**
  * Failure test case for checkPositive. The argument is 0 so <code>IllegalArgumentException</code>
  * should be thrown.
  */
 @Test(expected = IllegalArgumentException.class)
 public void testCheckPositiveFailure() {
   Helper.checkPositive(0, "obj", "method", LogManager.getLog());
 }
 /**
  * Accuracy test case for checkPositive. The argument is 1 so <code>IllegalArgumentException
  * </code> should not be thrown.
  */
 @Test
 public void testCheckPositive() {
   Helper.checkPositive(1, "obj", "method", LogManager.getLog());
 }
/**
 * This class contains operations to create and update group instances into the Informix database.
 * It is package level because it is used only in InformixScorecardPersistence class to persist
 * group information. Connection to the database is passed to this class during initialization.
 * Thread Safety: The implementation is not thread safe in that two threads running the same method
 * will use the same statement and could overwrite each other's work.
 *
 * <p>Changes in v1.0.2 (Cockpit Spec Review Backend Service Update v1.0): - LogManager is used
 * instead of LogFactory.
 *
 * @author tuenm, kr00tki, pulky
 * @version 1.0.2
 */
class InformixGroupPersistence {

  /** Logger instance using the class name as category */
  private static final Log logger = LogManager.getLog(InformixGroupPersistence.class.getName());

  /** Selects the groups using the parent id. */
  private static final String SELECT_SCORECARD_GROUP_BY_PARENT_ID =
      "SELECT scorecard_group_id, "
          + "name, weight FROM scorecard_group WHERE scorecard_id = ? ORDER BY sort";

  /** Selects the group be its id. */
  private static final String SELECT_SCORECARD_GROUP_BY_ID =
      "SELECT scorecard_group_id, name, "
          + "weight FROM scorecard_group WHERE scorecard_group_id = ?";

  /** Deletes the set of groups. */
  private static final String DELETE_SCORECARD_GROUPS =
      "DELETE FROM scorecard_group WHERE scorecard_group_id IN ";

  /** Selects the section ids for given set of groups. */
  private static final String SELECT_SCORECARD_SECTION_ID =
      "SELECT scorecard_section_id FROM scorecard_section " + "WHERE scorecard_group_id IN ";

  /** Updates the group table. */
  private static final String UPDATE_SCORECARD_GROUP =
      "UPDATE scorecard_group SET scorecard_id = ?, "
          + "name = ?, weight = ?, sort = ?, modify_user = ?, modify_date = ? WHERE scorecard_group_id = ?";

  /** Inserts the new group the database. */
  private static final String INSERT_SCORECARD_GROUP =
      "INSERT INTO scorecard_group (scorecard_group_id, "
          + "scorecard_id, name, weight, sort, create_user, create_date, modify_user, modify_date) "
          + "VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)";

  /**
   * The connection used to connection to the database. It is initialized in the constructor from
   * the passing parameter.
   */
  private final Connection connection;

  /**
   * Create a new instance of InformixGroupPersistence. The passing connection parameter will be
   * used to connect to the database. Null value is not allowed. Implementation notes: - Set the
   * member connection variable.
   *
   * @param connection The connection to the database.
   * @throws IllegalArgumentException if the input is null.
   */
  public InformixGroupPersistence(Connection connection) {
    if (connection == null) {
      throw new IllegalArgumentException("connection cannot be null.");
    }
    this.connection = connection;
  }

  /**
   * Creates the group in the database using the given group instance. The group instance can
   * include sub items such as sections and questions. Those sub items will be persisted as well.
   * The operator parameter is used as the creation/modification user of the group and its subitems.
   * The creation date and modification date will be the current date time when the group is
   * created.
   *
   * @param group The group instance to be created in the database.
   * @param order the sort order of this group.
   * @param operator The creation user of this group.
   * @param parentId The id of the scorecard that contains this.
   * @throws IllegalArgumentException if any input is null or the operator is empty string.
   * @throws PersistenceException if error occurred while accessing the database.
   */
  public void createGroup(Group group, int order, String operator, long parentId)
      throws PersistenceException {
    if (group == null) {
      throw new IllegalArgumentException("group cannot be null.");
    }
    if (operator == null) {
      throw new IllegalArgumentException("operator cannot be null.");
    }

    if (operator.trim().length() == 0) {
      throw new IllegalArgumentException("operator cannot be empty String.");
    }

    logger.log(
        Level.INFO,
        new LogMessage(
            "Group",
            null,
            operator,
            "create new Group with order:" + order + " and parentId:" + parentId));
    // get next id
    long groupId = DBUtils.nextId(IdGeneratorUtility.getGroupIdGenerator());
    Timestamp time = new Timestamp(System.currentTimeMillis());
    // create section persistence
    InformixSectionPersistence sectionPersistence = new InformixSectionPersistence(connection);
    PreparedStatement pstmt = null;

    logger.log(Level.INFO, "insert record into scorecard_group with group_id:" + groupId);
    try {
      // create statement
      pstmt = connection.prepareStatement(INSERT_SCORECARD_GROUP);

      // set the variables
      pstmt.setLong(1, groupId);
      pstmt.setLong(2, parentId);
      pstmt.setString(3, group.getName());
      pstmt.setFloat(4, group.getWeight());
      pstmt.setInt(5, order);

      pstmt.setString(6, operator);
      pstmt.setTimestamp(7, time);
      pstmt.setString(8, operator);
      pstmt.setTimestamp(9, time);

      // create group and create the sections
      pstmt.executeUpdate();
      sectionPersistence.createSections(group.getAllSections(), operator, groupId);
    } catch (SQLException ex) {
      logger.log(
          Level.ERROR,
          new LogMessage("Group", new Long(groupId), operator, "Fail to create Group.", ex));
      throw new PersistenceException("Error occur while creating the scorecard group.", ex);
    } finally {
      DBUtils.close(pstmt);
    }

    // set the group id.
    group.setId(groupId);
  }

  /**
   * This method creates the groups instances in the datastore. It has the same behavior as {@link
   * #createGroup(Group, int, String, long)} except it executes insert at once.
   *
   * @param groups the groups to be created.
   * @param operator the creation operator.
   * @param parentId the id of the parent scorecard.
   * @throws PersistenceException if error occurred while accessing the database.
   */
  void createGroups(Group[] groups, String operator, long parentId) throws PersistenceException {
    logger.log(
        Level.INFO,
        new LogMessage("Group", null, operator, "create new Groups with parentId:" + parentId));

    // generate the ids
    long[] groupIds =
        DBUtils.generateIdsArray(groups.length, IdGeneratorUtility.getGroupIdGenerator());
    Timestamp time = new Timestamp(System.currentTimeMillis());
    // create sections persistence
    InformixSectionPersistence sectionPersistence = new InformixSectionPersistence(connection);
    PreparedStatement pstmt = null;

    try {
      pstmt = connection.prepareStatement(INSERT_SCORECARD_GROUP);
      // set the id of the parent
      pstmt.setLong(2, parentId);

      // for each group - set the variables
      for (int i = 0; i < groups.length; i++) {
        pstmt.setLong(1, groupIds[i]);

        pstmt.setString(3, groups[i].getName());
        pstmt.setFloat(4, groups[i].getWeight());
        pstmt.setInt(5, i);

        pstmt.setString(6, operator);
        pstmt.setTimestamp(7, time);
        pstmt.setString(8, operator);
        pstmt.setTimestamp(9, time);

        // execute the update and creates sections of this group
        pstmt.executeUpdate();

        logger.log(
            Level.INFO, "insert record into scorecard_group table with groupId:" + groupIds[i]);
        sectionPersistence.createSections(groups[i].getAllSections(), operator, groupIds[i]);
      }
    } catch (SQLException ex) {
      logger.log(
          Level.INFO,
          new LogMessage(
              "Group",
              null,
              operator,
              "Failed to create new Groups with parentId:" + parentId,
              ex));
      throw new PersistenceException("Error occur while creating the scorecard group.", ex);
    } finally {
      DBUtils.close(pstmt);
    }

    // set ids to groups
    for (int i = 0; i < groups.length; i++) {
      groups[i].setId(groupIds[i]);
    }
  }

  /**
   * Update the given group instance into the database. The group instance can include sub items
   * such as sections and questions. Those sub items will be updated as well. If sub items are
   * removed from the group, they will be deleted from the persistence. Likewise, if new sub items
   * are added, they will be created in the persistence. The operator parameter is used as the
   * modification user of the group and its subitems. The modification date will be the current date
   * time when the group is updated.
   *
   * @param group The group instance to be updated into the database.
   * @param order the position of the group.
   * @param operator The modification user of this group.
   * @param parentId The id of the scorecard that contains this.
   * @param oldScorecard The scorecard instance before update. It is used to find out remeved items.
   * @param deletedSectionIds This is an output parameter. An empty array is expected to be passed
   *     in. Deleted section ids will be saved into this list.
   * @param deletedQuestionIds This is an output parameter. An empty array is expected to be passed
   *     in. Deleted question ids will be saved into this list. Delete question ids is collected
   *     from updateSection() call.
   * @throws IllegalArgumentException if any input is null or the operator is empty string.
   * @throws PersistenceException if error occurred while accessing the database.
   */
  public void updateGroup(
      Group group,
      int order,
      String operator,
      long parentId,
      Scorecard oldScorecard,
      List deletedSectionIds,
      List deletedQuestionIds)
      throws PersistenceException {
    if (group == null) {
      throw new IllegalArgumentException("group cannot be null.");
    }

    if (operator == null) {
      throw new IllegalArgumentException("operator cannot be null.");
    }

    if (operator.trim().length() == 0) {
      throw new IllegalArgumentException("operator cannot be empty String.");
    }

    if (oldScorecard == null) {
      throw new IllegalArgumentException("oldScorecard cannot be null.");
    }

    if (deletedSectionIds == null) {
      throw new IllegalArgumentException("deletedSectionIds cannot be null.");
    }

    if (deletedQuestionIds == null) {
      throw new IllegalArgumentException("deletedQuestionIds cannot be null.");
    }

    logger.log(
        Level.INFO,
        new LogMessage(
            "Group",
            new Long(group.getId()),
            operator,
            "create new Group with order:"
                + order
                + " ,parentId:"
                + parentId
                + ", oldScorecard:"
                + oldScorecard.getId()));

    Set oldSectionIds = getSectionsIds(group, oldScorecard);
    // mark all old section as 'to delete'
    deletedSectionIds.addAll(oldSectionIds);

    // get the section and create its persistence
    Section[] sections = group.getAllSections();
    InformixSectionPersistence sectionPersistence = new InformixSectionPersistence(connection);

    // for each new section
    for (int i = 0; i < sections.length; i++) {
      Long longId = new Long(sections[i].getId());
      // if is new - create it
      if (sections[i].getId() == NamedScorecardStructure.SENTINEL_ID) {
        sectionPersistence.createSection(sections[i], i, operator, group.getId());
      } else if (oldSectionIds.contains(longId)) {
        // if is old - update it and removed from delete list
        sectionPersistence.updateSection(
            sections[i], i, operator, group.getId(), oldScorecard, deletedQuestionIds);
        deletedSectionIds.remove(longId);
      }
    }
    // update the group in the database
    updateGroup(connection, group, operator, parentId, order);
  }

  /**
   * Updates the group (the scorecard_group table) in the database.
   *
   * @param conn the database connection to be used.
   * @param group the groupt to update.
   * @param operator the update operator name.
   * @param parentId the parent of this group (scorecard id).
   * @param order the order of this group in the database.
   * @throws PersistenceException if any database error occurs.
   */
  private static void updateGroup(
      Connection conn, Group group, String operator, long parentId, int order)
      throws PersistenceException {

    logger.log(Level.INFO, "update scorecard_group with groupId:" + group.getId());
    PreparedStatement pstmt = null;
    try {
      // prepare the statement
      pstmt = conn.prepareStatement(UPDATE_SCORECARD_GROUP);

      // set the variables
      pstmt.setLong(1, parentId);
      pstmt.setString(2, group.getName());
      pstmt.setFloat(3, group.getWeight());
      pstmt.setInt(4, order);

      // set the modification user and time
      Timestamp time = new Timestamp(System.currentTimeMillis());
      pstmt.setString(5, operator);
      pstmt.setTimestamp(6, time);
      pstmt.setLong(7, group.getId());

      if (pstmt.executeUpdate() != 1) {
        logger.log(Level.ERROR, "No group with id = " + group.getId());
        throw new PersistenceException("No group with id = " + group.getId());
      }
    } catch (SQLException ex) {
      logger.log(
          Level.ERROR,
          new LogMessage(
              "Group",
              new Long(group.getId()),
              operator,
              "Error occurs while updating the group.",
              ex));
      throw new PersistenceException("Error occurs while updating the group.", ex);
    } finally {
      DBUtils.close(pstmt);
    }
  }

  /**
   * Gets the ids of all sections for group from he scorecard.
   *
   * @param group the group for which the sections ids will be collected.
   * @param scorecard the source scorecard.
   * @return the set of sections ids for group.
   */
  private static Set getSectionsIds(Group group, Scorecard scorecard) {
    Set ids = new HashSet();
    // get all groups
    Group[] oldGroups = scorecard.getAllGroups();
    for (int i = 0; i < oldGroups.length; i++) {
      // find the one of given id
      if (oldGroups[i].getId() == group.getId()) {
        // get all sections and add the ids to set
        Section[] sections = oldGroups[i].getAllSections();
        for (int j = 0; j < sections.length; j++) {
          ids.add(new Long(sections[j].getId()));
        }
        break;
      }
    }

    return ids;
  }

  /**
   * Remove the groups with the given array of ids from the persistence. Group deletion is required
   * when user udpate a scorecard and remove a group from its group list.
   *
   * @param ids The id of the group to remove.
   * @throws IllegalArgumentException if the ids is less than or equal to zero. Or the input array
   *     is null or empty.
   * @throws PersistenceException if error occurred while accessing the database.
   */
  public void deleteGroups(long[] ids) throws PersistenceException {
    DBUtils.checkIdsArray(ids, "ids");

    logger.log(
        Level.INFO,
        new LogMessage(
            "Group",
            null,
            null,
            "Delete Groups with ids:" + InformixPersistenceHelper.generateIdString(ids)));

    PreparedStatement pstmt = null;
    PreparedStatement pstmt2 = null;
    ResultSet rs = null;
    try {
      // selects the sections ids for given groups
      pstmt =
          connection.prepareStatement(
              SELECT_SCORECARD_SECTION_ID + DBUtils.createQuestionMarks(ids.length));
      logger.log(
          Level.INFO,
          "delete record from scorecard_groups with ids:"
              + InformixPersistenceHelper.generateIdString(ids));
      // deletes the given groups
      pstmt2 =
          connection.prepareStatement(
              DELETE_SCORECARD_GROUPS + DBUtils.createQuestionMarks(ids.length));

      // set the ids for both statements
      for (int i = 0; i < ids.length; i++) {
        pstmt.setLong(i + 1, ids[i]);
        pstmt2.setLong(i + 1, ids[i]);
      }

      // get all the sections to be removed
      rs = pstmt.executeQuery();
      List idsToDelete = new ArrayList();
      while (rs.next()) {
        idsToDelete.add(new Long(rs.getLong(1)));
      }

      // delete the sections
      if (idsToDelete.size() > 0) {
        InformixSectionPersistence sectionPersistence = new InformixSectionPersistence(connection);
        sectionPersistence.deleteSections(DBUtils.listToArray(idsToDelete));
      }

      // delete the groups
      pstmt2.executeUpdate();
    } catch (SQLException ex) {
      logger.log(
          Level.ERROR,
          new LogMessage(
              "Group",
              null,
              null,
              "Failed to Delete Groups with ids:" + InformixPersistenceHelper.generateIdString(ids),
              ex));
      throw new PersistenceException("Error occurs while deleting the questions.", ex);
    } finally {
      DBUtils.close(rs);
      DBUtils.close(pstmt);
      DBUtils.close(pstmt2);
    }
  }

  /**
   * Retrieves the group instance from the persistence given its id. The group instance is always
   * retrieved with its sub items.
   *
   * @return The group instance or <code>null</code> if group not found.
   * @param id The id of the group to be retrieved.
   * @throws IllegalArgumentException if the input id is less than or equal to zero.
   * @throws PersistenceException if error occurred while accessing the database.
   */
  public Group getGroup(long id) throws PersistenceException {
    if (id <= 0) {
      throw new IllegalArgumentException("The id must be positive. Id: " + id);
    }
    logger.log(Level.INFO, new LogMessage("Group", new Long(id), null, "retrieve group"));

    PreparedStatement pstmt = null;
    ResultSet rs = null;

    try {
      // create the statement and set the id
      pstmt = connection.prepareStatement(SELECT_SCORECARD_GROUP_BY_ID);
      pstmt.setLong(1, id);
      rs = pstmt.executeQuery();
      // if the group exists - create it
      if (rs.next()) {
        InformixSectionPersistence sectionPersistence = new InformixSectionPersistence(connection);
        Group group = populateGroup(rs);
        group.addSections(sectionPersistence.getSections(group.getId()));
        return group;
      }
    } catch (SQLException ex) {
      logger.log(
          Level.ERROR, new LogMessage("Group", new Long(id), null, "Failed to retrieve group", ex));
      throw new PersistenceException("Error occurs while retrieving the group.", ex);
    } finally {
      DBUtils.close(rs);
      DBUtils.close(pstmt);
    }
    // return null if group not found.
    return null;
  }

  /**
   * Retrieves the group instances from the persistence with the given parent id. The group instance
   * is always retrieved with its sub items.
   *
   * @param parentId the id of the paren scorecard.
   * @return the list of groups for the given parent.
   * @throws PersistenceException if database error occur.
   */
  Group[] getGroups(long parentId) throws PersistenceException {
    PreparedStatement pstmt = null;
    ResultSet rs = null;

    try {
      // create the statement
      pstmt = connection.prepareStatement(SELECT_SCORECARD_GROUP_BY_PARENT_ID);
      pstmt.setLong(1, parentId);

      // get all groups
      rs = pstmt.executeQuery();
      List result = new ArrayList();
      InformixSectionPersistence sectionPersistence = new InformixSectionPersistence(connection);
      while (rs.next()) {
        Group group = populateGroup(rs);
        // get the sections for the group
        group.addSections(sectionPersistence.getSections(group.getId()));
        result.add(group);
      }

      return (Group[]) result.toArray(new Group[result.size()]);
    } catch (SQLException ex) {
      logger.log(
          Level.ERROR,
          new LogMessage(
              "Group", null, null, "Failed to retrieve group with parentId:" + parentId, ex));
      throw new PersistenceException("Error occurs while retrieving the group.", ex);
    } finally {
      DBUtils.close(rs);
      DBUtils.close(pstmt);
    }
  }

  /**
   * Creates the Group instance using the data from the ResultSet.
   *
   * @param rs the source result set.
   * @return the Group instance.
   * @throws SQLException if any error with ResultSet occurs.
   */
  private Group populateGroup(ResultSet rs) throws SQLException {
    Group group = new Group();
    group.setId(rs.getLong("scorecard_group_id"));
    group.setName(rs.getString("name"));
    group.setWeight(rs.getFloat("weight"));

    return group;
  }
}
/**
 * This action will be used to handle the request for view marathon match result. This action should
 * not be called when the marathon match contest is still active.
 *
 * <p><strong>Thread Safety: </strong> This class is only used in thread-safe manner by Struts2
 * framework.
 *
 * <p>Version 1.1 - Release Assembly - TopCoder Cockpit - Tracking Marathon Matches Progress -
 * Results Tab 2
 *
 * <ol>
 *   <li>Remove property roundId instead use viewData.roundId.
 *   <li>Add property {@link #type}, {@link #sr} and {@link #stc}.
 *   <li>Add static property {@link #RESULT_DETAIL} and {@link #ALLOWABLE_SORT_ORDER}.
 * </ol>
 *
 * <p>Version 1.2 - BUGR -9794
 *
 * <ol>
 *   <li>Remove ALLOWABLE_SORT_ORDER
 *   <li>Update method {@link #convertResults(SearchResult)} to remove the api call to
 *       competitorSubmissionHistory method. So the performance will be improved.
 * </ol>
 *
 * @author Ghost_141
 * @version 1.1
 * @since 1.0 (Release Assembly - TopCoder Cockpit - Tracking Marathon Matches Progress - Results
 *     Tab)
 */
public class MarathonMatchResultsViewAction extends AbstractAction
    implements ViewAction<MMResultsInfoDTO>, SessionAware {

  /** This field represents the qualified name of this class. */
  private static final String CLASS_NAME = MarathonMatchResultsViewAction.class.getName();

  /**
   * The static field to indicate the result detail page.
   *
   * @since 1.1
   */
  private static final String RESULT_DETAIL = "system";

  /** Log instance. */
  private Log logger = LogManager.getLog(CLASS_NAME);

  /** Represent the session map. */
  private Map<String, Object> session;

  /** The view data dto. */
  private MMResultsInfoDTO viewData;

  /** Represent the project id. */
  private long projectId;

  /**
   * Represent the results page type.
   *
   * @since 1.1
   */
  private String type;

  /**
   * Represent the coder start number used in request parameter. Default value is 1(Start with first
   * coder).
   *
   * @since 1.1
   */
  private Integer sr = 1;

  /**
   * Represent the test case start number used in request parameter. Default value is 1(Start with
   * first test case).
   *
   * @since 1.1
   */
  private Integer stc = 1;

  /** The Marathon match analytics service. */
  private MarathonMatchAnalyticsService marathonMatchAnalyticsService;

  /** Represent the contest service facade instance. */
  private ContestServiceFacade contestServiceFacade;

  /** Represent the contest. */
  private SoftwareCompetition softwareCompetition;

  /** The Timeline interval. */
  private int timelineInterval;

  /** Represent the contest has round id or not. */
  private boolean hasRoundId = false;

  /** Create an instance of <code>MarathonMatchResultsViewAction</code>. */
  public MarathonMatchResultsViewAction() {
    // Empty
  }

  /**
   * This method is responsible for retrieving MM, list of RegistrantInfo and list of SubmissionInfo
   * given round id, pageSize, pageNumber, sortingOrder, sortingField
   *
   * @return a <code>String</code> that represent the state of execute result.
   * @throws Exception if any error occurred.
   */
  public String execute() throws Exception {
    final String signature = CLASS_NAME + "#execute()";
    try {
      if (projectId <= 0) {
        throw new DirectException("project less than 0 or not defined.");
      }

      TCSubject currentUser = DirectUtils.getTCSubjectFromSession();
      softwareCompetition =
          contestServiceFacade.getSoftwareContestByProjectId(
              DirectStrutsActionsHelper.getTCSubjectFromSession(), projectId);

      // Get the round id from the project info.
      String roundIdStr = softwareCompetition.getProjectHeader().getProperty("Marathon Match Id");

      hasRoundId = !(roundIdStr == null);

      viewData = new MMResultsInfoDTO();

      if (hasRoundId) {
        viewData.setRoundId(Long.valueOf(roundIdStr));
      }

      // If the contest don't have the round id or the contest is a active contest then throw an
      // exception.
      if (!hasRoundId
          || MarathonMatchHelper.isMarathonMatchActive(
              viewData.getRoundId(), marathonMatchAnalyticsService)) {
        throw new Exception("The contest is either don't have round id or is an active contest");
      }

      MarathonMatchHelper.getMarathonMatchDetails(
          viewData.getRoundId().toString(),
          marathonMatchAnalyticsService,
          timelineInterval,
          viewData);

      // Get the common data for contest page.
      MarathonMatchHelper.getCommonData(
          projectId,
          currentUser,
          softwareCompetition,
          viewData,
          contestServiceFacade,
          getSessionData());

      if (type == null) {
        // results page.
        viewResults();
      } else if (type.equals(RESULT_DETAIL)) {
        // result detail page.
        viewSystemTestResults();
      }

      return SUCCESS;
    } catch (Exception e) {
      LoggingWrapperUtility.logException(logger, signature, e);
      throw new Exception("Error when executing action : " + getAction() + " : " + e.getMessage());
    }
  }

  /**
   * Handle the view system test results request.
   *
   * @throws MarathonMatchAnalyticsServiceException if any error occurred.
   * @since 1.1
   */
  private void viewSystemTestResults() throws Exception {
    SystemTestResourceWrapper systemTestResourceWrapper =
        marathonMatchAnalyticsService.getSystemTests(
            viewData.getRoundId(),
            null,
            sr,
            stc,
            "score",
            "desc",
            MarathonMatchHelper.ACCESS_TOKEN);
    if (systemTestResourceWrapper.getItems() != null
        && systemTestResourceWrapper.getItems().size() != 0) {
      viewData.setCompetitorsTestCases(new ArrayList<TestCaseSubmissionInfo>());
      for (SystemTestResource systemTestResource : systemTestResourceWrapper.getItems()) {
        TestCaseSubmissionInfo competitorTestCases = new TestCaseSubmissionInfo();
        competitorTestCases.setTestCases(new ArrayList<TestCaseInfo>());

        for (TestCaseResource testCase : systemTestResource.getTestCases()) {
          TestCaseInfo testCaseInfo = new TestCaseInfo();
          testCaseInfo.setTestCaseId(testCase.getTestCaseId());
          testCaseInfo.setTestCaseScore(testCase.getScore());
          competitorTestCases.getTestCases().add(testCaseInfo);
        }
        competitorTestCases.setHandle(systemTestResource.getHandleName());
        competitorTestCases.setUserId(Long.valueOf(systemTestResource.getHandleId()));
        competitorTestCases.setFinalScore(systemTestResource.getScore());
        viewData.getCompetitorsTestCases().add(competitorTestCases);
      }
      viewData.setTestCasesStartNumber(systemTestResourceWrapper.getTestCasesStartNumber());
      // TODO: Due to the API bug, this test statement have to be added.
      viewData.setTestCasesEndNumber(
          systemTestResourceWrapper.getTestCasesEndNumber()
                      - systemTestResourceWrapper.getTestCasesStartNumber()
                  == 9
              ? systemTestResourceWrapper.getTestCasesEndNumber() - 1
              : systemTestResourceWrapper.getTestCasesEndNumber());
      viewData.setTestCasesCount(systemTestResourceWrapper.getTestCasesCount());
      viewData.setCodersStartNumber(systemTestResourceWrapper.getCodersStartNumber());
      // TODO: Due to the API bug, this test statement have to be added.
      viewData.setCodersEndNumber(
          systemTestResourceWrapper.getCodersEndNumber()
                      - systemTestResourceWrapper.getCodersStartNumber()
                  == 39
              ? systemTestResourceWrapper.getCodersEndNumber() - 1
              : systemTestResourceWrapper.getCodersEndNumber());
      viewData.setCodersCount(systemTestResourceWrapper.getCodersCount());
    } else {
      ServletActionContext.getRequest()
          .setAttribute("errorPageMessage", systemTestResourceWrapper.getMessage());
      throw new Exception("The system test cases result is empty");
    }
  }

  /**
   * This method will handle the request to results page.
   *
   * @throws MarathonMatchAnalyticsServiceException if any error occurred.
   */
  private void viewResults() throws MarathonMatchAnalyticsServiceException {
    SearchResult<MatchResultResource> matchResults =
        marathonMatchAnalyticsService.getMatchResults(
            viewData.getRoundId(),
            Integer.MAX_VALUE,
            1,
            "desc",
            "finalScore",
            MarathonMatchHelper.ACCESS_TOKEN);

    List<ResultInfo> resultInfos = convertResults(matchResults);

    // Sort the results info by provisional score and set the provisional rank.
    Collections.sort(
        resultInfos,
        new Comparator<ResultInfo>() {
          public int compare(ResultInfo resultInfo, ResultInfo resultInfo2) {
            if (resultInfo.getProvisionalScore() != null
                && resultInfo2.getProvisionalScore() != null) {
              if (resultInfo.getProvisionalScore() < resultInfo2.getProvisionalScore()) {
                return 1;
              }
              if (resultInfo.getProvisionalScore().equals(resultInfo2.getProvisionalScore())) {
                return String.CASE_INSENSITIVE_ORDER.compare(
                    resultInfo.getHandle(), resultInfo2.getHandle());
              }
            }
            return 0;
          }
        });
    for (ResultInfo r : resultInfos) {
      r.setProvisionalRank(resultInfos.indexOf(r) + 1);
    }
    viewData.setResults(resultInfos);

    // calculate the results graph data.
    calculateResultsGraphData(resultInfos);
  }

  /**
   * Calculate the results graph data. The graph includes final rank graph and final vs provisional
   * score graph.
   *
   * @param resultInfos the results
   */
  private void calculateResultsGraphData(List<ResultInfo> resultInfos) {
    // Sort the results info by final score.
    Collections.sort(
        resultInfos,
        new Comparator<ResultInfo>() {
          public int compare(ResultInfo resultInfo, ResultInfo resultInfo2) {
            if (resultInfo.getFinalRank() < resultInfo2.getFinalRank()) {
              return 0;
            }
            if (resultInfo.getFinalRank() == resultInfo2.getFinalRank()) {
              return String.CASE_INSENSITIVE_ORDER.compare(
                  resultInfo.getHandle(), resultInfo2.getHandle());
            }
            return 1;
          }
        });
    ObjectMapper objectMapper = new ObjectMapper();
    ObjectNode finalScoreRankingData = objectMapper.createObjectNode();
    ObjectNode finalVsProvisionalScoreData = objectMapper.createObjectNode();
    ArrayNode rating = objectMapper.createArrayNode();
    ArrayNode score = objectMapper.createArrayNode();
    for (ResultInfo result : resultInfos) {
      ObjectNode finalScoreNode = objectMapper.createObjectNode();
      finalScoreNode.put("handle", result.getHandle());
      finalScoreNode.put("number", result.getFinalScore());
      finalScoreNode.put("rank", result.getFinalRank());
      rating.add(finalScoreNode);

      ObjectNode finalProvisionalScoreNode = objectMapper.createObjectNode();
      finalProvisionalScoreNode.put("handle", result.getHandle());
      finalProvisionalScoreNode.put("finalScore", result.getFinalScore());
      finalProvisionalScoreNode.put("provisionalScore", result.getProvisionalScore());
      score.add(finalProvisionalScoreNode);
    }
    finalScoreRankingData.put("rating", rating);
    finalVsProvisionalScoreData.put("score", score);

    viewData.setFinalScoreRankingData(finalScoreRankingData.toString());
    viewData.setFinalVsProvisionalScoreData(finalVsProvisionalScoreData.toString());
  }

  /**
   * This method will convert the MatchResultResource instance to ResultInfo instance and get the
   * missing field by calling service method.
   *
   * @param matchResults the results.
   * @return the result infos.
   */
  private List<ResultInfo> convertResults(SearchResult<MatchResultResource> matchResults)
      throws MarathonMatchAnalyticsServiceException {
    List<ResultInfo> resultInfos = new ArrayList<ResultInfo>();
    int place = 1;
    for (MatchResultResource matchResultResource : matchResults.getItems()) {
      ResultInfo resultInfo = new ResultInfo();
      resultInfo.setPlace(place++);
      resultInfo.setHandle(matchResultResource.getHandleName());
      resultInfo.setUserId(matchResultResource.getCoderId());
      resultInfo.setLanguage(matchResultResource.getLanguage());
      resultInfo.setFinalScore(matchResultResource.getFinalScore());
      resultInfo.setFinalRank(matchResultResource.getRank());
      resultInfo.setProvisionalScore(matchResultResource.getProvisionalScore());
      resultInfos.add(resultInfo);
    }
    return resultInfos;
  }

  /**
   * Setter of session.
   *
   * @param sessionData the session data.
   */
  public void setSession(Map<String, Object> sessionData) {
    this.session = sessionData;
  }

  /**
   * Getter of view data.
   *
   * @return the view data instance.
   */
  public MMResultsInfoDTO getViewData() {
    return viewData;
  }

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

  /**
   * Gets project id.
   *
   * @return the project id
   */
  public long getProjectId() {
    return projectId;
  }

  /**
   * Sets project id.
   *
   * @param projectId the project id
   */
  public void setProjectId(long projectId) {
    this.projectId = projectId;
  }

  /**
   * Gets marathon match analytics service.
   *
   * @return the marathon match analytics service
   */
  public MarathonMatchAnalyticsService getMarathonMatchAnalyticsService() {
    return marathonMatchAnalyticsService;
  }

  /**
   * Sets marathon match analytics service.
   *
   * @param marathonMatchAnalyticsService the marathon match analytics service
   */
  public void setMarathonMatchAnalyticsService(
      MarathonMatchAnalyticsService marathonMatchAnalyticsService) {
    this.marathonMatchAnalyticsService = marathonMatchAnalyticsService;
  }

  /**
   * Gets contest service facade.
   *
   * @return the contest service facade
   */
  public ContestServiceFacade getContestServiceFacade() {
    return contestServiceFacade;
  }

  /**
   * Sets contest service facade.
   *
   * @param contestServiceFacade the contest service facade
   */
  public void setContestServiceFacade(ContestServiceFacade contestServiceFacade) {
    this.contestServiceFacade = contestServiceFacade;
  }

  /**
   * Gets software competition.
   *
   * @return the software competition
   */
  public SoftwareCompetition getSoftwareCompetition() {
    return softwareCompetition;
  }

  /**
   * Sets software competition.
   *
   * @param softwareCompetition the software competition
   */
  public void setSoftwareCompetition(SoftwareCompetition softwareCompetition) {
    this.softwareCompetition = softwareCompetition;
  }

  /**
   * Is has round id.
   *
   * @return the boolean
   */
  public boolean isHasRoundId() {
    return hasRoundId;
  }

  /**
   * Sets has round id.
   *
   * @param hasRoundId the has round id
   */
  public void setHasRoundId(boolean hasRoundId) {
    this.hasRoundId = hasRoundId;
  }

  /**
   * Gets timeline interval.
   *
   * @return the timeline interval
   */
  public int getTimelineInterval() {
    return timelineInterval;
  }

  /**
   * Sets timeline interval.
   *
   * @param timelineInterval the timeline interval
   */
  public void setTimelineInterval(int timelineInterval) {
    this.timelineInterval = timelineInterval;
  }

  /**
   * Determines if the contest is marathon contest or not. For this class it should be always true
   * otherwise there is error occurred.
   *
   * @return true if it's marathon contest, false otherwise.
   */
  public boolean isMarathon() {
    return DirectUtils.isMM(softwareCompetition);
  }

  /**
   * Gets type.
   *
   * @return the type
   * @since 1.1
   */
  public String getType() {
    return type;
  }

  /**
   * Sets type.
   *
   * @param type the type
   * @since 1.1
   */
  public void setType(String type) {
    this.type = type;
  }

  /**
   * Gets sr.
   *
   * @return the sr
   * @since 1.1
   */
  public Integer getSr() {
    return sr;
  }

  /**
   * Sets sr.
   *
   * @param sr the sr
   * @since 1.1
   */
  public void setSr(Integer sr) {
    this.sr = sr;
  }

  /**
   * Gets stc.
   *
   * @return the stc
   * @since 1.1
   */
  public Integer getStc() {
    return stc;
  }

  /**
   * Sets stc.
   *
   * @param stc the stc
   * @since 1.1
   */
  public void setStc(Integer stc) {
    this.stc = stc;
  }
}