/*
 * Copyright (c) 2017-2019 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.
 */

#include <inttypes.h>

#include <vat/vat.h>
#include <vlibapi/api.h>
#include <vlibmemory/api.h>
#include <vppinfra/error.h>

#include <vnet/ip/ip4_packet.h>
#include <vnet/ip/ip6_packet.h>
#include <vnet/ip/format.h>
#include <vnet/ip/ip_types_api.h>

#define __plugin_msg_base hicn_test_main.msg_id_base
#include <vlibapi/vat_helper_macros.h>

#include <hicn/hicn_api.h>
#include "error.h"

// uword unformat_sw_if_index(unformat_input_t * input, va_list * args);

/* Declare message IDs */
#include "hicn_msg_enum.h"

/* define message structures */
#define vl_typedefs
#include <vpp/api/vpe_all_api_h.h>
#include <hicn/hicn_all_api_h.h>
#undef vl_typedefs

/* Get CRC codes of the messages defined outside of this plugin */
#define vl_msg_name_crc_list
#include <vpp/api/vpe_all_api_h.h>
#undef vl_msg_name_crc_list

/* declare message handlers for each api */

#define vl_endianfun		/* define message structures */
#include "hicn_all_api_h.h"
#undef vl_endianfun

/* instantiate all the print functions we know about */
#define vl_print(handle, ...)
#define vl_printfun
#include "hicn_all_api_h.h"
#undef vl_printfun

/* Get the API version number. */
#define vl_api_version(n, v) static u32 api_version=(v);
#include "hicn_all_api_h.h"
#undef vl_api_version

/* SUPPORTING FUNCTIONS NOT LOADED BY VPP_API_TEST */
uword
unformat_ip46_address (unformat_input_t * input, va_list * args)
{
  ip46_address_t *ip46 = va_arg (*args, ip46_address_t *);
  ip46_type_t type = va_arg (*args, ip46_type_t);
  if ((type != IP46_TYPE_IP6) &&
      unformat (input, "%U", unformat_ip4_address, &ip46->ip4))
    {
      ip46_address_mask_ip4 (ip46);
      return 1;
    }
  else if ((type != IP46_TYPE_IP4) &&
	   unformat (input, "%U", unformat_ip6_address, &ip46->ip6))
    {
      return 1;
    }
  return 0;
}

static ip46_type_t
ip_address_union_decode (const vl_api_address_union_t * in,
			 vl_api_address_family_t af, ip46_address_t * out)
{
  ip46_type_t type;

  switch (clib_net_to_host_u32 (af))
    {
    case ADDRESS_IP4:
      clib_memset (out, 0, sizeof (*out));
      clib_memcpy (&out->ip4, &in->ip4, sizeof (out->ip4));
      type = IP46_TYPE_IP4;
      break;
    case ADDRESS_IP6:
      clib_memcpy (&out->ip6, &in->ip6, sizeof (out->ip6));
      type = IP46_TYPE_IP6;
      break;
    default:
      ASSERT (!"Unkown address family in API address type");
      type = IP46_TYPE_ANY;
      break;
    }

  return type;
}

void
ip6_address_encode (const ip6_address_t * in, vl_api_ip6_address_t out)
{
  clib_memcpy (out, in, sizeof (*in));
}

void
ip6_address_decode (const vl_api_ip6_address_t in, ip6_address_t * out)
{
  clib_memcpy (out, in, sizeof (*out));
}

void
ip4_address_encode (const ip4_address_t * in, vl_api_ip4_address_t out)
{
  clib_memcpy (out, in, sizeof (*in));
}

void
ip4_address_decode (const vl_api_ip4_address_t in, ip4_address_t * out)
{
  clib_memcpy (out, in, sizeof (*out));
}

static void
ip_address_union_encode (const ip46_address_t * in,
			 vl_api_address_family_t af,
			 vl_api_address_union_t * out)
{
  if (ADDRESS_IP6 == clib_net_to_host_u32 (af))
    ip6_address_encode (&in->ip6, out->ip6);
  else
    ip4_address_encode (&in->ip4, out->ip4);
}

ip46_type_t
ip_address_decode (const vl_api_address_t * in, ip46_address_t * out)
{
  return (ip_address_union_decode (&in->un, in->af, out));
}

void
ip_address_encode (const ip46_address_t * in, ip46_type_t type,
		   vl_api_address_t * out)
{
  switch (type)
    {
    case IP46_TYPE_IP4:
      out->af = clib_net_to_host_u32 (ADDRESS_IP4);
      break;
    case IP46_TYPE_IP6:
      out->af = clib_net_to_host_u32 (ADDRESS_IP6);
      break;
    case IP46_TYPE_ANY:
      if (ip46_address_is_ip4 (in))
	out->af = clib_net_to_host_u32 (ADDRESS_IP4);
      else
	out->af = clib_net_to_host_u32 (ADDRESS_IP6);
      break;
    }
  ip_address_union_encode (in, out->af, &out->un);
}

fib_protocol_t
fib_proto_from_ip46 (ip46_type_t iproto)
{
  switch (iproto)
    {
    case IP46_TYPE_IP4:
      return FIB_PROTOCOL_IP4;
    case IP46_TYPE_IP6:
      return FIB_PROTOCOL_IP6;
    case IP46_TYPE_ANY:
      ASSERT (0);
      return FIB_PROTOCOL_IP4;
    }

  ASSERT (0);
  return FIB_PROTOCOL_IP4;
}

ip46_type_t
fib_proto_to_ip46 (fib_protocol_t fproto)
{
  switch (fproto)
    {
    case FIB_PROTOCOL_IP4:
      return (IP46_TYPE_IP4);
    case FIB_PROTOCOL_IP6:
      return (IP46_TYPE_IP6);
    case FIB_PROTOCOL_MPLS:
      return (IP46_TYPE_ANY);
    }
  ASSERT (0);
  return (IP46_TYPE_ANY);
}

void
ip_prefix_decode (const vl_api_prefix_t * in, fib_prefix_t * out)
{
  switch (clib_net_to_host_u32 (in->address.af))
    {
    case ADDRESS_IP4:
      out->fp_proto = FIB_PROTOCOL_IP4;
      break;
    case ADDRESS_IP6:
      out->fp_proto = FIB_PROTOCOL_IP6;
      break;
    }
  out->fp_len = in->len;
  out->___fp___pad = 0;
  ip_address_decode (&in->address, &out->fp_addr);
}

void
ip_prefix_encode (const fib_prefix_t * in, vl_api_prefix_t * out)
{
  out->len = in->fp_len;
  ip_address_encode (&in->fp_addr,
		     fib_proto_to_ip46 (in->fp_proto), &out->address);
}

/////////////////////////////////////////////////////

#define HICN_FACE_NULL ~0

typedef struct
{
  /* API message ID base */
  u16 msg_id_base;
  vat_main_t *vat_main;
  u32 ping_id;
} hicn_test_main_t;

hicn_test_main_t hicn_test_main;

#define foreach_standard_reply_retval_handler            \
_(hicn_api_node_params_set_reply)                        \
_(hicn_api_face_ip_del_reply)                            \
_(hicn_api_face_del_reply)                               \
_(hicn_api_route_nhops_add_reply)                        \
_(hicn_api_route_del_reply)                              \
_(hicn_api_route_nhop_del_reply)                         \
_(hicn_api_punting_add_reply)                            \
_(hicn_api_face_cons_del_reply)                          \
_(hicn_api_face_prod_del_reply)

#define _(n)                                            \
    static void vl_api_##n##_t_handler                  \
    (vl_api_##n##_t * mp)                               \
    {                                                   \
        vat_main_t * vam = hicn_test_main.vat_main;     \
        i32 retval = ntohl(mp->retval);                 \
        if (vam->async_mode) {                          \
            vam->async_errors += (retval < 0);          \
        } else {					\
	    fformat (vam->ofp,"%s\n", get_error_string(retval));\
            vam->retval = retval;                       \
            vam->result_ready = 1;                      \
        }                                               \
    }
foreach_standard_reply_retval_handler;
#undef _

/*
 * Table of message reply handlers, must include boilerplate handlers we just
 * generated
 */
#define foreach_vpe_api_reply_msg                                       \
_(HICN_API_NODE_PARAMS_SET_REPLY, hicn_api_node_params_set_reply)       \
_(HICN_API_NODE_PARAMS_GET_REPLY, hicn_api_node_params_get_reply)       \
_(HICN_API_NODE_STATS_GET_REPLY, hicn_api_node_stats_get_reply)         \
_(HICN_API_FACE_IP_DEL_REPLY, hicn_api_face_ip_del_reply)               \
_(HICN_API_FACE_IP_ADD_REPLY, hicn_api_face_ip_add_reply)               \
_(HICN_API_FACE_ADD_REPLY, hicn_api_face_add_reply)                     \
_(HICN_API_FACE_DEL_REPLY, hicn_api_face_del_reply)                     \
_(HICN_API_FACE_GET_REPLY, hicn_api_face_get_reply)                     \
_(HICN_API_FACES_DETAILS, hicn_api_faces_details)                       \
_(HICN_API_FACE_STATS_DETAILS, hicn_api_face_stats_details)             \
_(HICN_API_ROUTE_NHOPS_ADD_REPLY, hicn_api_route_nhops_add_reply)       \
_(HICN_API_FACE_IP_PARAMS_GET_REPLY, hicn_api_face_ip_params_get_reply) \
_(HICN_API_ROUTE_GET_REPLY, hicn_api_route_get_reply)                   \
_(HICN_API_ROUTES_DETAILS, hicn_api_routes_details)                     \
_(HICN_API_ROUTE_DEL_REPLY, hicn_api_route_del_reply)                   \
_(HICN_API_ROUTE_NHOP_DEL_REPLY, hicn_api_route_nhop_del_reply)         \
_(HICN_API_STRATEGIES_GET_REPLY, hicn_api_strategies_get_reply)         \
_(HICN_API_STRATEGY_GET_REPLY, hicn_api_strategy_get_reply)             \
_(HICN_API_PUNTING_ADD_REPLY, hicn_api_punting_add_reply)               \
_(HICN_API_REGISTER_PROD_APP_REPLY, hicn_api_register_prod_app_reply)   \
_(HICN_API_FACE_PROD_DEL_REPLY, hicn_api_face_prod_del_reply)           \
_(HICN_API_REGISTER_CONS_APP_REPLY, hicn_api_register_cons_app_reply)   \
_(HICN_API_FACE_CONS_DEL_REPLY, hicn_api_face_cons_del_reply)


static int
api_hicn_api_node_params_set (vat_main_t * vam)
{
  unformat_input_t *input = vam->input;
  int enable_disable = 1;
  int pit_size = -1, cs_size = -1;
  f64 pit_max_lifetime_sec = -1.0f;
  int ret;

  vl_api_hicn_api_node_params_set_t *mp;

  /* Parse args required to build the message */
  while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
    {
      if (unformat (input, "disable"))
	{
	  enable_disable = 0;
	}
      else if (unformat (input, "PIT size %d", &pit_size))
	{;
	}
      else if (unformat (input, "CS size %d", &cs_size))
	{;
	}
      else if (unformat (input, "PIT maxlife %f", &pit_max_lifetime_sec))
	{;
	}
      else
	{
	  break;
	}
    }

  /* Construct the API message */
  M (HICN_API_NODE_PARAMS_SET, mp);
  mp->enable_disable = enable_disable;
  mp->pit_max_size = clib_host_to_net_i32 (pit_size);
  mp->cs_max_size = clib_host_to_net_i32 (cs_size);
  mp->pit_max_lifetime_sec = pit_max_lifetime_sec;

  /* send it... */
  S (mp);

  /* Wait for a reply... */
  W (ret);

  return ret;
}

static int
api_hicn_api_node_params_get (vat_main_t * vam)
{
  vl_api_hicn_api_node_params_get_t *mp;
  int ret;

  //Construct the API message
  M (HICN_API_NODE_PARAMS_GET, mp);

  /* send it... */
  S (mp);

  /* Wait for a reply... */
  W (ret);

  return ret;
}

static void
  vl_api_hicn_api_node_params_get_reply_t_handler
  (vl_api_hicn_api_node_params_get_reply_t * mp)
{
  vat_main_t *vam = hicn_test_main.vat_main;
  i32 retval = ntohl (mp->retval);

  if (vam->async_mode)
    {
      vam->async_errors += (retval < 0);
      return;
    }
  vam->retval = retval;
  vam->result_ready = 1;

  if (vam->retval < 0)
    {
      //vpp_api_test infra will also print out string form of error
      fformat (vam->ofp, "   (API call error: %d)\n", vam->retval);
      return;
    }
  fformat (vam->ofp,
	   "Enabled %d\n"
	   "  Features: cs:%d\n"
	   "  PIT size %d\n"
	   "  PIT lifetime dflt %.3f, min %.3f, max %.3f\n"
	   "  CS size %d\n",
	   mp->is_enabled,
	   mp->feature_cs,
	   clib_net_to_host_u32 (mp->pit_max_size),
	   mp->pit_max_lifetime_sec, clib_net_to_host_u32 (mp->cs_max_size));
}

static int
api_hicn_api_node_stats_get (vat_main_t * vam)
{
  vl_api_hicn_api_node_stats_get_t *mp;
  int ret;

  /* Construct the API message */
  M (HICN_API_NODE_STATS_GET, mp);

  /* send it... */
  S (mp);

  /* Wait for a reply... */
  W (ret);

  return ret;
}

static void
  vl_api_hicn_api_node_stats_get_reply_t_handler
  (vl_api_hicn_api_node_stats_get_reply_t * rmp)
{
  vat_main_t *vam = hicn_test_main.vat_main;
  i32 retval = ntohl (rmp->retval);

  if (vam->async_mode)
    {
      vam->async_errors += (retval < 0);
      return;
    }
  vam->retval = retval;
  vam->result_ready = 1;

  if (vam->retval < 0)
    {
      //vpp_api_test infra will also print out string form of error
      fformat (vam->ofp, "   (API call error: %d)\n", vam->retval);
      return;
    }
  else
    {
      fformat (vam->ofp,	//compare hicn_cli_show_command_fn block:should match
	       "  PIT entries (now): %d\n"
	       "  CS entries (now): %d\n"
	       "  Forwarding statistics:"
	       "    pkts_processed: %d\n"
	       "    pkts_interest_count: %d\n"
	       "    pkts_data_count: %d\n"
	       "    pkts_nak_count: %d\n"
	       "    pkts_from_cache_count: %d\n"
	       "    pkts_nacked_interests_count: %d\n"
	       "    pkts_nak_hoplimit_count: %d\n"
	       "    pkts_nak_no_route_count: %d\n"
	       "    pkts_no_pit_count: %d\n"
	       "    pit_expired_count: %d\n"
	       "    cs_expired_count: %d\n"
	       "    cs_lru_count: %d\n"
	       "    pkts_drop_no_buf: %d\n"
	       "    interests_aggregated: %d\n"
	       "    interests_retransmitted: %d\n",
	       clib_net_to_host_u64 (rmp->pit_entries_count),
	       clib_net_to_host_u64 (rmp->cs_entries_count),
	       clib_net_to_host_u64 (rmp->pkts_processed),
	       clib_net_to_host_u64 (rmp->pkts_interest_count),
	       clib_net_to_host_u64 (rmp->pkts_data_count),
	       clib_net_to_host_u64 (rmp->pkts_from_cache_count),
	       clib_net_to_host_u64 (rmp->pkts_no_pit_count),
	       clib_net_to_host_u64 (rmp->pit_expired_count),
	       clib_net_to_host_u64 (rmp->cs_expired_count),
	       clib_net_to_host_u64 (rmp->cs_lru_count),
	       clib_net_to_host_u64 (rmp->pkts_drop_no_buf),
	       clib_net_to_host_u64 (rmp->interests_aggregated),
	       clib_net_to_host_u64 (rmp->interests_retx));
    }
}

static int
api_hicn_api_face_ip_add (vat_main_t * vam)
{
  unformat_input_t *input = vam->input;
  ip46_address_t local_addr = { 0 };
  ip46_address_t remote_addr = { 0 };
  int ret = HICN_ERROR_NONE;
  int sw_if = 0;
  vl_api_hicn_api_face_add_t *mp;

  /* Parse args required to build the message */
  while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
    {
      if (unformat
	  (input, "local %U", unformat_ip46_address, &local_addr,
	   IP46_TYPE_ANY));
      else
	if (unformat
	    (input, "remote %U", unformat_ip46_address, &remote_addr,
	     IP46_TYPE_ANY));
      else if (unformat (input, "intfc %d", &sw_if));
      else
	{
	  break;
	}
    }

  /* Check for presence of both addresses */
  if (ip46_address_is_zero (&remote_addr))
    {
      clib_warning ("Incomplete IP face. Please specify remote address");
      return (1);
    }
  /* Construct the API message */
  M (HICN_API_FACE_ADD, mp);
  mp->type = clib_host_to_net_u32(IP_FACE);
  ip_address_encode (&local_addr, IP46_TYPE_ANY, &mp->face.ip.local_addr);
  ip_address_encode (&remote_addr, IP46_TYPE_ANY, &mp->face.ip.remote_addr);
  mp->face.ip.swif = clib_host_to_net_u32 (sw_if);

  /* send it... */
  S (mp);

  /* Wait for a reply... */
  W (ret);

  return ret;
}

static void
  vl_api_hicn_api_face_ip_add_reply_t_handler
  (vl_api_hicn_api_face_ip_add_reply_t * rmp)
{
  vat_main_t *vam = hicn_test_main.vat_main;
  i32 retval = ntohl (rmp->retval);

  if (vam->async_mode)
    {
      vam->async_errors += (retval < 0);
      return;
    }
  vam->retval = retval;
  vam->result_ready = 1;

  if (vam->retval < 0)
    {
      //vpp_api_test infra will also print out string form of error
      fformat (vam->ofp, "   (API call error: %d)\n", vam->retval);
      return;
    }
  fformat (vam->ofp, "New Face ID: %d\n", ntohl (rmp->faceid));
}

static int
api_hicn_api_face_udp_add (vat_main_t * vam)
{
  unformat_input_t *input = vam->input;
  ip46_address_t local_addr = ip46_address_initializer;
  ip46_address_t remote_addr = ip46_address_initializer;
  u32 sport = 0;
  u32 dport = 0;
  int ret = HICN_ERROR_NONE;
  int sw_if = ~0;
  vl_api_hicn_api_face_add_t *mp;

  /* Parse args required to build the message */
  while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
    {
      if (unformat
	  (input, "local %U port %u", unformat_ip46_address, &local_addr,
	   IP46_TYPE_ANY, &sport));
      else
	if (unformat
	    (input, "remote %U port %u", unformat_ip46_address, &remote_addr,
	     IP46_TYPE_ANY, &dport));
      else if (unformat (input, "intfc %d", &sw_if));
      else
	{
	  break;
	}
    }

  /* Check for presence of both addresses */
  if (ip46_address_is_zero (&remote_addr)
      || ip46_address_is_zero (&local_addr) || sport == 0 || dport == 0)
    {
      clib_warning
	("Incomplete UDP face. Please specify local and remote address and port");
      return (1);
    }
  /* Construct the API message */
  M (HICN_API_FACE_ADD, mp);
  mp->type = clib_host_to_net_u32(UDP_FACE);
  ip_address_encode (&local_addr, IP46_TYPE_ANY, &mp->face.udp.local_addr);
  ip_address_encode (&remote_addr, IP46_TYPE_ANY, &mp->face.udp.remote_addr);
  mp->face.udp.lport = clib_host_to_net_u16 (sport);
  mp->face.udp.rport = clib_host_to_net_u16 (dport);
  mp->face.udp.swif = clib_host_to_net_u32 (sw_if);

  /* send it... */
  S (mp);

  /* Wait for a reply... */
  W (ret);

  return ret;
}

static void
  vl_api_hicn_api_face_add_reply_t_handler
  (vl_api_hicn_api_face_add_reply_t * rmp)
{
  vat_main_t *vam = hicn_test_main.vat_main;
  i32 retval = ntohl (rmp->retval);

  if (vam->async_mode)
    {
      vam->async_errors += (retval < 0);
      return;
    }
  vam->retval = retval;
  vam->result_ready = 1;

  if (vam->retval < 0)
    {
      //vpp_api_test infra will also print out string form of error
      fformat (vam->ofp, "   (API call error: %d)\n", vam->retval);
      return;
    }
  fformat (vam->ofp, "New Face ID: %d\n", ntohl (rmp->faceid));
}

static int
api_hicn_api_face_ip_del (vat_main_t * vam)
{
  unformat_input_t *input = vam->input;
  vl_api_hicn_api_face_ip_del_t *mp;
  u32 faceid = 0, ret;

  while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
    {
      if (unformat (input, "face %d", &faceid))
	{;
	}
      else
	{
	  break;
	}
    }

  //Check for presence of face ID
  if (faceid == ~0)
    {
      clib_warning ("Please specify face ID");
      return 1;
    }
  //Construct the API message
  M (HICN_API_FACE_IP_DEL, mp);
  mp->faceid = clib_host_to_net_u32 (faceid);

  //send it...
  S (mp);

  //Wait for a reply...
  W (ret);

  return ret;
}

static int
api_hicn_api_face_del (vat_main_t * vam)
{
  unformat_input_t *input = vam->input;
  vl_api_hicn_api_face_del_t *mp;
  u32 faceid = 0, ret;

  while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
    {
      if (unformat (input, "face %d", &faceid))
	{;
	}
      else
	{
	  break;
	}
    }

  //Check for presence of face ID
  if (faceid == ~0)
    {
      clib_warning ("Please specify face ID");
      return 1;
    }
  //Construct the API message
  M (HICN_API_FACE_DEL, mp);
  mp->faceid = clib_host_to_net_u32 (faceid);

  //send it...
  S (mp);

  //Wait for a reply...
  W (ret);

  return ret;
}

static int
api_hicn_api_face_ip_params_get (vat_main_t * vam)
{
  unformat_input_t *input = vam->input;
  vl_api_hicn_api_face_ip_params_get_t *mp;
  u32 faceid = HICN_FACE_NULL, ret;

  while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
    {
      if (unformat (input, "face %d", &faceid))
	{;
	}
      else
	{
	  break;
	}
    }

  //Check for presence of face ID
  if (faceid == HICN_FACE_NULL)
    {
      clib_warning ("Please specify face ID");
      return 1;
    }
  //Construct the API message
  M (HICN_API_FACE_IP_PARAMS_GET, mp);
  mp->faceid = clib_host_to_net_u32 (faceid);

  //send it...
  S (mp);

  //Wait for a reply...
  W (ret);

  return ret;
}

static void
  vl_api_hicn_api_face_ip_params_get_reply_t_handler
  (vl_api_hicn_api_face_ip_params_get_reply_t * rmp)
{
  vat_main_t *vam = hicn_test_main.vat_main;
  i32 retval = ntohl (rmp->retval);
  u8 *sbuf = 0;
  ip46_address_t remote_addr;
  ip46_address_t local_addr;

  if (vam->async_mode)
    {
      vam->async_errors += (retval < 0);
      return;
    }
  vam->retval = retval;
  vam->result_ready = 1;

  if (vam->retval < 0)
    {
      //vpp_api_test infra will also print out string form of error
      fformat (vam->ofp, "   (API call error: %d)\n", vam->retval);
      return;
    }
  vec_reset_length (sbuf);
  ip_address_decode (&rmp->local_addr, &local_addr);
  ip_address_decode (&rmp->remote_addr, &remote_addr);
  sbuf =
    format (0, "local_addr %U remote_addr %U", format_ip46_address,
	    &local_addr, 0 /*IP46_ANY_TYPE */ , format_ip46_address,
	    &remote_addr, 0 /*IP46_ANY_TYPE */ );

  fformat (vam->ofp, "%s swif %d flags %d\n",
	   sbuf,
	   clib_net_to_host_u32 (rmp->swif),
	   clib_net_to_host_i32 (rmp->flags));
}

static void
format_ip_face (vl_api_hicn_face_ip_t * rmp)
{
  vat_main_t *vam = hicn_test_main.vat_main;
  u8 *sbuf = 0;
  ip46_address_t remote_addr;
  ip46_address_t local_addr;

  vec_reset_length (sbuf);
  ip_address_decode (&rmp->local_addr, &local_addr);
  ip_address_decode (&rmp->remote_addr, &remote_addr);
  sbuf =
    format (0, "local_addr %U remote_addr %U", format_ip46_address,
	    &local_addr, 0 /*IP46_ANY_TYPE */ , format_ip46_address,
	    &remote_addr, 0 /*IP46_ANY_TYPE */ );

  fformat (vam->ofp, "%s swif %d flags %d name %s\n",
	   sbuf,
	   clib_net_to_host_u32 (rmp->swif),
	   clib_net_to_host_i32 (rmp->flags), rmp->if_name);
}

static void
format_udp_face (vl_api_hicn_face_udp_t * rmp)
{
  vat_main_t *vam = hicn_test_main.vat_main;
  u8 *sbuf = 0;
  ip46_address_t remote_addr;
  ip46_address_t local_addr;

  vec_reset_length (sbuf);
  ip_address_decode (&rmp->local_addr, &local_addr);
  ip_address_decode (&rmp->remote_addr, &remote_addr);
  u16 lport = clib_net_to_host_u16 (rmp->lport);
  u16 rport = clib_net_to_host_u16 (rmp->rport);;
  sbuf =
    format (0, "local_addr %U port %u remote_addr %U port %u",
	    format_ip46_address, &local_addr, 0 /*IP46_ANY_TYPE */ , lport,
	    format_ip46_address, &remote_addr, 0 /*IP46_ANY_TYPE */ , rport);

  fformat (vam->ofp, "%s swif %d flags %d name %s\n",
	   sbuf,
	   clib_net_to_host_u16 (rmp->swif),
	   clib_net_to_host_i32 (rmp->flags), rmp->if_name);
}

static int
api_hicn_api_faces_dump (vat_main_t * vam)
{
  hicn_test_main_t *hm = &hicn_test_main;
  vl_api_hicn_api_faces_dump_t *mp;
  vl_api_control_ping_t *mp_ping;
  int ret;

  if (vam->json_output)
    {
      clib_warning ("JSON output not supported for faces_dump");
      return -99;
    }

  M (HICN_API_FACES_DUMP, mp);
  S (mp);

  /* Use a control ping for synchronization */
  mp_ping = vl_msg_api_alloc_as_if_client (sizeof (*mp_ping));
  mp_ping->_vl_msg_id = htons (hm->ping_id);
  mp_ping->client_index = vam->my_client_index;

  fformat (vam->ofp, "Sending ping id=%d\n", hm->ping_id);

  vam->result_ready = 0;
  S (mp_ping);

  W (ret);
  return ret;
}

static void
  vl_api_hicn_api_faces_details_t_handler
  (vl_api_hicn_api_faces_details_t * mp)
{
  if (mp->type == IP_FACE)
    {
      format_ip_face (&(mp->face.ip));
    }
  else
    {
      format_udp_face (&(mp->face.udp));
    }
}

static int
api_hicn_api_face_get (vat_main_t * vam)
{
  unformat_input_t *input = vam->input;
  vl_api_hicn_api_face_get_t *mp;
  u32 faceid = HICN_FACE_NULL, ret;

  while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
    {
      if (unformat (input, "face %d", &faceid))
	{;
	}
      else
	{
	  break;
	}
    }

  //Check for presence of face ID
  if (faceid == HICN_FACE_NULL)
    {
      clib_warning ("Please specify face ID");
      return 1;
    }
  //Construct the API message
  M (HICN_API_FACE_GET, mp);
  mp->faceid = clib_host_to_net_u32 (faceid);

  //send it...
  S (mp);

  //Wait for a reply...
  W (ret);

  return ret;
}


static void
  vl_api_hicn_api_face_get_reply_t_handler
  (vl_api_hicn_api_face_get_reply_t * rmp)
{

  vat_main_t *vam = hicn_test_main.vat_main;
  i32 retval = ntohl (rmp->retval);

  if (vam->async_mode)
    {
      vam->async_errors += (retval < 0);
      return;
    }
  vam->retval = retval;
  vam->result_ready = 1;

  if (vam->retval < 0)
    {
      //vpp_api_test infra will also print out string form of error
      fformat (vam->ofp, "   (API call error: %d)\n", vam->retval);
      return;
    }

  if (rmp->type == IP_FACE)
    {
      format_ip_face (&(rmp->face.ip));
    }
  else
    {
      format_udp_face (&(rmp->face.udp));
    }
}



static int
api_hicn_api_face_stats_dump (vat_main_t * vam)
{
  hicn_test_main_t *hm = &hicn_test_main;
  vl_api_hicn_api_face_stats_dump_t *mp;
  vl_api_control_ping_t *mp_ping;
  int ret;

  if (vam->json_output)
    {
      clib_warning ("JSON output not supported for memif_dump");
      return -99;
    }

  M (HICN_API_FACE_STATS_DUMP, mp);
  S (mp);

  /* Use a control ping for synchronization */
  mp_ping = vl_msg_api_alloc_as_if_client (sizeof (*mp_ping));
  mp_ping->_vl_msg_id = htons (hm->ping_id);
  mp_ping->client_index = vam->my_client_index;

  fformat (vam->ofp, "Sending ping id=%d\n", hm->ping_id);

  vam->result_ready = 0;
  S (mp_ping);

  W (ret);
  return ret;
}

/* face_stats-details message handler */
static void
  vl_api_hicn_api_face_stats_details_t_handler
  (vl_api_hicn_api_face_stats_details_t * mp)
{
  vat_main_t *vam = hicn_test_main.vat_main;

  fformat (vam->ofp, "face id %d\n"
	   "    interest rx           packets %16Ld\n"
	   "                          bytes %16Ld\n"
	   "    interest tx           packets %16Ld\n"
	   "                          bytes %16Ld\n"
	   "    data rx               packets %16Ld\n"
	   "                          bytes %16Ld\n"
	   "    data tx               packets %16Ld\n"
	   "                          bytes %16Ld\n",
	   clib_host_to_net_u32 (mp->faceid),
	   clib_host_to_net_u64 (mp->irx_packets),
	   clib_host_to_net_u64 (mp->irx_bytes),
	   clib_host_to_net_u64 (mp->itx_packets),
	   clib_host_to_net_u64 (mp->itx_bytes),
	   clib_host_to_net_u64 (mp->drx_packets),
	   clib_host_to_net_u64 (mp->drx_bytes),
	   clib_host_to_net_u64 (mp->dtx_packets),
	   clib_host_to_net_u64 (mp->dtx_bytes));
}

static int
api_hicn_api_route_get (vat_main_t * vam)
{
  unformat_input_t *input = vam->input;

  vl_api_hicn_api_route_get_t *mp;
  fib_prefix_t prefix;
  int ret;

  while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
    {
      if (unformat (input, "prefix %U/%d", unformat_ip46_address,
		    &prefix.fp_addr, IP46_TYPE_ANY, &prefix.fp_len))
	{;
	}
      else
	{
	  break;
	}
    }

  /* Check parse */
  if (((prefix.fp_addr.as_u64[0] == 0) && (prefix.fp_addr.as_u64[1] == 0))
      || (prefix.fp_len == 0))
    {
      clib_warning ("Please specify a valid prefix...");
      return 1;
    }
  //Construct the API message
  M (HICN_API_ROUTE_GET, mp);
  if (!ip46_address_is_ip4 (&(prefix.fp_addr)))
    prefix.fp_proto = fib_proto_from_ip46 (IP46_TYPE_IP6);
  ip_prefix_encode (&prefix, &mp->prefix);

  //send it...
  S (mp);

  //Wait for a reply...
  W (ret);

  return ret;
}

static int
api_hicn_api_routes_dump (vat_main_t * vam)
{

  hicn_test_main_t *hm = &hicn_test_main;
  vl_api_hicn_api_route_get_t *mp;
  vl_api_control_ping_t *mp_ping;
  int ret;

  if (vam->json_output)
    {
      clib_warning ("JSON output not supported for routes_dump");
      return -99;
    }

  M (HICN_API_ROUTES_DUMP, mp);
  S (mp);

  /* Use a control ping for synchronization */
  mp_ping = vl_msg_api_alloc_as_if_client (sizeof (*mp_ping));
  mp_ping->_vl_msg_id = htons (hm->ping_id);
  mp_ping->client_index = vam->my_client_index;

  fformat (vam->ofp, "Sending ping id=%d\n", hm->ping_id);

  vam->result_ready = 0;
  S (mp_ping);

  W (ret);
  return ret;
}

static void
vl_api_hicn_api_route_get_reply_t_handler (vl_api_hicn_api_route_get_reply_t *
					   rmp)
{
  vat_main_t *vam = hicn_test_main.vat_main;
  i32 retval = ntohl (rmp->retval);
  u8 *sbuf = 0;

  if (vam->async_mode)
    {
      vam->async_errors += (retval < 0);
      return;
    }
  vam->retval = retval;
  vam->result_ready = 1;

  if (vam->retval < 0)
    {
      //vpp_api_test infra will also print out string form of error
      fformat (vam->ofp, "   (API call error: %d)\n", vam->retval);
      return;
    }
  int i = 0;
  u8 null_face = 0;
  u32 faceid;

  vec_reset_length (sbuf);
  sbuf = format (sbuf, "Faces: \n");
  while (i < 1000 && !null_face)
    {
      faceid = clib_net_to_host_u32 (rmp->faceids[i]);
      if (faceid != HICN_FACE_NULL)
	{
	  sbuf =
	    format (sbuf, "faceid %d",
		    clib_net_to_host_u32 (rmp->faceids[i]));
	  i++;
	}
      else
	{
	  null_face = 1;
	}
    }

  fformat (vam->ofp, "%s\n Strategy: %d\n",
	   sbuf, clib_net_to_host_u32 (rmp->strategy_id));
}

/* face_stats-details message handler */
static void
  vl_api_hicn_api_routes_details_t_handler
  (vl_api_hicn_api_routes_details_t * mp)
{
  vat_main_t *vam = hicn_test_main.vat_main;
  fib_prefix_t prefix;
  u32 faceid;
  u8 *sbuf = 0;
  vec_reset_length (sbuf);

  ip_prefix_decode (&mp->prefix, &prefix);
  sbuf =
    format (sbuf, "Prefix: %U/%u\n", format_ip46_address, &prefix.fp_addr, 0,
	    prefix.fp_len);

  sbuf = format (sbuf, "Faces: \n");
  for (int i = 0; i < mp->nfaces; i++)
    {
      faceid = clib_net_to_host_u32 (mp->faceids[i]);
      sbuf = format (sbuf, " faceid %d\n", faceid);
    }

  fformat (vam->ofp, "%sStrategy: %d\n",
	   sbuf, clib_net_to_host_u32 (mp->strategy_id));
}

static int
api_hicn_api_route_nhops_add (vat_main_t * vam)
{
  unformat_input_t *input = vam->input;
  vl_api_hicn_api_route_nhops_add_t *mp;

  fib_prefix_t prefix;
  u32 faceid = 0;
  int ret;


  while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
    {
      if (unformat (input, "add prefix %U/%d", unformat_ip46_address,
		    &prefix.fp_addr, IP46_TYPE_ANY, &prefix.fp_len))
	{;
	}
      else if (unformat (input, "face %d", &faceid))
	{;
	}
      else
	{
	  break;
	}
    }

  /* Check parse */
  if (((prefix.fp_addr.as_u64[0] == 0) && (prefix.fp_addr.as_u64[1] == 0))
      || (prefix.fp_len == 0) || (faceid == 0))
    {
      clib_warning ("Please specify prefix and faceid...");
      return 1;
    }
  /* Construct the API message */
  M (HICN_API_ROUTE_NHOPS_ADD, mp);
  ip_prefix_encode (&prefix, &mp->prefix);

  if (!ip46_address_is_ip4 (&(prefix.fp_addr)))
    prefix.fp_proto = fib_proto_from_ip46 (IP46_TYPE_IP6);

  mp->face_ids[0] = clib_host_to_net_u32 (faceid);
  mp->n_faces = 1;

  /* send it... */
  S (mp);

  /* Wait for a reply... */
  W (ret);

  return ret;
}

static int
api_hicn_api_route_del (vat_main_t * vam)
{
  unformat_input_t *input = vam->input;
  vl_api_hicn_api_route_del_t *mp;

  fib_prefix_t prefix;
  int ret;

  while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
    {
      if (unformat (input, "prefix %U/%d", unformat_ip46_address,
		    &prefix.fp_addr, IP46_TYPE_ANY, &prefix.fp_len))
	{;
	}
      else
	{
	  break;
	}
    }

  /* Check parse */
  if (((prefix.fp_addr.as_u64[0] == 0) && (prefix.fp_addr.as_u64[1] == 0))
      || (prefix.fp_len == 0))
    {
      clib_warning ("Please specify prefix...");
      return 1;
    }
  /* Construct the API message */
  M (HICN_API_ROUTE_DEL, mp);
  ip_prefix_encode (&prefix, &mp->prefix);

  if (!ip46_address_is_ip4 (&(prefix.fp_addr)))
    prefix.fp_proto = fib_proto_from_ip46 (IP46_TYPE_IP6);

  /* send it... */
  S (mp);

  /* Wait for a reply... */
  W (ret);

  return ret;

}

static int
api_hicn_api_route_nhop_del (vat_main_t * vam)
{
  unformat_input_t *input = vam->input;
  vl_api_hicn_api_route_nhop_del_t *mp;

  fib_prefix_t prefix;
  int faceid = 0, ret;

  while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
    {
      if (unformat (input, "del prefix %U/%d", unformat_ip46_address,
		    &prefix.fp_addr, IP46_TYPE_ANY, &prefix.fp_len))
	{;
	}
      else if (unformat (input, "face %d", &faceid))
	{;
	}
      else
	{
	  break;
	}
    }

  /* Check parse */
  if (((prefix.fp_addr.as_u64[0] == 0) && (prefix.fp_addr.as_u64[1] == 0))
      || (prefix.fp_len == 0) || (faceid == HICN_FACE_NULL))
    {
      clib_warning ("Please specify prefix and faceid...");
      return 1;
    }
  /* Construct the API message */
  M (HICN_API_ROUTE_NHOP_DEL, mp);
  ip_prefix_encode (&prefix, &mp->prefix);

  if (!ip46_address_is_ip4 (&(prefix.fp_addr)))
    prefix.fp_proto = fib_proto_from_ip46 (IP46_TYPE_IP6);

  mp->faceid = clib_host_to_net_u32 (faceid);

  /* send it... */
  S (mp);

  /* Wait for a reply... */
  W (ret);

  return ret;
}

static int
api_hicn_api_strategies_get (vat_main_t * vam)
{
  vl_api_hicn_api_strategies_get_t *mp;
  int ret;

  //TODO
  /* Construct the API message */
  M (HICN_API_STRATEGIES_GET, mp);

  /* send it... */
  S (mp);

  /* Wait for a reply... */
  W (ret);

  return ret;
}

static void
  vl_api_hicn_api_strategies_get_reply_t_handler
  (vl_api_hicn_api_strategies_get_reply_t * mp)
{
  vat_main_t *vam = hicn_test_main.vat_main;
  i32 retval = ntohl (mp->retval);
  u8 *sbuf = 0;

  if (vam->async_mode)
    {
      vam->async_errors += (retval < 0);
      return;
    }
  vam->retval = retval;
  vam->result_ready = 1;

  if (vam->retval < 0)
    {
      //vpp_api_test infra will also print out string form of error
      fformat (vam->ofp, "   (API call error: %d)\n", vam->retval);
      return;
    }
  int n_strategies = clib_net_to_host_i32 (mp->n_strategies);

  vec_reset_length (sbuf);
  sbuf = format (sbuf, "Available strategies:\n");

  int i;
  for (i = 0; i < n_strategies; i++)
    {
      u32 strategy_id = clib_net_to_host_u32 (mp->strategy_id[i]);
      sbuf = format (sbuf, "%d ", strategy_id);
    }
  fformat (vam->ofp, "%s", sbuf);
}

static int
api_hicn_api_strategy_get (vat_main_t * vam)
{
  unformat_input_t *input = vam->input;
  vl_api_hicn_api_strategy_get_t *mp;
  int ret;

  u32 strategy_id = HICN_STRATEGY_NULL;

  while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
    {
      if (unformat (input, "strategy %d", strategy_id))
	{;
	}
      else
	{
	  break;
	}
    }

  if (strategy_id == HICN_STRATEGY_NULL)
    {
      clib_warning ("Please specify strategy id...");
      return 1;
    }

  /* Construct the API message */
  M (HICN_API_STRATEGY_GET, mp);
  mp->strategy_id = clib_host_to_net_u32 (strategy_id);

  /* send it... */
  S (mp);

  /* Wait for a reply... */
  W (ret);

  return ret;
}

static void
  vl_api_hicn_api_strategy_get_reply_t_handler
  (vl_api_hicn_api_strategy_get_reply_t * mp)
{
  vat_main_t *vam = hicn_test_main.vat_main;
  i32 retval = ntohl (mp->retval);

  if (vam->async_mode)
    {
      vam->async_errors += (retval < 0);
      return;
    }
  vam->retval = retval;
  vam->result_ready = 1;

  if (vam->retval < 0)
    {
      //vpp_api_test infra will also print out string form of error
      fformat (vam->ofp, "   (API call error: %d)\n", vam->retval);
      return;
    }
  fformat (vam->ofp, "%s", mp->description);
}

static int
api_hicn_api_ip_punting_add (vat_main_t * vam)
{
  unformat_input_t *input = vam->input;
  vl_api_hicn_api_punting_add_t *mp;
  fib_prefix_t prefix;
  u32 swif = ~0;
  int ret;

  while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
    {
      if (unformat (input, "prefix %U/%d", unformat_ip46_address,
		    &prefix.fp_addr, IP46_TYPE_ANY, &prefix.fp_len))
	{;
	}
      else if (unformat (input, "intfc %d", &swif))
	{;
	}
      else
	{
	  break;
	}
    }

  /* Check parse */
  if (((prefix.fp_addr.as_u64[0] == 0) && (prefix.fp_addr.as_u64[1] == 0))
      || (prefix.fp_len == 0))
    {
      clib_warning ("Please specify prefix...");
      return 1;
    }

  if (swif == ~0)
    {
      clib_warning ("Please specify interface...");
      return 1;
    }
  /* Construct the API message */
  M (HICN_API_PUNTING_ADD, mp);
  mp->type = IP_PUNT;
  if (!ip46_address_is_ip4 (&(prefix.fp_addr)))
    {
      prefix.fp_proto = fib_proto_from_ip46 (IP46_TYPE_IP6);
    }
  ip_prefix_encode (&prefix, &mp->rule.ip.prefix);

  mp->rule.ip.swif = clib_host_to_net_u32 (swif);

  /* send it... */
  S (mp);

  /* Wait for a reply... */
  W (ret);

  return ret;
}

static int
api_hicn_api_udp_punting_add (vat_main_t * vam)
{
  unformat_input_t *input = vam->input;
  vl_api_hicn_api_punting_add_t *mp;
  fib_prefix_t prefix;
  u32 swif = ~0;
  u16 sport = 0;
  u16 dport = 0;
  vl_api_address_family_t ip_version = ~0;
  int ret;

  while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
    {
      if (unformat (input, "prefix %U/%d", unformat_ip46_address,
		    &prefix.fp_addr, IP46_TYPE_ANY, &prefix.fp_len))
	{;
	}
      else if (unformat (input, "sport %u", &sport));
      else if (unformat (input, "dport %u", &dport));
      else if (unformat (input, "ip4"))
	{
	  ip_version = ADDRESS_IP4;
	}
      else if (unformat (input, "ip6"))
	{
	  ip_version = ADDRESS_IP6;
	}
      else if (unformat (input, "intfc %d", &swif))
	{;
	}
      else
	{
	  break;
	}
    }

  /* Check parse */
  if (((prefix.fp_addr.as_u64[0] == 0) && (prefix.fp_addr.as_u64[1] == 0))
      || (prefix.fp_len == 0))
    {
      clib_warning ("Please specify prefix...");
      return 1;
    }

  if (swif == ~0)
    {
      clib_warning ("Please specify interface...");
      return 1;
    }
  if (ip_version == ~0)
    {
      clib_warning ("Please specify ip version of the udp tunnel...");
      return 1;
    }
  /* Construct the API message */
  M (HICN_API_PUNTING_ADD, mp);
  mp->type = UDP_PUNT;
  if (!ip46_address_is_ip4 (&(prefix.fp_addr)))
    {
      prefix.fp_proto = fib_proto_from_ip46 (IP46_TYPE_IP6);
    }
  ip_prefix_encode (&prefix, &mp->rule.ip.prefix);

  mp->rule.udp.ip_version = ip_version;

  mp->rule.udp.swif = clib_host_to_net_u32 (swif);
  mp->rule.udp.sport = clib_host_to_net_u16 (sport);
  mp->rule.udp.sport = clib_host_to_net_u16 (dport);

  /* send it... */
  S (mp);

  /* Wait for a reply... */
  W (ret);

  return ret;
}


static int
api_hicn_api_register_prod_app (vat_main_t * vam)
{
  unformat_input_t *input = vam->input;
  vl_api_hicn_api_register_prod_app_t *mp;
  fib_prefix_t prefix;
  u32 swif = ~0;
  int ret;

  while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
    {
      if (unformat (input, "prefix %U/%d", unformat_ip46_address,
		    &prefix.fp_addr, IP46_TYPE_ANY, &prefix.fp_len))
	{;
	}
      else if (unformat (input, "id %d", &swif))
	{;
	}
      else
	{
	  break;
	}
    }

  /* Check parse */
  if (((prefix.fp_addr.as_u64[0] == 0) && (prefix.fp_addr.as_u64[1] == 0))
      || (prefix.fp_len == 0))
    {
      clib_warning ("Please specify prefix...");
      return 1;
    }

  prefix.fp_proto =
    ip46_address_is_ip4 (&(prefix.fp_addr)) ? FIB_PROTOCOL_IP4 :
    FIB_PROTOCOL_IP6;
  /* Construct the API message */
  M (HICN_API_REGISTER_PROD_APP, mp);
  ip_prefix_encode (&prefix, &mp->prefix);

  mp->swif = clib_host_to_net_u32 (swif);

  /* send it... */
  S (mp);

  /* Wait for a reply... */
  W (ret);

  return ret;
}

static void
  vl_api_hicn_api_register_prod_app_reply_t_handler
  (vl_api_hicn_api_register_prod_app_reply_t * mp)
{
  vat_main_t *vam = hicn_test_main.vat_main;
  i32 retval = ntohl (mp->retval);

  if (vam->async_mode)
    {
      vam->async_errors += (retval < 0);
      return;
    }
  vam->retval = retval;
  vam->result_ready = 1;

  if (vam->retval < 0)
    {
      //vpp_api_test infra will also print out string form of error
      fformat (vam->ofp, "   (API call error: %d)\n", vam->retval);
      return;
    }
}

static int
api_hicn_api_face_prod_del (vat_main_t * vam)
{
  unformat_input_t *input = vam->input;
  vl_api_hicn_api_face_prod_del_t *mp;
  u32 faceid = 0, ret;

  while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
    {
      if (unformat (input, "face %d", &faceid))
	{;
	}
      else
	{
	  break;
	}
    }

  //Check for presence of face ID
  if (faceid == ~0)
    {
      clib_warning ("Please specify face ID");
      return 1;
    }
  //Construct the API message
  M (HICN_API_FACE_PROD_DEL, mp);
  mp->faceid = clib_host_to_net_u32 (faceid);

  //send it...
  S (mp);

  //Wait for a reply...
  W (ret);

  return ret;
}

static int
api_hicn_api_register_cons_app (vat_main_t * vam)
{
  vl_api_hicn_api_register_cons_app_t *mp;
  int ret;

  /* Construct the API message */
  M (HICN_API_REGISTER_CONS_APP, mp);

  /* send it... */
  S (mp);

  /* Wait for a reply... */
  W (ret);

  return ret;
}

static int
api_hicn_api_face_cons_del (vat_main_t * vam)
{
  unformat_input_t *input = vam->input;
  vl_api_hicn_api_face_cons_del_t *mp;
  u32 faceid = 0, ret;

  while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
    {
      if (unformat (input, "face %d", &faceid))
	{;
	}
      else
	{
	  break;
	}
    }

  //Check for presence of face ID
  if (faceid == ~0)
    {
      clib_warning ("Please specify face ID");
      return 1;
    }
  //Construct the API message
  M (HICN_API_FACE_CONS_DEL, mp);
  mp->faceid = clib_host_to_net_u32 (faceid);

  //send it...
  S (mp);

  //Wait for a reply...
  W (ret);

  return ret;
}

static void
  vl_api_hicn_api_register_cons_app_reply_t_handler
  (vl_api_hicn_api_register_cons_app_reply_t * mp)
{
  vat_main_t *vam = hicn_test_main.vat_main;
  i32 retval = ntohl (mp->retval);

  if (vam->async_mode)
    {
      vam->async_errors += (retval < 0);
      return;
    }
  vam->retval = retval;
  vam->result_ready = 1;

  if (vam->retval < 0)
    {
      //vpp_api_test infra will also print out string form of error
      fformat (vam->ofp, "   (API call error: %d)\n", vam->retval);
      return;
    }
  ip46_address_t src_addr4 = ip46_address_initializer;
  ip46_address_t src_addr6 = ip46_address_initializer;
  ip_address_decode (&mp->src_addr4, &src_addr4);
  ip_address_decode (&mp->src_addr6, &src_addr6);

  fformat (vam->ofp,
	   "ip4 address %U\n"
	   "ip6 address :%U\n"
	   "appif id :%d\n",
	   format_ip46_address, IP46_TYPE_ANY, &src_addr4,
	   format_ip46_address, IP46_TYPE_ANY, &src_addr6);
}

/*
 * List of messages that the api test plugin sends, and that the data plane
 * plugin processes
 */
#define foreach_vpe_api_msg                                             \
_(hicn_api_node_params_set, "PIT size <sz> CS size <sz>"                \
  "PIT minlimit <f> PIT maxlimit <f> [disable] ")                       \
_(hicn_api_node_params_get, "")                                         \
_(hicn_api_node_stats_get, "")                                          \
_(hicn_api_face_ip_del, "face <faceID>")                                \
_(hicn_api_face_ip_add, "local <address> remote <address> intfc <swif>")\
_(hicn_api_face_udp_add, "local <address> port <port> remote <address> port <port> intfc <swif>") \
_(hicn_api_face_del, "face <faceID>")                                   \
_(hicn_api_faces_dump, "")                                              \
_(hicn_api_face_get, "face <faceID>")                                   \
_(hicn_api_face_stats_dump, "")                                         \
_(hicn_api_route_nhops_add, "add prefix <IP4/IP6>/<subnet> face <faceID> weight <weight>") \
_(hicn_api_face_ip_params_get, "face <faceID>")                         \
_(hicn_api_route_get, "prefix <IP4/IP6>/<subnet>")                      \
_(hicn_api_routes_dump, "")                                             \
_(hicn_api_route_del, "prefix <IP4/IP6>/<subnet>")                      \
_(hicn_api_route_nhop_del, "del prefix <IP4/IP6>/<subnet> face <faceID>") \
_(hicn_api_strategies_get, "")                                          \
_(hicn_api_strategy_get, "strategy <id>")                               \
_(hicn_api_ip_punting_add, "prefix <IP4/IP6>/<subnet> intfc <swif>")    \
_(hicn_api_udp_punting_add, "prefix <IP4/IP6>/<subnet> intfc <swif> sport <port> dport <port> ip4/ip6")  \
_(hicn_api_register_prod_app, "prefix <IP4/IP6>/<subnet> id <appif_id>") \
_(hicn_api_face_prod_del, "face <faceID>")                              \
_(hicn_api_register_cons_app, "")                                       \
_(hicn_api_face_cons_del, "face <faceID>")

void
hicn_vat_api_hookup (vat_main_t * vam)
{
  hicn_test_main_t *sm = &hicn_test_main;
  /* Hook up handlers for replies from the data plane plug-in */
#define _(N, n)                                                  \
  vl_msg_api_set_handlers((VL_API_##N + sm->msg_id_base),        \
                          #n,                                    \
                          vl_api_##n##_t_handler,                \
                          vl_noop_handler,                       \
                          vl_api_##n##_t_endian,                 \
                          vl_api_##n##_t_print,                  \
                          sizeof(vl_api_##n##_t), 1);
  foreach_vpe_api_reply_msg;
#undef _

  /* API messages we can send */
#define _(n, h) hash_set_mem (vam->function_by_name, #n, api_##n);
  foreach_vpe_api_msg;
#undef _

  /* Help strings */
#define _(n, h) hash_set_mem (vam->help_by_name, #n, h);
  foreach_vpe_api_msg;
#undef _
}

clib_error_t *
vat_plugin_register (vat_main_t * vam)
{
  hicn_test_main_t *sm = &hicn_test_main;
  u8 *name;

  sm->vat_main = vam;

  /* Ask the vpp engine for the first assigned message-id */
  name = format (0, "hicn_%08x%c", api_version, 0);
  sm->msg_id_base = vl_client_get_first_plugin_msg_id ((char *) name);

  /* Get the control ping ID */
#define _(id,n,crc)                                                     \
  const char *id ## _CRC __attribute__ ((unused)) = #n "_" #crc;
  foreach_vl_msg_name_crc_vpe;
#undef _
  sm->ping_id = vl_msg_api_get_msg_index ((u8 *) (VL_API_CONTROL_PING_CRC));

  if (sm->msg_id_base != (u16) ~ 0)
    hicn_vat_api_hookup (vam);

  vec_free (name);

  return 0;
}

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