/*
 *------------------------------------------------------------------
 * Copyright (c) 2018 Cisco and/or its affiliates.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at:
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *------------------------------------------------------------------
 */

#ifndef __IGMP_GROUP_H__
#define __IGMP_GROUP_H__

#include <igmp/igmp_types.h>
#include <igmp/igmp_src.h>

/**
 * QUERY_REPLY = Timer running to reply to a G/SG specific query
 * QUERY_SENT  = wait for response from a sent G/SG specific query.
 *               Sent when a host leaves a group
 * RESEND_REPORT = Timer running to resend report
 * FILTER_MODE_CHANGE = to check if the group can swap to
 *                      INCLUDE mode (section 6.2.2)
 */
#define foreach_igmp_group_timer                \
  _(QUERY_REPLY, "query-reply")                 \
  _(QUERY_SENT,  "query-sent")                  \
  _(RESEND_REPORT, "resend-report")             \
  _(FILTER_MODE_CHANGE, "filter-mode-change")

/**
 * Types of timers maintained for each group
 */
typedef enum igmp_group_timer_type_t_
{
#define _(v,s) IGMP_GROUP_TIMER_##v,
  foreach_igmp_group_timer
#undef _
} igmp_group_timer_type_t;

#define IGMP_GROUP_N_TIMERS (IGMP_GROUP_TIMER_FILTER_MODE_CHANGE + 1)

extern u8 *format_igmp_group_timer_type (u8 * s, va_list * args);

/**
 * @brief IGMP group
 *  A multicast group address for which reception has been requested.
 */
typedef struct igmp_group_t_
{
  /** The group's key within the per-interface config */
  igmp_key_t *key;

  /**
   * A vector of running timers for the group. this can include:
   *  - group-specific query, sent on reception of a host 'leave'
   *  - filter-mode change timer, to check if the group can swap to
   *      INCLUDE mode (section 6.2.2)
   */
  u32 timers[IGMP_GROUP_N_TIMERS];

  /**
   * The current filter mode of the group (see 6.2.1)
   */
  igmp_filter_mode_t router_filter_mode;

  /**
   * The pool index of the config object this group is in
   */
  u32 config;

  /**
   * The number of times the last report has been sent
   */
  u32 n_reports_sent;

  /**
   * Source list per-filter mode
   */
  uword *igmp_src_by_key[IGMP_N_FILTER_MODES];
} igmp_group_t;

#define FOR_EACH_SRC(_src, _group, _filter, _body)                       \
do {                                                                    \
  igmp_key_t *__key__;                                                  \
  u32 __sid__;                                                          \
  hash_foreach_mem(__key__, __sid__, ((igmp_group_t*)_group)->igmp_src_by_key[(_filter)], \
  ({                                                                    \
    _src = pool_elt_at_index(igmp_main.srcs, __sid__);                  \
    do { _body; } while (0);                                            \
  }));                                                                  \
 } while (0);

/**
 * Forward declarations
 */
struct igmp_config_t_;

extern void igmp_group_clear (igmp_group_t ** group);
extern void igmp_group_free_all_srcs (igmp_group_t * group);

extern igmp_group_t *igmp_group_alloc (struct igmp_config_t_ *config,
				       const igmp_key_t * gkey,
				       igmp_filter_mode_t mode);

extern igmp_src_t *igmp_group_src_update (igmp_group_t * group,
					  const igmp_key_t * skey,
					  igmp_mode_t mode);

extern void igmp_group_src_remove (igmp_group_t * group, igmp_src_t * src);
extern u8 *format_igmp_group (u8 * s, va_list * args);


extern ip46_address_t *igmp_group_present_minus_new (igmp_group_t * group,
						     igmp_filter_mode_t mode,
						     const ip46_address_t *
						     saddrs);

extern ip46_address_t *igmp_group_new_minus_present (igmp_group_t * group,
						     igmp_filter_mode_t mode,
						     const ip46_address_t *
						     saddrs);

extern ip46_address_t *igmp_group_new_intersect_present (igmp_group_t * group,
							 igmp_filter_mode_t
							 mode,
							 const ip46_address_t
							 * saddrs);

extern u32 igmp_group_n_srcs (const igmp_group_t * group,
			      igmp_filter_mode_t mode);


/** \brief igmp group lookup
    @param group - igmp group
    @param key - igmp key
*/
extern igmp_src_t *igmp_src_lookup (igmp_group_t * group,
				    const igmp_key_t * key);

extern u32 igmp_group_index (const igmp_group_t * g);
extern igmp_group_t *igmp_group_get (u32 index);

#endif

/*
 * fd.io coding-style-patch-verification: ON
 *
 * Local Variables:
 * eval: (c-set-style "gnu")
 * End:
 */