#include <rte_acl.h>
#include <getopt.h>
#include <string.h>

#ifndef RTE_LIBRTE_ACL_STANDALONE

#include <rte_cycles.h>
#include <rte_per_lcore.h>
#include <rte_lcore.h>
#include <rte_ip.h>

#define	PRINT_USAGE_START	"%s [EAL options]\n"

#else 

#define	RTE_LCORE_FOREACH_SLAVE(x)	while (((x) = 0))

#define	rte_eal_remote_launch(a, b, c)	DUMMY_MACRO
#define	rte_eal_mp_wait_lcore()		DUMMY_MACRO

#define	rte_eal_init(c, v)	(0)

#define	PRINT_USAGE_START	"%s\n"

#endif /*RTE_LIBRTE_ACL_STANDALONE */

#include "main.h"

/*
 * Rule and trace formats definitions.
 */

typedef struct ipv6_5tuple ipv6_5tuple;
struct ipv6_5tuple {
   uint8_t proto;         /* Protocol, next header. */
   uint32_t src_addr0;  /* IP address of source host. */
   uint32_t src_addr1;  /* IP address of source host. */
   uint32_t src_addr2;  /* IP address of source host. */
   uint32_t src_addr3;  /* IP address of source host. */
}; 

enum {
   PROTO_FIELD_IPV6,
   SRC_FIELD0_IPV6,
   SRC_FIELD1_IPV6,
   SRC_FIELD2_IPV6,
   SRC_FIELD3_IPV6,
   NUM_FIELDS_IPV6
};

struct rte_acl_field_def ipv6_2tuple_defs[NUM_FIELDS_IPV6] = {
   {
      .type = RTE_ACL_FIELD_TYPE_BITMASK,
      .size = sizeof (uint8_t),
      .field_index = PROTO_FIELD_IPV6,
      .input_index = PROTO_FIELD_IPV6,
      .offset = offsetof (ipv6_5tuple, proto),
   },
   {
      .type = RTE_ACL_FIELD_TYPE_BITMASK,
      .size = sizeof (uint8_t),
      .field_index = SRC_FIELD0_IPV6,
      .input_index = SRC_FIELD0_IPV6,
      .offset = offsetof (ipv6_5tuple, src_addr0),
   },
   {
      .type = RTE_ACL_FIELD_TYPE_BITMASK,
      .size = sizeof (uint8_t),
      .field_index = SRC_FIELD1_IPV6,
      .input_index = SRC_FIELD1_IPV6,
      .offset = offsetof (ipv6_5tuple, src_addr1),
   },
   {
      .type = RTE_ACL_FIELD_TYPE_BITMASK,
      .size = sizeof (uint8_t),
      .field_index = SRC_FIELD2_IPV6,
      .input_index = SRC_FIELD2_IPV6,
      .offset = offsetof (ipv6_5tuple, src_addr2),
   },
   {
      .type = RTE_ACL_FIELD_TYPE_BITMASK,
      .size = sizeof (uint8_t),
      .field_index = SRC_FIELD2_IPV6,
      .input_index = SRC_FIELD2_IPV6,
      .offset = offsetof (ipv6_5tuple, src_addr3),
   },
};

struct rte_acl_ctx *acx;
struct rte_acl_config cfg;
int ret;

/* define a structure for the rule with up to 5 fields. */


RTE_ACL_RULE_DEF(acl_ipv6_rule, RTE_DIM(ipv6_2tuple_defs));

/* AC context creation parameters. */

struct rte_acl_param prm = {
   .name = "ACL_example",
   .socket_id = SOCKET_ID_ANY,
   .rule_size = RTE_ACL_RULE_SZ(RTE_DIM(ipv6_2tuple_defs)),

   /* number of fields per rule. */
   .max_rule_num = 8, /* maximum number of rules in the AC context. */
};

struct acl_ipv6_rule acl6_rules[] = {
   {
      .data = {.userdata = 1, .category_mask = 1, .priority = 1},
      .field[0] = {.value.u8 = 0x33, .mask_range.u8 = 0xFF,},
   },
   {
      .data = {.userdata = 11, .category_mask = 1, .priority = 11},
      .field[0] = {.value.u8 = 0x3a, .mask_range.u8 = 0xFF,},
   },
   {
      .data = {.userdata = 3, .category_mask = 1, .priority = 255},
      .field[0] = {.value.u8 = 0xFF, .mask_range.u8 = 0xFF,},
      //.field[1] = {.value.u32 = 0xFFFFFFFF,. mask_range.u32 = 0xFFFFFFFF,},
      //.field[2] = {.value.u32 = 0xFFFFFFFF,. mask_range.u32 = 0xFFFFFFFF,},
      //.field[3] = {.value.u32 = 0xFFFFFFFF,. mask_range.u32 = 0xFFFFFFFF,},
      //.field[4] = {.value.u32 = 0xFFFFFFFF,. mask_range.u32 = 0xFFFFFFFF,},
   },
   {
      .data = {.userdata = 2, .category_mask = 1, .priority = 2},
      .field[0] = {.value.u8 = 0x35, .mask_range.u8 = 0xFF,},
   },
};

ipv6_5tuple data;

int MAIN(int argc, char **argv)
{
   int ret;
   int32_t rc;
   uint32_t res;
   const uint8_t *dp;

   data.proto = 0x3a;
   data.src_addr0 = 0x12345678;
   data.src_addr1 = 0xaabbccdd;
   data.src_addr2 = 0xaabbccdd;
   data.src_addr3 = 0xaabbccdd;
   dp = (const uint8_t *)&data;

   ret = rte_eal_init(argc, argv);
   if (ret < 0)
      rte_panic("Cannot init EAL\n");

   /* create an empty AC context  */

   if ((acx = rte_acl_create(&prm)) == NULL) {

      /* handle context create failure. */

   }

   /* add rules to the context */

   ret = rte_acl_add_rules(acx, (const struct rte_acl_rule *)acl6_rules, RTE_DIM(acl6_rules));
   if (ret != 0) {
      /* handle error at adding ACL rules. */
   }

   /* prepare AC build config. */

   cfg.num_categories = 1;
   cfg.num_fields = RTE_DIM(ipv6_2tuple_defs);

   memcpy(cfg.defs, ipv6_2tuple_defs, sizeof (ipv6_2tuple_defs));

   /* build the runtime structures for added rules, with 2 categories. */

   ret = rte_acl_build(acx, &cfg);
   if (ret != 0) {
      /* handle error at build runtime structures for ACL context. */
   }
   rc = rte_acl_classify(acx, &dp, &res, 1, 1);
   if(rc != 0)
   {
      printf("rte_acl_classify failed!!\n");
      exit(0);
   }

   printf("rte_acl_classify() returns %d\n, res[0]=%u\n", rc, res);

   return (0);
}
