// Copyright 2014 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifndef COMPONENTS_SYNC_SESSIONS_TAB_NODE_POOL_H_
#define COMPONENTS_SYNC_SESSIONS_TAB_NODE_POOL_H_

#include <stddef.h>

#include <map>
#include <set>
#include <string>

#include "base/macros.h"
#include "components/sessions/core/session_id.h"

namespace sync_sessions {

// A pool for managing free/used tab sync nodes for the *local* session.
// Performs lazy creation of sync nodes when necessary.
// Note: We make use of the following "id's"
// - a tab_id: created by session service, unique to this client
// - a tab_node_id: the id for a particular sync tab node. This is used
//   to generate the sync tab node tag through:
//       tab_tag = StringPrintf("%s %d", local_session_tag, tab_node_id);
//
// A sync node can be in one of the two states:
// 1. Associated   : Sync node is used and associated with a tab.
// 2. Free         : Sync node is unused.

class TabNodePool {
 public:
  TabNodePool();
  ~TabNodePool();

  // If free nodes > kFreeNodesHighWatermark, delete all free nodes until
  // free nodes <= kFreeNodesLowWatermark.
  static const size_t kFreeNodesLowWatermark;

  // Maximum limit of FreeNodes allowed on the client.
  static const size_t kFreeNodesHighWatermark;

  static const int kInvalidTabNodeID;

  // Returns the tab node associated with |tab_id| or kInvalidTabNodeID if
  // no association existed.
  int GetTabNodeIdFromTabId(SessionID tab_id) const;

  // Returns the tab_id for |tab_node_id| if it is associated else returns an
  // invalid ID.
  SessionID GetTabIdFromTabNodeId(int tab_node_id) const;

  // Gets the next free tab node (or creates a new one if needed) and associates
  // it to |tab_id|. Returns the tab node ID associated to |tab_id|. |tab_id|
  // must not be previously associated.
  int AssociateWithFreeTabNode(SessionID tab_id);

  // Reassociates |tab_node_id| with |tab_id|. If |tab_node_id| is not already
  // known, it is added to the tab node pool before being associated.
  void ReassociateTabNode(int tab_node_id, SessionID tab_id);

  // Removes association for |tab_id| and returns its tab node to the free node
  // pool.
  void FreeTab(SessionID tab_id);

  // Fills |deleted_node_ids| with any free nodes to be deleted as proscribed
  // by the free node low/high watermarks, in order to ensure the free node pool
  // does not grow too large.
  void CleanupTabNodes(std::set<int>* deleted_node_ids);

  // Deletes all known mappings for |tab_node_id|. As opposed to FreeTab(), it
  // does NOT free the node for later reuse. This is used for foreign sessions
  // when remote deletions are received.
  void DeleteTabNode(int tab_node_id);

  // Clear tab pool.
  void Clear();

  // Return the number of tab nodes this client currently has allocated
  // (including both free and associated nodes).
  size_t Capacity() const;

  // Return empty status (all tab nodes are in use).
  bool Empty() const;

  // Return full status (no tab nodes are in use).
  bool Full();

  // Returns tab node IDs for all known (used or free) tab nodes.
  std::set<int> GetAllTabNodeIds() const;

 private:
  friend class SyncTabNodePoolTest;
  using TabNodeIDToTabIDMap = std::map<int, SessionID>;
  using TabIDToTabNodeIDMap = std::map<SessionID, int>;

  // Adds |tab_node_id| to the tab node pool.
  // Note: this should only be called when we discover tab sync nodes from
  // previous sessions, not for freeing tab nodes we created through
  // GetTabNodeForTab (use FreeTab for that).
  void AddTabNode(int tab_node_id);

  // Associates |tab_node_id| with |tab_id|. |tab_node_id| must be free. In
  // order to associated a non-free tab node, ReassociateTabNode must be
  // used.
  void AssociateTabNode(int tab_node_id, SessionID tab_id);

  // Stores mapping of node ids associated with tab_ids, these are the used
  // nodes of tab node pool.
  // The nodes in the map can be returned to free tab node pool by calling
  // FreeTab(..).
  TabNodeIDToTabIDMap nodeid_tabid_map_;
  TabIDToTabNodeIDMap tabid_nodeid_map_;

  // The node ids for the set of free sync nodes.
  std::set<int> free_nodes_pool_;

  // The maximum used tab_node id for a sync node. A new sync node will always
  // be created with max_used_tab_node_id_ + 1.
  int max_used_tab_node_id_;

  DISALLOW_COPY_AND_ASSIGN(TabNodePool);
};

}  // namespace sync_sessions

#endif  // COMPONENTS_SYNC_SESSIONS_TAB_NODE_POOL_H_
