#include "network/switch/shared_buffer.h"

namespace dofs {

SharedBuffer::SharedBuffer(Simulator* const sim,
                           NetworkSwitch* const owner,
                           Bytes total_bytes,
                           uint16_t ports)
        : SwitchBuffer(sim, owner, SwitchBufferType::SHARED, total_bytes, ports),
          _per_port_queues(ports),
          _used_total(0)
{}

std::array<std::deque<SwitchBuffer::Queued>, SwitchBuffer::PRI_COUNT>&SharedBuffer::queues_for(
        PortId p) {
        return _per_port_queues[p];
}

const std::array<std::deque<SwitchBuffer::Queued>, SwitchBuffer::PRI_COUNT>&SharedBuffer::queues_for(
        PortId p) const {
        return _per_port_queues[p];
}

bool SharedBuffer::on_enqueue_cap_check(PortId, Bytes sz) {
        if (_used_total + sz > _buffer_bytes)
                return false;

        return true;
}
void SharedBuffer::on_enqueue_commit(PortId port, Bytes sz) {
        _used_total += sz;
        _per_port_bytes[port] += sz;
}
void SharedBuffer::on_dequeue_commit(PortId port, Bytes sz) {
        _used_total -= sz;
        _per_port_bytes[port] -= sz;
}

bool SharedBuffer::enqueue_packet(const Packet& pkt, PortId egress,
                                  FlowPriority prio) {
        if (egress >= _port_cnt)
                return false;

        const Bytes sz = pkt.total_size();

        if (!on_enqueue_cap_check(egress, sz))
                return false;

        Queued q{pkt, egress, prio, _sim->now(), sz};
        auto& qset = queues_for(egress);
        qset[to_idx(prio)].push_back(std::move(q));
        on_enqueue_commit(egress, sz);

        schedule_drain_if_needed(egress);

        return true;
}

bool SharedBuffer::drain_one(PortId port) {
        return drain_one_common(port);
}

} // namespace dofs
