/**
   * 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);
    }
  }
  /**
   * 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;
  }
  /**
   * 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;
  }
  /**
   * 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);
  }