//
// Copyright (c) 2006 Georgia Tech Research Corporation
//
// SPDX-License-Identifier: GPL-2.0-only
//
// Author: Rajib Bhattacharjea<raj.b@gatech.edu>
//

#ifndef DATA_RATE_H
#define DATA_RATE_H

#include "ns3/attribute-helper.h"
#include "ns3/attribute.h"
#include "ns3/nstime.h"

#include <iostream>
#include <stdint.h>
#include <string>

namespace ns3
{

/**
 * @ingroup network
 * @defgroup datarate Data Rate
 */
/**
 * @ingroup datarate
 * @brief Class for representing data rates
 *
 * Allows for natural and familiar use of data rates.  Allows construction
 * from strings, natural multiplication e.g.:
 * @code
 *   DataRate x("56kbps");
 *   double nBits = x*ns3::Seconds (19.2);
 *   uint32_t nBytes = 20;
 *   Time txtime = x.CalculateBytesTxTime (nBytes);
 * @endcode
 * This class also supports the regular comparison operators \c <, \c >,
 * \c <=, \c >=, \c ==, and \c !=
 *
 * Data rate specifiers consist of
 * * A numeric value,
 * * An optional multiplier prefix and
 * * A unit.
 *
 * Whitespace is allowed but not required between the numeric value and
 * multiplier or unit.
 *
 * Supported multiplier prefixes:
 *
 * | Prefix   | Value       |
 * | :------- | ----------: |
 * | "k", "K" | 1000        |
 * | "Ki"     | 1024        |
 * | "M"      | 1000000     |
 * | "Mi"     | 1024 Ki     |
 * | "G"      | 10^9        |
 * | "Gi "    | 1024 Mi     |
 *
 * Supported unit strings:
 *
 * | Symbol   | Meaning     |
 * | :------- | :---------- |
 * | "b"      | bits        |
 * | "B"      | 8-bit bytes |
 * | "s", "/s"| per second  |
 *
 * Examples:
 * * "56kbps" = 56,000 bits/s
 * * "128 kb/s" = 128,000 bits/s
 * * "8Kib/s" = 1 KiB/s = 8192 bits/s
 * * "1kB/s" = 8000 bits/s
 *
 * @see attribute_DataRate
 */
class DataRate
{
  public:
    DataRate();
    /**
     * @brief Integer constructor
     *
     * Construct a data rate from an integer.  This class only supports positive
     * integer data rates in units of bits/s, meaning 1bit/s is the smallest
     * non-trivial bitrate available.
     * @param bps bit/s value
     */
    DataRate(uint64_t bps);
    /**
     * @brief String constructor
     *
     * Construct a data rate from a string.  Many different unit strings are supported
     * Supported unit strings:
     * bps, b/s, Bps, B/s \n
     * kbps, kb/s, Kbps, Kb/s, kBps, kB/s, KBps, KB/s, Kib/s, KiB/s \n
     * Mbps, Mb/s, MBps, MB/s, Mib/s, MiB/s \n
     * Gbps, Gb/s, GBps, GB/s, Gib/s, GiB/s \n
     *
     * Examples:
     * "56kbps" = 56,000 bits/s \n
     * "128 kb/s" = 128,000 bits/s \n
     * "8Kib/s" = 1 KiB/s = 8192 bits/s \n
     * "1kB/s" = 8000 bits/s
     *
     * @param rate string representing the desired rate
     */
    DataRate(std::string rate);

    /**
     * @return the DataRate representing the sum of this object with rhs
     *
     * @param rhs the DataRate to add to this DataRate
     */
    DataRate operator+(DataRate rhs) const;

    /**
     * @return the DataRate representing the sum of this object with rhs
     *
     * @param rhs the DataRate to add to this DataRate
     */
    DataRate& operator+=(DataRate rhs);

    /**
     * @return the DataRate representing the difference of this object with rhs
     *
     * @param rhs the DataRate to subtract from this DataRate
     */
    DataRate operator-(DataRate rhs) const;

    /**
     * @return the DataRate representing the difference of this object with rhs
     *
     * @param rhs the DataRate to subtract from this DataRate
     */
    DataRate& operator-=(DataRate rhs);

    /**
     * @brief Scales the DataRate
     *
     * Multiplies with double and is re-casted to an int
     *
     * @return DataRate object representing the product of this object with rhs
     *
     * @param rhs the double to multiply to this datarate
     */
    DataRate operator*(double rhs) const;

    /**
     * @brief Scales the DataRate
     *
     * Multiplies with double and is re-casted to an int
     *
     * @return DataRate object representing the product of this object with rhs
     *
     * @param rhs the double to multiply to this datarate
     */
    DataRate& operator*=(double rhs);

    /**
     * @brief Scales the DataRate
     *
     * @return DataRate object representing the product of this object with rhs
     *
     * @param rhs the uint64_t to multiply to this datarate
     */
    DataRate operator*(uint64_t rhs) const;

    /**
     * @brief Scales the DataRate
     *
     * @return DataRate object representing the product of this object with rhs
     *
     * @param rhs the uint64_t to multiply to this datarate
     */
    DataRate& operator*=(uint64_t rhs);

    /**
     * Spaceship comparison operator. All the other comparison operators
     * are automatically generated from this one.
     *
     * @param rhs the datarate to compare to this datarate
     * @returns The result of the comparison.
     */
    auto operator<=>(const DataRate& rhs) const = default;

    /**
     * @brief Calculate transmission time
     *
     * Calculates the transmission time at this data rate
     * @param bytes The number of bytes (not bits) for which to calculate
     * @return The transmission time for the number of bytes specified
     */
    Time CalculateBytesTxTime(uint32_t bytes) const;

    /**
     * @brief Calculate transmission time
     *
     * Calculates the transmission time at this data rate
     * @param bits The number of bits (not bytes) for which to calculate
     * @return The transmission time for the number of bits specified
     */
    Time CalculateBitsTxTime(uint32_t bits) const;

    /**
     * Get the underlying bitrate
     * @return The underlying bitrate in bits per second
     */
    uint64_t GetBitRate() const;

  private:
    /**
     * @brief Parse a string representing a DataRate into an uint64_t
     *
     * Allowed unit representations include all combinations of
     *
     * * An SI prefix: k, K, M, G
     * * Decimal or kibibit (as in "Kibps", meaning 1024 bps)
     * * Bits or bytes (8 bits)
     * * "bps" or "/s"
     *
     * @param [in] s The string representation, including unit
     * @param [in,out] v The location to put the value, in bits/sec.
     * @return true if parsing was successful.
     */
    static bool DoParse(const std::string s, uint64_t* v);

    // Uses DoParse
    friend std::istream& operator>>(std::istream& is, DataRate& rate);

    uint64_t m_bps; //!< data rate [bps]
};

/**
 * @brief Stream insertion operator.
 *
 * @param os the stream
 * @param rate the data rate
 * @returns a reference to the stream
 */
std::ostream& operator<<(std::ostream& os, const DataRate& rate);

/**
 * @brief Stream extraction operator.
 *
 * @param is the stream
 * @param rate the data rate
 * @returns a reference to the stream
 */
std::istream& operator>>(std::istream& is, DataRate& rate);

ATTRIBUTE_HELPER_HEADER(DataRate);

/**
 * @brief Multiply datarate by a time value
 *
 * Calculates the number of bits that have been transmitted over a period of time
 * @param lhs rate
 * @param rhs time
 * @return the number of bits over the period of time
 */
double operator*(const DataRate& lhs, const Time& rhs);
/**
 * @brief Multiply time value by a data rate
 *
 * Calculates the number of bits that have been transmitted over a period of time
 * @param lhs time
 * @param rhs rate
 * @return the number of bits over the period of time
 */
double operator*(const Time& lhs, const DataRate& rhs);

namespace TracedValueCallback
{

/**
 * @ingroup network
 * TracedValue callback signature for DataRate
 *
 * @param [in] oldValue original value of the traced variable
 * @param [in] newValue new value of the traced variable
 */
typedef void (*DataRate)(DataRate oldValue, DataRate newValue);

} // namespace TracedValueCallback

} // namespace ns3

#endif /* DATA_RATE_H */
