/*
 * "Copyright (c) 2008 The Regents of the University  of California.
 * All rights reserved."
 *
 * Permission to use, copy, modify, and distribute this software and its
 * documentation for any purpose, without fee, and without written agreement is
 * hereby granted, provided that the above copyright notice, the following
 * two paragraphs and the author appear in all copies of this software.
 *
 * IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY FOR
 * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT
 * OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF THE UNIVERSITY OF
 * CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES,
 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
 * AND FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
 * ON AN "AS IS" BASIS, AND THE UNIVERSITY OF CALIFORNIA HAS NO OBLIGATION TO
 * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS."
 *
 */
#ifndef _IPDISPATCH_H_
#define _IPDISPATCH_H_

#include <message.h>
#include <lib6lowpan.h>
#include <Statistics.h>

enum {
  N_PARENTS = 3,
  N_EPOCHS = 2,
  N_EPOCHS_COUNTED = 1,
  N_RECONSTRUCTIONS = 2,
  N_FORWARD_ENT = IP_NUMBER_FRAGMENTS,
};

enum {
  CONF_EVICT_THRESHOLD = 5, // Neighbor is 'mature'
  CONF_PROM_THRESHOLD = 5, // Acceptable threshold for promotion
  MAX_CONSEC_FAILURES = 11, // Max Failures before reroute is attempted
  PATH_COST_DIFF_THRESH = 10, // Threshold for 'similar' path costs
  LQI_DIFF_THRESH = 10, // Threshold for 'similar' LQI's
  LINK_EVICT_THRESH = 50, // ETX * 10
  RANDOM_ROUTE = 20, //Percentage of time to select random default route
  LQI_ADMIT_THRESH = 0x200, 
};

/* chip-specific lqi values */
uint16_t adjustLQI(uint8_t val);


enum {
  WITHIN_THRESH = 1,
  ABOVE_THRESH = 2,
  BELOW_THRESH = 3,
};

#ifndef LOW_POWER_LISTENING
enum {
  TGEN_BASE_TIME = 512,
  TGEN_MAX_INTERVAL = 60L * 1024L * 5L,
};
#else
enum {
  TGEN_BASE_TIME = 16384L,
  TGEN_MAX_INTERVAL = 60L * 1024L * 5L,
};
#endif


struct epoch_stats {
  uint16_t success;
  uint16_t total;
  uint16_t receptions;
};

struct report_stats {
  uint8_t messages;
  uint8_t transmissions;
  uint8_t successes;
};

enum {
  T_PIN_OFFSET    = 0,
  T_PIN_MASK      = 1 << T_PIN_OFFSET,
  T_VALID_OFFSET  = 2,
  T_VALID_MASK    = 1 << T_VALID_OFFSET,
  T_MARKED_OFFSET = 3,
  T_MARKED_MASK   = 1 << T_MARKED_OFFSET,
  T_MATURE_OFFSET = 4,
  T_MATURE_MASK   = 1 << T_MATURE_OFFSET,
  T_EVICT_OFFSET  = 5,
  T_EVICT_MASK    = 1 << T_EVICT_OFFSET,
};

enum {
  // store the top-k neighbors.  This could be a poor topology
  // formation critera is very dense networks.  we may be able to
  // really use the fact that the "base" has infinite memory.
  N_NEIGH = 8,
  N_LOW_NEIGH = 2,
  N_FREE_NEIGH = (N_NEIGH - N_LOW_NEIGH),
  N_FLOW_ENT = 6,
  N_FLOW_CHOICES = 2,
  N_PARENT_CHOICES = 3,
  T_DEF_PARENT = 0xfffd,
  T_DEF_PARENT_SLOT = 0,
};

typedef struct {
  // The extra 2 is because one dest could be from source route, other
  //  from the dest being a direct neighbor
  ieee154_saddr_t dest[N_FLOW_CHOICES + N_PARENT_CHOICES + 2];
  uint8_t   current:4;
  uint8_t   nchoices:4;
  uint8_t   retries;
  uint8_t   actRetries;
  uint16_t  delay;
} send_policy_t;

typedef struct {
  send_policy_t policy;
  uint8_t frags_sent;
  bool failed;
  uint8_t refcount;
  uint8_t local_flow_label;
} send_info_t;

typedef struct {
  send_info_t *info;
  message_t  *msg;
} send_entry_t;

typedef struct {
  uint8_t timeout;
  ieee154_saddr_t l2_src;
  uint16_t old_tag;
  uint16_t new_tag;
  send_info_t *s_info;
} forward_entry_t;

/* typedef struct { */
/*   /\* how to dispatch this packet *\/ */
/*   union { */
/*     struct sockaddr_in6 sock; */
/*     ip6_addr_t src; */
/*   } address; */
/*   /\* packet metadata *\/ */
/*   union { */
/*     uint16_t udp_port; */
/*   } dispatch; */
/*   struct ip_metadata metadata; */

/*   /\* the lib6lowpan reconstruct structure *\/ */
/*   reconstruct_t recon; */
/* } ip_recon_t; */

enum {
  F_VALID_MASK = 0x01,
  //F_TOTAL_VALID_ENTRY_MASK = 0x80, // For entire entry (not just specific choice)
  F_FULL_PATH_OFFSET = 1,
  F_FULL_PATH_MASK = 0x02,

  MAX_PATH_LENGTH = 10,
  N_FULL_PATH_ENTRIES = (N_FLOW_CHOICES * N_FLOW_ENT),
};
  
struct flow_path {
  uint8_t path_len;
  cmpr_ip6_addr_t path[MAX_PATH_LENGTH];
};

struct f_entry {
  uint8_t flags;
  union {
    struct flow_path *pathE;
    cmpr_ip6_addr_t nextHop;
  };
};

// Need to add another entry to avoid useless padding
//  Or can make sure that the flow_table has an even
//  number of entries.
struct flow_entry {
  uint8_t flags;
  uint8_t count;
  struct flow_match match;
  struct f_entry entries[N_FLOW_CHOICES];
};

//#define IS_VALID_SLOT(f) (((f)->entries[0].flags & F_TOTAL_VALID_ENTRY_MASK) == F_TOTAL_VALID_ENTRY_MASK)
#define IS_VALID_SLOT(f) (((f)->flags & F_VALID_MASK) == F_VALID_MASK)
//#define SET_VALID_SLOT(f) (f)->entries[0].flags |= F_TOTAL_VALID_ENTRY_MASK
#define SET_VALID_SLOT(f) (f)->flags |= F_VALID_MASK
//#define SET_INVALID_SLOT(f) (f)->entries[0].flags &= ~F_TOTAL_VALID_ENTRY_MASK
#define SET_INVALID_SLOT(f) (f)->flags &= ~F_VALID_MASK
#define IS_VALID_ENTRY(e) (((e).flags & F_VALID_MASK) == F_VALID_MASK)
#define SET_VALID_ENTRY(e) (e).flags |= F_VALID_MASK
#define SET_INVALID_ENTRY(e) (e).flags &= ~F_VALID_MASK
#define IS_FULL_TYPE(e) (((e).flags & F_FULL_PATH_MASK) == F_FULL_PATH_MASK)
#define IS_HOP_TYPE(e) !IS_FULL_TYPE(e)
#define SET_FULL_TYPE(e) ((e).flags |= F_FULL_PATH_MASK)
#define SET_HOP_TYPE(e) ((e).flags &= ~F_FULL_PATH_MASK)
  

struct neigh_entry {
  uint8_t flags;
  uint8_t hops; // Put this before neighbor to remove potential padding issues
  ieee154_saddr_t neighbor;
  uint16_t costEstimate;
  uint16_t linkEstimate;
  struct epoch_stats stats[N_EPOCHS];
}
#ifdef MIG
 __attribute__((packed));
#else
;
#endif

#define IS_NEIGH_VALID(e) (((e)->flags & T_VALID_MASK) == T_VALID_MASK)
#define SET_NEIGH_VALID(e) ((e)->flags |= T_VALID_MASK)
#define SET_NEIGH_INVALID(e) ((e)->flags &= ~T_VALID_MASK)
#define PINNED(e) (((e)->flags & T_PIN_MASK) == T_PIN_MASK)
#define REMOVABLE(e) (((e)->refCount == 0) && !(PINNED(e)))
#define SET_PIN(e) (((e)->flags |= T_PIN_MASK))
#define UNSET_PIN(e) (((e)->flags &= ~T_PIN_MASK))
#define IS_MARKED(e) (((e)->flags & T_MARKED_MASK) == T_MARKED_MASK)
#define SET_MARK(e) (((e)->flags |= T_MARKED_MASK))
#define UNSET_MARK(e) (((e)->flags &= ~T_MARKED_MASK))
#define IS_MATURE(e) (((e)->flags & T_MATURE_MASK) == T_MATURE_MASK)
#define SET_MATURE(e) ((e)->flags |= T_MATURE_MASK)
#define SET_EVICT(e) ((e).flags |= T_EVICT_MASK)
#define UNSET_EVICT(e) ((e).flags &= ~T_EVICT_MASK)
#define SHOULD_EVICT(e) ((e).flags & T_EVICT_MASK)


typedef enum {
  S_FORWARD,
  S_REQ,
} send_type_t;


#endif
