// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*-
// vim:set sts=4 ts=8 sw=4:

// Copyright (c) 2001-2009 XORP, Inc.
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License, Version 2, June
// 1991 as published by the Free Software Foundation. Redistribution
// and/or modification of this program under the terms of any other
// version of the GNU General Public License is not permitted.
// 
// This program is distributed in the hope that it will be useful, but
// WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details,
// see the GNU General Public License, Version 2, a copy of which can be
// found in the XORP LICENSE.gpl file.
// 
// XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA;
// http://xorp.net

// $XORP: xorp/contrib/olsr/olsr_types.hh,v 1.5 2008/10/02 21:56:35 bms Exp $

#ifndef __OLSR_OLSR_TYPES_HH__
#define __OLSR_OLSR_TYPES_HH__

/**
 * @short A scope to hold primitive types specific to OLSR.
 *
 * These are named exactly as they are in RFC 3626, to avoid
 * any confusion.
 */
struct OlsrTypes {
    /**
     * @short The default static cost of an OLSR interface.
     */
    static const int DEFAULT_STATIC_FACE_COST = 0;

    /**
     * @short The maximum time-to-live of an OLSR message.
     */
    static const int MAX_TTL = 255;

    /*
     * Constants from Section 18.2.
     */

    /**
     * @short The default interval between HELLO advertisements, in seconds.
     */
    static const int DEFAULT_HELLO_INTERVAL = 2;

    /**
     * @short The default interval between link advertisements, in seconds.
     * Used in situations where HELLO advertisements are segmented.
     */
    static const int DEFAULT_REFRESH_INTERVAL = 2;

    /**
     * @short The default interval between TC broadcasts, in seconds.
     */
    static const int DEFAULT_TC_INTERVAL = 5;

    /**
     * @short The default lifetime for each histogram-based duplicate
     * message detection entry.
     */
    static const int DEFAULT_DUP_HOLD_TIME = 30;

    /**
     * @short The default interval between MID broadcasts, in seconds.
     */
    static const int DEFAULT_MID_INTERVAL = DEFAULT_TC_INTERVAL;

    /**
     * @short The default interval between HNA broadcasts, in seconds.
     */
    static const int DEFAULT_HNA_INTERVAL = DEFAULT_TC_INTERVAL;

    /**
     * @short The type of an OLSR external route ID.
     */
    typedef uint32_t ExternalID;

    static const ExternalID UNUSED_EXTERNAL_ID = 0;

    /**
     * @short The type of an OLSR interface ID.
     */
    typedef uint32_t FaceID;

    static const FaceID UNUSED_FACE_ID = 0;

    /*
     * Defaults controlling link hysteresis defined in link.cc.
     * Used to derive link quality when RFC-compliant
     * link hysteresis is enabled.
     */

    /**
     * The default link hysteresis high threshold.
     */
    static double DEFAULT_HYST_THRESHOLD_HIGH;

    /**
     * The default link hysteresis low threshold.
     */
    static double DEFAULT_HYST_THRESHOLD_LOW;

    /**
     * The default link hysteresis scaling factor.
     */
    static double DEFAULT_HYST_SCALING;

    /**
     * @short The type of an OLSR link, as found in a HELLO message.
     */
    typedef uint8_t LinkType;

    enum LinkTypes {
	UNSPEC_LINK = 0,
	ASYM_LINK = 1,
	SYM_LINK = 2,
	LOST_LINK = 3,
	// May be used as a vector or array index.
	LINKTYPE_START = UNSPEC_LINK,
	LINKTYPE_END = LOST_LINK
    };
    static const int NUM_LINKTYPE =
	LINKTYPE_END - LINKTYPE_START + 1;

    /**
     * @short The type of an OLSR link ID.
     */
    typedef uint32_t LogicalLinkID;

    static const LogicalLinkID UNUSED_LINK_ID = 0;

    /**
     * @short The type of an OLSR protocol message.
     */
    typedef uint8_t MessageType;

    enum MessageTypes {
	HELLO_MESSAGE = 1,
	TC_MESSAGE = 2,
	MID_MESSAGE = 3,
	HNA_MESSAGE = 4,
	LQ_HELLO_MESSAGE = 201,	    // HELLO with ETX measurements.
	LQ_TC_MESSAGE = 202	    // TC with ETX measurements.
    };

    /**
     * @short The type of a multiple interface descriptor (MID) ID.
     */
    typedef uint32_t MidEntryID;

    static const MidEntryID UNUSED_MID_ID = 0;

    /**
     * @short The type of an OLSR neighbor ID.
     */
    typedef uint32_t NeighborID;

    static const NeighborID UNUSED_NEIGHBOR_ID = 0;

    /**
     * @short The type of an OLSR neighbor, as found in a HELLO message.
     */
    typedef uint8_t NeighborType;

    enum NeighborTypes {
	NOT_NEIGH = 0,
	SYM_NEIGH = 1,
	MPR_NEIGH = 2,
	// May be used as a vector or array index.
	NEIGHBORTYPE_START = NOT_NEIGH,
	NEIGHBORTYPE_END = MPR_NEIGH,
    };
    static const int NUM_NEIGHBORTYPE =
	NEIGHBORTYPE_END - NEIGHBORTYPE_START + 1;

    /**
     * @short The default UDP port for the OLSR protocol.
     */
    static const uint16_t DEFAULT_OLSR_PORT = 698;

    /**
     * @short Type representing a node's willingness to forward.
     */
    typedef uint8_t WillType;

    enum Willingness {
	WILL_NEVER = 0,
	WILL_LOW = 1,
	WILL_DEFAULT = 3,
	WILL_HIGH = 6,
	WILL_ALWAYS = 7,
	// limits
	WILL_MIN = WILL_LOW,
	WILL_MAX = WILL_ALWAYS,
	// bounds
	WILL_START = WILL_NEVER,
	WILL_END = WILL_ALWAYS
    };

    /**
     * @short Type representing a node's TC redundancy mode.
     * Section 15.1: TC_REDUNDANCY parameter.
     */
    typedef uint8_t TcRedundancyType;

    enum TcRedundancyMode {
	TCR_MPRS_IN = 0,	// MPR selectors only
	TCR_MPRS_INOUT = 1,	// MPR selectors and MPRs
	TCR_ALL = 2,		// The full neighbor set.
	// bounds
	TCR_START = TCR_MPRS_IN,
	TCR_END = TCR_ALL
    };

    /**
     * @short The default TC_REDUNDANCY value.
     */
    static const uint8_t DEFAULT_TC_REDUNDANCY = TCR_MPRS_IN;

    /**
     * @short The default number of MPRs which must cover a two-hop
     * neighbor when the OLSR MPR selection algorithm is enabled.
     */
    static const uint8_t DEFAULT_MPR_COVERAGE = 1;

    /**
     * @short The type of an OLSR topology set entry ID.
     */
    typedef uint32_t TopologyID;

    static const TopologyID UNUSED_TOPOLOGY_ID = 0;

    /**
     * @short The type of an OLSR two-hop node ID.
     */
    typedef uint32_t TwoHopNodeID;

    static const TwoHopNodeID UNUSED_TWOHOP_NODE_ID = 0;

    /**
     * @short The type of an OLSR two-hop link ID.
     */
    typedef uint32_t TwoHopLinkID;

    static const TwoHopLinkID UNUSED_TWOHOP_LINK_ID = 0;

    /**
     * @short Type representing the type of a Vertex in the
     * shortest-path-tree.
     *
     * SPT is a meta-class which embeds the Vertex as a POD type,
     * therefore, inheritance cannot be used to represent the vertex type.
     */
    enum VertexType {
	VT_ORIGINATOR = 0,	// iff origin() is true
	VT_UNKNOWN = 0,
	VT_NEIGHBOR = 1,	// Nodes at radius 1.
	VT_TWOHOP = 2,		// Nodes at radius 2.
	VT_TOPOLOGY = 3,
	VT_MID = 4,		// Not a vertex type; used by routing table
	VT_HNA = 5,		// do.
	// bounds
	VT_START = VT_ORIGINATOR,
	VT_END = VT_HNA
    };

    /**
     * @short Type fields used when saving OLSR databases.
     */
    enum TlvType {
	TLV_VERSION = 1,	// Version number (u32)
	TLV_SYSTEM_INFO = 2,	// Where created (string)
	TLV_OLSR_VERSION = 3,	// Version of OLSR in use (u32)
	TLV_OLSR_FAMILY = 4,	// Protocol family (u32)
	TLV_OLSR_N1 = 5,	// One-hop neighbor
	TLV_OLSR_L1 = 6,	// One-hop link
	TLV_OLSR_N2 = 7,	// Two-hop neighbor
	TLV_OLSR_L2 = 8,	// Two-hop link
	TLV_OLSR_MID = 9,	// MID entry
	TLV_OLSR_TC = 10,	// Topology Control entry
	TLV_OLSR_HNA = 11,	// HNA entry
    };
};

/**
 * @return true if the given vertex type maps directly to a single
 * OLSR node.
 */
inline bool
is_node_vertex(const OlsrTypes::VertexType vt)
{
    if (vt == OlsrTypes::VT_NEIGHBOR || vt == OlsrTypes::VT_TWOHOP ||
	vt == OlsrTypes::VT_TOPOLOGY || vt == OlsrTypes::VT_MID)
	return true;
    return false;
}

/**
 * @short Helper class to encode a TimeVal as an 8 bit binary
 * floating point value.
 */
class EightBitTime {
public:
	/**
	 * @short Helper function to convert from a TimeVal
	 * to the eight-bit floating point format used by OLSR.
	 */
	static inline TimeVal to_timeval(const uint8_t byte) {
		unsigned int mant = byte >> 4;
		unsigned int exp = byte & 0x0F;
		double sec = ((16 + mant) << exp) * _scaling_factor / 16.0;
		return (TimeVal(sec));
	}

	/**
	 * @short Helper function to convert the eight-bit floating point
	 * format used by OLSR to a TimeVal.
	 */
	static inline uint8_t from_timeval(const TimeVal& tv) {
		double sec = tv.get_double();
		int isec = static_cast<int>(sec / _scaling_factor);
		int mant = 0;
		int exp = 0;
		while (isec >= (1 << exp))
			exp++;
		if (exp == 0) {
			mant = 1;
		} else {
			exp--;
			if (mant > (_mod - 1)) {
				// ceiling
				mant = exp = (_mod - 1);
			} else {
				mant = static_cast<int>(_mod * sec /
				    _scaling_factor / (1 << exp) - _mod);
				exp += mant >> 4;
				mant &= 0x0F;
			}
		}
		return (static_cast<uint8_t>(mant << 4 | (exp & 0x0F)));
	}

private:
	static const int	_mod = 16;
	static const double	_scaling_factor;
};

/**
 * Helper function to compare two sequence numbers.
 * RFC 3626 Section 19: Sequence Numbers.
 *
 * @param seq1 An OLSR sequence number
 * @param seq2 An OLSR sequence number
 * @return true if seq1 is newer than seq2.
 */
inline bool
is_seq_newer(const uint16_t seq1, const uint16_t seq2)
{
    // UINT16_MAX is defined in C99 stdint.h, however this is not
    // part of the C++ Standard yet.
    static const uint16_t uint16_max = 65535;
    return  (seq1 > seq2 && seq1 - seq2 <= uint16_max/2) ||
	    (seq2 > seq1 && seq2 - seq1 > uint16_max/2);
}

/**
 * @short Return a C string describing a topology control flooding mode.
 */
const char* tcr_to_str(const OlsrTypes::TcRedundancyType t);

/**
 * @short Return a C string describing a willingness-to-forward mode.
 */
const char* will_to_str(const OlsrTypes::WillType t);

/**
 * @return a C string describing a vertex type.
 */
const char* vt_to_str(const OlsrTypes::VertexType vt);

#endif // __OLSR_OLSR_TYPES_HH__
