/*
 * Copyright (c) 2017 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 __VOM_PREFIX_H__
#define __VOM_PREFIX_H__

#include <boost/asio/ip/address.hpp>

#include "vom/enum_base.hpp"

namespace VOM {
/**
 * Types belonging to Routing
 */

/**
 * An L3 protocol can be used to construct a prefix that is used
 * to match packets are part of a route.
 */
class l3_proto_t : public enum_base<l3_proto_t>
{
public:
  const static l3_proto_t IPV4;
  const static l3_proto_t IPV6;
  const static l3_proto_t MPLS;

  bool is_ipv4();
  bool is_ipv6();

  static const l3_proto_t& from_address(const boost::asio::ip::address& addr);

private:
  /**
   * Private constructor taking the value and the string name
   */
  l3_proto_t(int v, const std::string& s);
};

/**
 * A next-hop protocol describes the protocol of a peer to which packets
 * are sent after matching a route.
 */
class nh_proto_t : public enum_base<nh_proto_t>
{
public:
  const static nh_proto_t IPV4;
  const static nh_proto_t IPV6;
  const static nh_proto_t MPLS;
  const static nh_proto_t ETHERNET;

  static const nh_proto_t& from_address(const boost::asio::ip::address& addr);

private:
  /**
   * Private constructor taking the value and the string name
   */
  nh_proto_t(int v, const std::string& s);
};

namespace route {
/**
 * type def the table-id
 */
typedef uint32_t table_id_t;

/**
 * The table-id for the default table
 */
const static table_id_t DEFAULT_TABLE = 0;

/**
 * A prefix defintion. Address + length
 */
class prefix_t
{
public:
  /**
   * Default Constructor - creates ::/0
   */
  prefix_t();
  /**
   * Constructor with address and length
   */
  prefix_t(const boost::asio::ip::address& addr, uint8_t len);
  /**
   * Constructor with just the address, this creates a
   * host prefix
   */
  prefix_t(const boost::asio::ip::address& addr);

  /**
   * Constructor with string and length
   */
  prefix_t(const std::string& s, uint8_t len);
  /**
   * Copy Constructor
   */
  prefix_t(const prefix_t&);
  /**
   * Constructor with VPP API prefix representation
   */
  prefix_t(uint8_t is_ip6, uint8_t* addr, uint8_t len);
  /**
   * Destructor
   */
  ~prefix_t();

  /**
   * Get the address
   */
  const boost::asio::ip::address& address() const;

  /**
   * Get the network mask width
   */
  uint8_t mask_width() const;

  /**
   * Assignement
   */
  prefix_t& operator=(const prefix_t&);

  /**
   * Less than operator
   */
  bool operator<(const prefix_t& o) const;

  /**
   * equals operator
   */
  bool operator==(const prefix_t& o) const;

  /**
   * not equal opartor
   */
  bool operator!=(const prefix_t& o) const;

  /**
   * convert to string format for debug purposes
   */
  std::string to_string() const;

  /**
   * The all Zeros prefix
   */
  const static prefix_t ZERO;

  /**
   * The all Zeros v6 prefix
   */
  const static prefix_t ZEROv6;

  /**
   * Convert the prefix into VPP API parameters
   */
  void to_vpp(uint8_t* is_ip6, uint8_t* addr, uint8_t* len) const;

  /**
   * Return a address representation of the mask, e.g. 255.255.0.0
   */
  boost::asio::ip::address_v4 mask() const;

  /**
   * get the lowest address in the prefix
   */
  boost::asio::ip::address_v4 low() const;

  /**
   * Get the highest address in the prefix
   */
  boost::asio::ip::address_v4 high() const;

  /**
   * Get the L3 protocol
   */
  l3_proto_t l3_proto() const;

private:
  /**
   * The address
   */
  boost::asio::ip::address m_addr;

  /**
   * The prefix length
   */
  uint8_t m_len;
};
};

boost::asio::ip::address_v4 operator|(const boost::asio::ip::address_v4& addr1,
                                      const boost::asio::ip::address_v4& addr2);

boost::asio::ip::address_v4 operator&(const boost::asio::ip::address_v4& addr1,
                                      const boost::asio::ip::address_v4& addr2);

boost::asio::ip::address_v4 operator~(const boost::asio::ip::address_v4& addr1);

/**
 * Ostream printer for prefix_t
 */
std::ostream& operator<<(std::ostream& os, const route::prefix_t& pfx);

/**
 * Convert a boost address into a VPP bytes string
 */
void to_bytes(const boost::asio::ip::address& addr,
              uint8_t* is_ip6,
              uint8_t* array);
void to_bytes(const boost::asio::ip::address_v4& addr, uint8_t* array);
void to_bytes(const boost::asio::ip::address_v6& addr, uint8_t* array);

/**
 * Get the prefix mask length of a host route from the boost address
 */
uint32_t mask_width(const boost::asio::ip::address& addr);

/**
 * Convert a VPP byte stinrg into a boost addresss
 */
boost::asio::ip::address from_bytes(uint8_t is_ip6, uint8_t* array);
};

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

#endif