/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License, Version 1.0 only
 * (the "License").  You may not use this file except in compliance
 * with the License.
 *
 * You can obtain a copy of the license at legal-notices/CDDLv1_0.txt
 * or http://forgerock.org/license/CDDLv1.0.html.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the License file at legal-notices/CDDLv1_0.txt.
 * If applicable, add the following below this CDDL HEADER, with the
 * fields enclosed by brackets "[]" replaced with your own identifying
 * information:
 *      Portions Copyright [yyyy] [name of copyright owner]
 *
 * CDDL HEADER END
 *
 *
 *      Copyright 2008-2009 Sun Microsystems, Inc.
 *      Portions Copyright 2013-2015 ForgeRock AS.
 */

package org.opends.server.replication.server;

import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.opends.server.replication.common.AssuredMode;
import org.opends.server.replication.common.CSN;
import org.opends.server.replication.protocol.AckMsg;

/**
 * This class is the mother class for sub-classes holding any information needed
 * about the acks that the replication server will wait for, when he receives an
 * update message with the assured flag on (assured replication acknowledgments
 * expected).
 * It also includes info/routines for constructing the final ack to be sent to
 * the sender of the update message.
 *
 * It is expected to have one sub-class per assured replication sub mode.
 */
public abstract class ExpectedAcksInfo
{
  /**
   * The server handler of the server that sent the assured update message and
   * to who we want to return the final ack.
   */
  private ServerHandler requesterServerHandler;

  /** The requested assured mode of matching update message. */
  private AssuredMode assuredMode;

  /** The CSN of the assured update message we want acks for. */
  protected CSN csn;

  /**
   * Is the treatment of the acks for the update message completed or not ?
   * This is used for concurrent access to this object by either the assured
   * timeout task or the code for processing an ack for the matching update
   * message. This should be set to true when the treatment of the expected
   * acks is completed or an ack timeout has occurred and we are going to
   * remove this object from the map where it is stored.
   */
  private boolean completed;

  /**
   * This gives the list of servers we are willing to wait acks from and the
   * information about the ack from the servers.
   * key: the id of the server.
   * value: a boolean true if we received the ack from the server,
   * false otherwise.
   */
  protected Map<Integer,Boolean> expectedServersAckStatus = new HashMap<>();

  /**
   * Facility for monitoring:
   * If the timeout occurs for the original update, we call createAck(true)
   * in the timeout code for sending back an error ack to the original server.
   * We use this call to also save the list of server ids for server we did not
   * have time to receive an ack from. For updating its counters, the timeout
   * code can then call getTimeoutServers() method to now which servers did not
   * respond in time.
   */
  protected List<Integer> serversInTimeout;

  /**
   * Creates a new ExpectedAcksInfo.
   * @param csn The CSN of the assured update message
   * @param requesterServerHandler The server handler of the server that sent
   * the assured update message
   * @param assuredMode The assured mode requested by the assured update message
   * @param expectedServers The list of servers we want an ack from
   */
  protected ExpectedAcksInfo(CSN csn, ServerHandler requesterServerHandler,
      AssuredMode assuredMode, List<Integer> expectedServers)
  {
    this.requesterServerHandler = requesterServerHandler;
    this.assuredMode = assuredMode;
    this.csn = csn;

    // Initialize list of servers we expect acks from
    for (Integer serverId : expectedServers)
    {
      expectedServersAckStatus.put(serverId, false);
    }
  }

  /**
   * Gets the server handler of the server which requested the acknowledgments.
   * @return The server handler of the server which requested the
   * acknowledgments.
   */
  public ServerHandler getRequesterServer()
  {
    return requesterServerHandler;
  }

  /**
   * Gets the list of expected servers that did not respond in time.
   * @return The list of expected servers that did not respond in time.
   */
  public List<Integer> getTimeoutServers()
  {
    return serversInTimeout;
  }

  /**
   * Gets the requested assured mode for the matching update message.
   * @return The requested assured mode for the matching update message.
   */
  public AssuredMode getAssuredMode()
  {
    return assuredMode;
  }

  /**
   * Process the received ack from a server we are waiting an ack from.
   * @param ackingServer The server handler of the server that sent the ack
   * @param ackMsg The ack message to process
   * @return True if the expected number of acks has just been reached
   */
  public abstract boolean processReceivedAck(ServerHandler ackingServer,
    AckMsg ackMsg);

  /**
   * Creates the ack message to be returned to the requester server, taking into
   * account the information in the received acks from every servers.
   * @param timeout True if we call this method when the timeout occurred, that
   * is we did not received every expected acks in time, and thus, the timeout
   * flag should also be enabled in the returned ack message.
   * @return The ack message ready to be sent to the requester server
   */
  public abstract AckMsg createAck(boolean timeout);

  /**
   * Has the treatment of this object been completed or not?
   * If true is returned, one must not modify this object (useless) nor remove
   * it from the map where it is stored (will be or has already been done by the
   * other code (ack timeout code, or ack processing code)).
   * @return True if treatment of this object has been completed.
   */
  public boolean isCompleted()
  {
    return completed;
  }

  /**
   * Signal that treatment of this object has been completed and that it is
   * going to be removed from the map where it is stored.
   */
  public void completed()
  {
    completed = true;
  }
}
