/**
   * When a post was changed on both the server and the client /after/ synchronization, this method
   * resolved the corresponding conflict.
   *
   * @param clientPost
   * @param serverPost
   * @param conflictResolutionStrategy
   * @param direction
   */
  private void resolveConflict(
      final SynchronizationPost clientPost,
      final SynchronizationPost serverPost,
      final ConflictResolutionStrategy conflictResolutionStrategy,
      final SynchronizationDirection direction) {
    switch (conflictResolutionStrategy) {
      case CLIENT_WINS:
        if (!SynchronizationDirection.SERVER_TO_CLIENT.equals(direction))
          clientPost.setAction(SynchronizationAction.UPDATE_SERVER);
        else clientPost.setAction(SynchronizationAction.OK);
        break;
      case SERVER_WINS:
        if (!SynchronizationDirection.CLIENT_TO_SERVER.equals(direction))
          clientPost.setAction(SynchronizationAction.UPDATE_CLIENT);
        else clientPost.setAction(SynchronizationAction.OK);
        break;
        //		case ASK_USER: temporary disabled
        //			clientPost.setAction(SynchronizationAction.ASK);
        //			break;
      case FIRST_WINS:
        if (clientPost.getChangeDate().before(serverPost.getChangeDate())) {
          if (!SynchronizationDirection.SERVER_TO_CLIENT.equals(direction))
            clientPost.setAction(SynchronizationAction.UPDATE_SERVER);
          else clientPost.setAction(SynchronizationAction.OK);
        } else {
          if (!SynchronizationDirection.CLIENT_TO_SERVER.equals(direction))
            clientPost.setAction(SynchronizationAction.UPDATE_CLIENT);
          else clientPost.setAction(SynchronizationAction.OK);
        }
        break;
      case LAST_WINS:
        if (clientPost.getChangeDate().after(serverPost.getChangeDate())) {
          if (!SynchronizationDirection.SERVER_TO_CLIENT.equals(direction))
            clientPost.setAction(SynchronizationAction.UPDATE_SERVER);
          else clientPost.setAction(SynchronizationAction.OK);

        } else {
          if (!SynchronizationDirection.CLIENT_TO_SERVER.equals(direction))
            clientPost.setAction(SynchronizationAction.UPDATE_CLIENT);
          else clientPost.setAction(SynchronizationAction.OK);
        }
        break;
      default:
        clientPost.setAction(SynchronizationAction.UNDEFINED);
        break;
    }
  }
  /**
   * Computes the synchronization plan.
   *
   * @param serverPosts - Note: this map is modified by this method - posts are removed.
   * @param clientPosts - Note: this list is modified by this method - posts are added. It's the
   *     same list that is returned by this method.
   * @param lastSyncDate
   * @param conflictResolutionStrategy
   * @return The clientPosts with {@link SynchronizationAction}'s and posts from the server added.
   */
  public List<SynchronizationPost> getSyncPlan(
      final Map<String, SynchronizationPost> serverPosts,
      final List<SynchronizationPost> clientPosts,
      final Date lastSyncDate,
      final ConflictResolutionStrategy conflictResolutionStrategy,
      final SynchronizationDirection direction) {

    // is there something to synchronize?
    if (!present(serverPosts) && !present(clientPosts)) {
      throw new IllegalArgumentException("both serverPosts and clientPosts must be given");
    }

    if (!present(lastSyncDate)) {
      throw new IllegalArgumentException("lastSyncDate not present");
    }

    /*
     * check all client posts
     */
    for (final SynchronizationPost clientPost : clientPosts) {
      final SynchronizationPost serverPost = serverPosts.get(clientPost.getIntraHash());

      if (!present(serverPost)) {
        /*
         * no such post on server
         */
        if (clientPost.getCreateDate().before(lastSyncDate)) {
          /*
           * post was created before last sync, but when was it changed?
           */
          if (clientPost.getChangeDate().before(lastSyncDate)) {
            /*
             * client post was created and last changed before last synchronization
             * -> post was deleted on server
             */
            if (!SynchronizationDirection.CLIENT_TO_SERVER.equals(direction))
              clientPost.setAction(SynchronizationAction.DELETE_CLIENT);
            else clientPost.setAction(SynchronizationAction.OK);
          } else {
            /*
             * CONFLICT! (we can't solve, currently :-(
             *
             * Post was changed after last sync but does not exist on server
             * --> either it was deleted on server, or it's hash has changed
             * Since it is neither simple to find out if the post has been deleted
             * or its hash has changed, we create the post on the server.
             * FIXME: This can result in
             * a) a duplicate post (if the hash has changed on the client but the
             * post still exists on the server), or
             * b) an unwanted post (if the post has been deleted on the server, but
             * according to the strategy this deletion should be carried out on
             * the client, too).
             */
            if (!SynchronizationDirection.SERVER_TO_CLIENT.equals(direction))
              clientPost.setAction(SynchronizationAction.CREATE_SERVER);
            else clientPost.setAction(SynchronizationAction.OK);
          }
        } else {
          /*
           * post was created on client after last sync
           */
          if (!SynchronizationDirection.SERVER_TO_CLIENT.equals(direction))
            clientPost.setAction(SynchronizationAction.CREATE_SERVER);
          else clientPost.setAction(SynchronizationAction.OK);
        }
        continue;
      }

      if (!present(serverPost.getChangeDate())) {
        log.error("post on server has no changedate");
        // FIXME what is to do in this case?
      }

      if (serverPost.getChangeDate().after(lastSyncDate)) {
        /*
         * changed on server since last sync
         */
        if (clientPost.getChangeDate().after(lastSyncDate)) {

          if (clientPost.getChangeDate().equals(serverPost.getChangeDate())) {
            /*
             * both have the same change date -> do nothing
             */
            clientPost.setAction(SynchronizationAction.OK);
          } else {
            /*
             * changed on client, too -> conflict!
             */
            resolveConflict(clientPost, serverPost, conflictResolutionStrategy, direction);
          }
        } else {
          /*
           * must be updated on client
           */
          if (!SynchronizationDirection.CLIENT_TO_SERVER.equals(direction))
            clientPost.setAction(SynchronizationAction.UPDATE_CLIENT);
          else clientPost.setAction(SynchronizationAction.OK);
        }
      } else {
        /*
         * post is in sync on the server
         */
        if (clientPost.getChangeDate().after(lastSyncDate)
            && !SynchronizationDirection.SERVER_TO_CLIENT.equals(direction)) {
          /*
           * ... but not on the client -> update
           */
          clientPost.setAction(SynchronizationAction.UPDATE_SERVER);
        } else {
          clientPost.setAction(SynchronizationAction.OK);
        }
      }
      /*
       * In the next loop we go over all *remaining* server posts and
       * compare them. To not handle this post twice, we remove it from
       * the server posts list.
       */
      serverPosts.remove(clientPost.getIntraHash());
    }

    /*
     * handle the remaining posts that do not exist on the client
     */
    for (final SynchronizationPost serverPost : serverPosts.values()) {
      if (serverPost.getCreateDate().before(lastSyncDate)) {
        /*
         * post is older than lastSyncDate but does not exist on client
         */
        if (serverPost.getChangeDate().before(lastSyncDate)) {
          /*
           * post was deleted on client and must now be deleted on server
           */
          if (!SynchronizationDirection.SERVER_TO_CLIENT.equals(direction))
            serverPost.setAction(SynchronizationAction.DELETE_SERVER);
          else serverPost.setAction(SynchronizationAction.OK);
        } else {
          /*
           * CONFLICT (see above! FIXME: currently, we can't resolve this)
           *
           * we create the post on the client
           */
          if (!SynchronizationDirection.CLIENT_TO_SERVER.equals(direction))
            serverPost.setAction(SynchronizationAction.CREATE_CLIENT);
          else serverPost.setAction(SynchronizationAction.OK);
        }
      } else {
        /*
         * post was created after last sync -> create on client
         */
        if (!SynchronizationDirection.CLIENT_TO_SERVER.equals(direction))
          serverPost.setAction(SynchronizationAction.CREATE_CLIENT);
        else serverPost.setAction(SynchronizationAction.OK);
      }
      /*
       * add post to list of client posts
       */
      clientPosts.add(serverPost);
    }

    /*
     * FIXME posts with OK-state could be omitted.
     */
    return clientPosts;
  }