#include "network/packet.h"

namespace dofs {

Packet::Packet(NodeId src_node,
               PortId src_port,
               NodeId dst_node,
               PortId dst_port,
               PacketProtocol proto,
               FlowPriority prio,
               PacketSeq seq,
               FlowId flow,
               uint16_t entropy,
               uint8_t notifications,
               Bytes payload_bytes) noexcept
        : _src_node(src_node),
          _src_port(src_port),
          _dst_node(dst_node),
          _dst_port(dst_port),
          _protocol(proto),
          _seq(seq),
          _flow(flow),
          _entropy(entropy),
          _payload_size((proto == PacketProtocol::DATA) ?
                        payload_bytes : Bytes{0}),
          _groups(0),
          _notifications(static_cast<uint8_t>(notifications & 0x07)) {
        const uint8_t p = static_cast<uint8_t>(prio) & 0x03;
        _notifications = static_cast<uint8_t>(
                                 (_notifications & ~PRIORITY_MASK) |
                                 (p << PRIORITY_SHIFT));
}

NodeId Packet::src_node() const noexcept {
        return _src_node;
}
PortId Packet::src_port() const noexcept {
        return _src_port;
}
NodeId Packet::dst_node() const noexcept {
        return _dst_node;
}
PortId Packet::dst_port() const noexcept {
        return _dst_port;
}
PacketProtocol Packet::protocol() const noexcept {
        return _protocol;
}
PacketSeq Packet::seq() const noexcept {
        return _seq;
}
FlowId Packet::flow_id() const noexcept {
        return _flow;
}
uint32_t Packet::entropy() const noexcept {
        return _entropy;
}

void Packet::set_src_node(NodeId n) noexcept {
        _src_node = n;
}
void Packet::set_src_port(PortId p) noexcept {
        _src_port = p;
}
void Packet::set_dst_node(NodeId n) noexcept {
        _dst_node = n;
}
void Packet::set_dst_port(PortId p) noexcept {
        _dst_port = p;
}
void Packet::set_seq(PacketSeq s) noexcept {
        _seq = s;
}
void Packet::set_flow_id(FlowId f) noexcept {
        _flow = f;
}
void Packet::set_entropy(uint32_t e) noexcept {
        _entropy = e;
}
void Packet::set_protocol(PacketProtocol p) noexcept {
        _protocol = p;
}

void Packet::set_payload_size(Bytes size) noexcept {
        _payload_size = size;
}

void Packet::set_ecn_enabled(bool v) noexcept {
        if (v)
                _notifications |= ECN_ENABLED_MASK;
        else
                _notifications &= ~ECN_ENABLED_MASK;
}
void Packet::set_ecn_marked(bool v) noexcept {
        if (v)
                _notifications |= ECN_MARK_MASK;
        else
                _notifications &= ~ECN_MARK_MASK;
}
void Packet::set_eof(bool v) noexcept {
        if (v)
                _notifications |= EOF_MASK;
        else
                _notifications &= ~EOF_MASK;
}

bool Packet::is_ecn_enabled() const noexcept {
        return (_notifications & ECN_ENABLED_MASK) != 0;
}

bool Packet::is_ecn() const noexcept {
        return _notifications & ECN_MARK_MASK;
}

bool Packet::is_eof() const noexcept {
        return (_notifications & EOF_MASK) != 0;
}

FlowPriority Packet::priority() const noexcept {
        const uint8_t pr = static_cast<uint8_t>(
                                   (_notifications & PRIORITY_MASK) >>
                                   PRIORITY_SHIFT);

        return static_cast<FlowPriority>(pr);
}

uint8_t Packet::priority_raw() const noexcept {
        return static_cast<uint8_t>((_notifications & PRIORITY_MASK) >>
                                    PRIORITY_SHIFT);
}

Bytes Packet::header_size() const noexcept {
        return HEADER_SIZE_BYTES;
}

Bytes Packet::payload_size() const noexcept {
        switch (_protocol) {
        case PacketProtocol::DATA:
                return _payload_size;

        case PacketProtocol::ACK:
        case PacketProtocol::NACK:
                return 0;

        case PacketProtocol::HEADER_TRIM:
        case PacketProtocol::HEADER_TRIM_BACK:
                return 0;
        }

        return 0;
}

Bytes Packet::total_size() const noexcept {
        switch (_protocol) {
        case PacketProtocol::DATA:
                return HEADER_SIZE_BYTES + _payload_size;

        case PacketProtocol::ACK:
        case PacketProtocol::NACK:
                return ACK_SIZE_BYTES;

        case PacketProtocol::HEADER_TRIM:
        case PacketProtocol::HEADER_TRIM_BACK:
                return HEADER_SIZE_BYTES;
        }

        return HEADER_SIZE_BYTES;
}

PacketGroups Packet::groups() const noexcept {
        return _groups;
}
void Packet::set_groups(PacketGroups g) noexcept {
        _groups = g;
}
void Packet::add_groups(PacketGroups gmask) noexcept {
        _groups |= gmask;
}

} // namespace dofs
