/*
     This file is part of GNUnet.
     Copyright (C)

     GNUnet is free software; you can redistribute it and/or modify
     it under the terms of the GNU General Public License as published
     by the Free Software Foundation; either version 3, or (at your
     option) any later version.

     GNUnet is distributed in the hope that it will be useful, but
     WITHOUT ANY WARRANTY; without even the implied warranty of
     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
     General Public License for more details.

     You should have received a copy of the GNU General Public License
     along with GNUnet; see the file COPYING.  If not, write to the
     Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
     Boston, MA 02110-1301, USA.
*/

/**
 * @file rps/gnunet-service-rps_peers.h
 * @brief utilities for managing (information about) peers
 * @author Julius Bünger
 */
#include "gnunet_util_lib.h"
#include <inttypes.h>
#include "gnunet_cadet_service.h"


/**
 * Different flags indicating the status of another peer.
 */
enum Peers_PeerFlags
{
  /**
   * If we are waiting for a reply from that peer (sent a pull request).
   */
  Peers_PULL_REPLY_PENDING   = 0x01,

  /* IN_OTHER_GOSSIP_LIST = 0x02, unneeded? */
  /* IN_OWN_SAMPLER_LIST  = 0x04, unneeded? */
  /* IN_OWN_GOSSIP_LIST   = 0x08, unneeded? */

  /**
   * We set this bit when we know the peer is online.
   */
  Peers_ONLINE               = 0x20,

  /**
   * We set this bit when we are going to destroy the channel to this peer.
   * When cleanup_channel is called, we know that we wanted to destroy it.
   * Otherwise the channel to the other peer was destroyed.
   */
  Peers_TO_DESTROY           = 0x40,
};

/**
 * Keep track of the status of a channel.
 *
 * This is needed in order to know what to do with a channel when it's
 * destroyed.
 */
enum Peers_ChannelFlags
{
  /**
   * We destroyed the channel because the other peer established a second one.
   */
  Peers_CHANNEL_ESTABLISHED_TWICE = 0x1,

  /**
   * The channel was removed because it was not needed any more. This should be
   * the sending channel.
   */
  Peers_CHANNEL_CLEAN = 0x2,
};

/**
 * @brief The role of a channel. Sending or receiving.
 */
enum Peers_ChannelRole
{
  /**
   * Channel is used for sending
   */
  Peers_CHANNEL_ROLE_SENDING   = 0x01,

  /**
   * Channel is used for receiving
   */
  Peers_CHANNEL_ROLE_RECEIVING = 0x02,
};

/**
 * @brief Functions of this type can be used to be stored at a peer for later execution.
 *
 * @param cls closure
 * @param peer peer to execute function on
 */
typedef void (* PeerOp) (void *cls, const struct GNUNET_PeerIdentity *peer);

/**
 * @brief Iterator over valid peers.
 *
 * @param cls closure
 * @param peer current public peer id
 * @return #GNUNET_YES if we should continue to
 *         iterate,
 *         #GNUNET_NO if not.
 */
typedef int
(*PeersIterator) (void *cls,
                  const struct GNUNET_PeerIdentity *peer);

/**
 * @brief Initialise storage of peers
 *
 * @param fn_valid_peers filename of the file used to store valid peer ids
 * @param cadet_h cadet handle
 * @param disconnect_handler Disconnect handler
 * @param c_handlers cadet handlers
 * @param own_id own peer identity
 */
void
Peers_initialise (char* fn_valid_peers,
                  struct GNUNET_CADET_Handle *cadet_h,
                  GNUNET_CADET_DisconnectEventHandler disconnect_handler,
                  const struct GNUNET_MQ_MessageHandler *c_handlers,
                  const struct GNUNET_PeerIdentity *own_id);

/**
 * @brief Delete storage of peers that was created with #Peers_initialise ()
 */
void
Peers_terminate ();


/**
 * @brief Get all currently known, valid peer ids.
 *
 * @param it function to call on each peer id
 * @param it_cls extra argument to @a it
 * @return the number of key value pairs processed,
 *         #GNUNET_SYSERR if it aborted iteration
 */
int
Peers_get_valid_peers (PeersIterator iterator,
                       void *it_cls);

/**
 * @brief Add peer to known peers.
 *
 * This function is called on new peer_ids from 'external' sources
 * (client seed, cadet get_peers(), ...)
 *
 * @param peer the new #GNUNET_PeerIdentity
 *
 * @return #GNUNET_YES if peer was inserted
 *         #GNUNET_NO  otherwise (if peer was already known or
 *                     peer was #own_identity)
 */
int
Peers_insert_peer (const struct GNUNET_PeerIdentity *peer);

/**
 * @brief Try connecting to a peer to see whether it is online
 *
 * If not known yet, insert into known peers
 *
 * @param peer the peer whose liveliness is to be checked
 * @return #GNUNET_YES if peer had to be inserted
 *         #GNUNET_NO  otherwise (if peer was already known or
 *                     peer was #own_identity)
 */
int
Peers_issue_peer_liveliness_check (const struct GNUNET_PeerIdentity *peer);

/**
 * @brief Check if peer is removable.
 *
 * Check if
 *  - a recv channel exists
 *  - there are pending messages
 *  - there is no pending pull reply
 *
 * @param peer the peer in question
 * @return #GNUNET_YES    if peer is removable
 *         #GNUNET_NO     if peer is NOT removable
 *         #GNUNET_SYSERR if peer is not known
 */
int
Peers_check_removable (const struct GNUNET_PeerIdentity *peer);

/**
 * @brief Remove peer
 *
 * @param peer the peer to clean
 * @return #GNUNET_YES if peer was removed
 *         #GNUNET_NO  otherwise
 */
int
Peers_remove_peer (const struct GNUNET_PeerIdentity *peer);

/**
 * @brief set flags on a given peer.
 *
 * @param peer the peer to set flags on
 * @param flags the flags
 */
void
Peers_set_peer_flag (const struct GNUNET_PeerIdentity *peer, enum Peers_PeerFlags flags);

/**
 * @brief unset flags on a given peer.
 *
 * @param peer the peer to unset flags on
 * @param flags the flags
 */
void
Peers_unset_peer_flag (const struct GNUNET_PeerIdentity *peer, enum Peers_PeerFlags flags);

/**
 * @brief Check whether flags on a peer are set.
 *
 * @param peer the peer to check the flag of
 * @param flags the flags to check
 *
 * @return #GNUNET_YES if all given flags are set
 *         ##GNUNET_NO  otherwise
 */
int
Peers_check_peer_flag (const struct GNUNET_PeerIdentity *peer, enum Peers_PeerFlags flags);


/**
 * @brief set flags on a given channel.
 *
 * @param channel the channel to set flags on
 * @param flags the flags
 */
void
Peers_set_channel_flag (uint32_t *channel_flags, enum Peers_ChannelFlags flags);

/**
 * @brief unset flags on a given channel.
 *
 * @param channel the channel to unset flags on
 * @param flags the flags
 */
void
Peers_unset_channel_flag (uint32_t *channel_flags, enum Peers_ChannelFlags flags);

/**
 * @brief Check whether flags on a channel are set.
 *
 * @param channel the channel to check the flag of
 * @param flags the flags to check
 *
 * @return #GNUNET_YES if all given flags are set
 *         #GNUNET_NO  otherwise
 */
int
Peers_check_channel_flag (uint32_t *channel_flags, enum Peers_ChannelFlags flags);

/**
 * @brief Get the flags for the channel in @a role for @a peer.
 *
 * @param peer Peer to get the channel flags for.
 * @param role Role of channel to get flags for
 *
 * @return The flags.
 */
uint32_t *
Peers_get_channel_flag (const struct GNUNET_PeerIdentity *peer,
                        enum Peers_ChannelRole role);

/**
 * @brief Check whether we have information about the given peer.
 *
 * FIXME probably deprecated. Make this the new _online.
 *
 * @param peer peer in question
 *
 * @return #GNUNET_YES if peer is known
 *         #GNUNET_NO  if peer is not knwon
 */
int
Peers_check_peer_known (const struct GNUNET_PeerIdentity *peer);

/**
 * @brief Check whether @a peer is actually a peer.
 *
 * A valid peer is a peer that we know exists eg. we were connected to once.
 *
 * @param peer peer in question
 *
 * @return #GNUNET_YES if peer is valid
 *         #GNUNET_NO  if peer is not valid
 */
int
Peers_check_peer_valid (const struct GNUNET_PeerIdentity *peer);

/**
 * @brief Indicate that we want to send to the other peer
 *
 * This establishes a sending channel
 *
 * @param peer the peer to establish channel to
 */
void
Peers_indicate_sending_intention (const struct GNUNET_PeerIdentity *peer);

/**
 * @brief Check whether other peer has the intention to send/opened channel
 *        towars us
 *
 * @param peer the peer in question
 *
 * @return #GNUNET_YES if peer has the intention to send
 *         #GNUNET_NO  otherwise
 */
int
Peers_check_peer_send_intention (const struct GNUNET_PeerIdentity *peer);

/**
 * Handle the channel a peer opens to us.
 *
 * @param cls The closure
 * @param channel The channel the peer wants to establish
 * @param initiator The peer's peer ID
 *
 * @return initial channel context for the channel
 *         (can be NULL -- that's not an error)
 */
void *
Peers_handle_inbound_channel (void *cls,
                              struct GNUNET_CADET_Channel *channel,
                              const struct GNUNET_PeerIdentity *initiator);

/**
 * @brief Check whether a sending channel towards the given peer exists
 *
 * @param peer the peer to check for
 *
 * @return #GNUNET_YES if a sending channel towards that peer exists
 *         #GNUNET_NO  otherwise
 */
int
Peers_check_sending_channel_exists (const struct GNUNET_PeerIdentity *peer);

/**
 * @brief check whether the given channel is the sending channel of the given
 *        peer
 *
 * @param peer the peer in question
 * @param channel the channel to check for
 * @param role either #Peers_CHANNEL_ROLE_SENDING, or
 *                    #Peers_CHANNEL_ROLE_RECEIVING
 *
 * @return #GNUNET_YES if the given chennel is the sending channel of the peer
 *         #GNUNET_NO  otherwise
 */
int
Peers_check_channel_role (const struct GNUNET_PeerIdentity *peer,
                          const struct GNUNET_CADET_Channel *channel,
                          enum Peers_ChannelRole role);

/**
 * @brief Destroy the send channel of a peer e.g. stop indicating a sending
 *        intention to another peer
 *
 * If there is also no channel to receive messages from that peer, remove it
 * from the peermap.
 *
 * @peer the peer identity of the peer whose sending channel to destroy
 * @return #GNUNET_YES if channel was destroyed
 *         #GNUNET_NO  otherwise
 */
int
Peers_destroy_sending_channel (const struct GNUNET_PeerIdentity *peer);

/**
 * This is called when a channel is destroyed.
 *
 * Removes peer completely from our knowledge if the send_channel was destroyed
 * Otherwise simply delete the recv_channel
 *
 * @param cls The closure
 * @param channel The channel being closed
 * @param channel_ctx The context associated with this channel
 */
void
Peers_cleanup_destroyed_channel (void *cls,
                                 const struct GNUNET_CADET_Channel *channel);

/**
 * @brief Send a message to another peer.
 *
 * Keeps track about pending messages so they can be properly removed when the
 * peer is destroyed.
 *
 * @param peer receeiver of the message
 * @param ev envelope of the message
 * @param type type of the message
 */
void
Peers_send_message (const struct GNUNET_PeerIdentity *peer,
                    struct GNUNET_MQ_Envelope *ev,
                    const char *type);

/**
 * @brief Schedule a operation on given peer
 *
 * Avoids scheduling an operation twice.
 *
 * @param peer the peer we want to schedule the operation for once it gets live
 *
 * @return #GNUNET_YES if the operation was scheduled
 *         #GNUNET_NO  otherwise
 */
int
Peers_schedule_operation (const struct GNUNET_PeerIdentity *peer,
                          const PeerOp peer_op);

/**
 * @brief Get the recv_channel of @a peer.
 * Needed to correctly handle (call #GNUNET_CADET_receive_done()) incoming
 * messages.
 *
 * @param peer The peer to get the recv_channel from.
 *
 * @return The recv_channel.
 */
struct GNUNET_CADET_Channel *
Peers_get_recv_channel (const struct GNUNET_PeerIdentity *peer);

/* end of gnunet-service-rps_peers.h */
