/*
 * Copyright (c) 2007 University of Washington
 *
 * SPDX-License-Identifier: GPL-2.0-only
 */

#include "queue.h"

#include "ns3/abort.h"
#include "ns3/enum.h"
#include "ns3/trace-source-accessor.h"
#include "ns3/uinteger.h"

namespace ns3
{

NS_LOG_COMPONENT_DEFINE("Queue");

NS_OBJECT_ENSURE_REGISTERED(QueueBase);
NS_OBJECT_TEMPLATE_CLASS_DEFINE(Queue, Packet);
NS_OBJECT_TEMPLATE_CLASS_DEFINE(Queue, QueueDiscItem);

TypeId
QueueBase::GetTypeId()
{
    static TypeId tid = TypeId("ns3::QueueBase")
                            .SetParent<Object>()
                            .SetGroupName("Network")
                            .AddTraceSource("PacketsInQueue",
                                            "Number of packets currently stored in the queue",
                                            MakeTraceSourceAccessor(&QueueBase::m_nPackets),
                                            "ns3::TracedValueCallback::Uint32")
                            .AddTraceSource("BytesInQueue",
                                            "Number of bytes currently stored in the queue",
                                            MakeTraceSourceAccessor(&QueueBase::m_nBytes),
                                            "ns3::TracedValueCallback::Uint32");
    return tid;
}

QueueBase::QueueBase()
    : m_nBytes(0),
      m_nTotalReceivedBytes(0),
      m_nPackets(0),
      m_nTotalReceivedPackets(0),
      m_nTotalDroppedBytes(0),
      m_nTotalDroppedBytesBeforeEnqueue(0),
      m_nTotalDroppedBytesAfterDequeue(0),
      m_nTotalDroppedPackets(0),
      m_nTotalDroppedPacketsBeforeEnqueue(0),
      m_nTotalDroppedPacketsAfterDequeue(0)
{
    NS_LOG_FUNCTION(this);
    m_maxSize = QueueSize(QueueSizeUnit::PACKETS, std::numeric_limits<uint32_t>::max());
}

QueueBase::~QueueBase()
{
    NS_LOG_FUNCTION(this);
}

void
QueueBase::AppendItemTypeIfNotPresent(std::string& typeId, const std::string& itemType)
{
    if (typeId.back() != '>')
    {
        typeId.append("<" + itemType + ">");
    }
}

bool
QueueBase::IsEmpty() const
{
    NS_LOG_FUNCTION(this);
    NS_LOG_LOGIC("returns " << (m_nPackets.Get() == 0));
    return m_nPackets.Get() == 0;
}

uint32_t
QueueBase::GetNPackets() const
{
    NS_LOG_FUNCTION(this);
    NS_LOG_LOGIC("returns " << m_nPackets);
    return m_nPackets;
}

uint32_t
QueueBase::GetNBytes() const
{
    NS_LOG_FUNCTION(this);
    NS_LOG_LOGIC(" returns " << m_nBytes);
    return m_nBytes;
}

QueueSize
QueueBase::GetCurrentSize() const
{
    NS_LOG_FUNCTION(this);

    if (m_maxSize.GetUnit() == QueueSizeUnit::PACKETS)
    {
        return QueueSize(QueueSizeUnit::PACKETS, m_nPackets);
    }
    if (m_maxSize.GetUnit() == QueueSizeUnit::BYTES)
    {
        return QueueSize(QueueSizeUnit::BYTES, m_nBytes);
    }
    NS_ABORT_MSG("Unknown queue size unit");
}

uint32_t
QueueBase::GetTotalReceivedBytes() const
{
    NS_LOG_FUNCTION(this);
    NS_LOG_LOGIC("returns " << m_nTotalReceivedBytes);
    return m_nTotalReceivedBytes;
}

uint32_t
QueueBase::GetTotalReceivedPackets() const
{
    NS_LOG_FUNCTION(this);
    NS_LOG_LOGIC("returns " << m_nTotalReceivedPackets);
    return m_nTotalReceivedPackets;
}

uint32_t
QueueBase::GetTotalDroppedBytes() const
{
    NS_LOG_FUNCTION(this);
    NS_LOG_LOGIC("returns " << m_nTotalDroppedBytes);
    return m_nTotalDroppedBytes;
}

uint32_t
QueueBase::GetTotalDroppedBytesBeforeEnqueue() const
{
    NS_LOG_FUNCTION(this);
    NS_LOG_LOGIC("returns " << m_nTotalDroppedBytesBeforeEnqueue);
    return m_nTotalDroppedBytesBeforeEnqueue;
}

uint32_t
QueueBase::GetTotalDroppedBytesAfterDequeue() const
{
    NS_LOG_FUNCTION(this);
    NS_LOG_LOGIC("returns " << m_nTotalDroppedBytesAfterDequeue);
    return m_nTotalDroppedBytesAfterDequeue;
}

uint32_t
QueueBase::GetTotalDroppedPackets() const
{
    NS_LOG_FUNCTION(this);
    NS_LOG_LOGIC("returns " << m_nTotalDroppedPackets);
    return m_nTotalDroppedPackets;
}

uint32_t
QueueBase::GetTotalDroppedPacketsBeforeEnqueue() const
{
    NS_LOG_FUNCTION(this);
    NS_LOG_LOGIC("returns " << m_nTotalDroppedPacketsBeforeEnqueue);
    return m_nTotalDroppedPacketsBeforeEnqueue;
}

uint32_t
QueueBase::GetTotalDroppedPacketsAfterDequeue() const
{
    NS_LOG_FUNCTION(this);
    NS_LOG_LOGIC("returns " << m_nTotalDroppedPacketsAfterDequeue);
    return m_nTotalDroppedPacketsAfterDequeue;
}

void
QueueBase::ResetStatistics()
{
    NS_LOG_FUNCTION(this);
    m_nTotalReceivedBytes = 0;
    m_nTotalReceivedPackets = 0;
    m_nTotalDroppedBytes = 0;
    m_nTotalDroppedBytesBeforeEnqueue = 0;
    m_nTotalDroppedBytesAfterDequeue = 0;
    m_nTotalDroppedPackets = 0;
    m_nTotalDroppedPacketsBeforeEnqueue = 0;
    m_nTotalDroppedPacketsAfterDequeue = 0;
}

void
QueueBase::SetMaxSize(QueueSize size)
{
    NS_LOG_FUNCTION(this << size);

    // do nothing if the size is null
    if (!size.GetValue())
    {
        return;
    }

    m_maxSize = size;

    NS_ABORT_MSG_IF(size < GetCurrentSize(),
                    "The new maximum queue size cannot be less than the current size");
}

QueueSize
QueueBase::GetMaxSize() const
{
    NS_LOG_FUNCTION(this);
    return m_maxSize;
}

bool
QueueBase::WouldOverflow(uint32_t nPackets, uint32_t nBytes) const
{
    if (m_maxSize.GetUnit() == QueueSizeUnit::PACKETS)
    {
        return (m_nPackets + nPackets > m_maxSize.GetValue());
    }
    else
    {
        return (m_nBytes + nBytes > m_maxSize.GetValue());
    }
}

} // namespace ns3
