#ifndef NETWORK_NETWORK_SWITCH_H
#define NETWORK_NETWORK_SWITCH_H

#include <cstdint>
#include <vector>

#include "core/time.h"
#include "core/types.h"
#include "core/error.h"
#include "network/network_node.h"
#include "network/packet.h"
#include "network/switch/ecn_engine.h"
#include "network/switch/switch_buffer.h"
#include "network/switch/routing_tables.h"
#include "network/switch/routing_alg.h"

namespace dofs {

class NetworkSwitch final : public NetworkNode {
public:
        NetworkSwitch(Simulator *const sim,
                      NodeId id,
                      uint16_t total_ports,
                      ECNEngine *const ecn,
                      SwitchBuffer *const buf,
                      const RoutingTables *const rt,
                      Time forwarding_latency,
                      Time multicast_dup_delay) noexcept;

        virtual void recv_pkt(Packet &pkt, PortId ingress) override;

        // Status control:
        // - OK: restore forwarding latency to initial
        // - THROTTLING(new_latency): set _fwd_latency_cur = new_latency (>= initial)
        // - DOWN/BOOTING: ignored with error log
        void set_status(NodeStatus s, Time new_forward_latency = Time(0)) noexcept;

        NodeStatus get_status() const noexcept {
                return status();
        }

        uint16_t port_cnt() const noexcept {
                return _port_cnt;
        }

private:
        void forward_after_delay(Packet pkt, PortId ingress) noexcept;

        // Schedules an enqueue of a single copy after delay offset
        void enqueue_one(Packet pkt, PortId egress, FlowPriority prio) noexcept;

        // Build multicast fanout: pick exactly one tree per active group bit,
        // optionally enforcing RPF via parent_port, and union child ports.
        void build_mc_fanout(const Packet& pkt, PortId ingress,
                             std::vector<PortId> &fanout) const;

        // Build unicast egress list: if multiple candidates, ECMP-pick one via hash_ecmp.
        void build_uc_egress(const Packet& pkt, std::vector<PortId> &out) const;

        static void merge_sorted_unique(std::vector<PortId> &base,
                                        const std::vector<PortId> &add);

        // Test if group bit gid is set in PacketGroups
        static inline bool group_bit_set(PacketGroups mask, std::size_t gid) noexcept;

private:
        ECNEngine               *const _ecn;
        SwitchBuffer            *const _buf;
        const RoutingTables     *const _rt;

        const uint16_t _port_cnt;

        const Time _fwd_latency_init;
        Time       _fwd_latency_cur;

        const Time _dup_delay;
};

} // namespace dofs

#endif // NETWORK_NETWORK_SWITCH_H
