# Copyright (C) 2013 Nippon Telegraph and Telephone Corporation.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#    http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
# implied.
# See the License for the specific language governing permissions and
# limitations under the License.


"""
Logical Link Control(LLC, IEEE 802.2) parser/serializer
http://standards.ieee.org/getieee802/download/802.2-1998.pdf


LLC format::

    +-----------------+--------------+
    | DSAP address    | 8 bits       |
    +-----------------+--------------+
    | SSAP address    | 8 bits       |
    +-----------------+--------------+
    | Control         | 8 or 16 bits |
    +-----------------+--------------+


DSAP address field::

      LSB
    +-----+---+---+---+---+---+---+---+
    | I/G | D | D | D | D | D | D | D |
    +-----+---+---+---+---+---+---+---+
     I/G bit = 0 : Individual DSAP
     I/G bit = 1 : Group DSA
     D : DSAP address

SSAP address field::

      LSB
    +-----+---+---+---+---+---+---+---+
    | C/R | S | S | S | S | S | S | S |
    +-----+---+---+---+---+---+---+---+
     C/R bit = 0 : Command
     C/R bit = 1 : Response
     S : SSAP address


Control field:

Information transfer
command/response
(I-format PDU)::

      1   2   3   4   5   6   7   8    9   10-16
    +---+---+---+---+---+---+---+---+-----+------+
    | 0 |           N(S)            | P/F | N(R) |
    +---+---+---+---+---+---+---+---+-----+------+

Supervisory
commands/responses
(S-format PDUs)::

      1   2   3   4   5   6   7   8    9   10-16
    +---+---+---+---+---+---+---+---+-----+------+
    | 1   0 | S   S | 0   0   0   0 | P/F | N(R) |
    +---+---+---+---+---+---+---+---+-----+------+

Unnumbered
commands/responses
(U-format PDUs)::

      1   2   3    4    5    6   7    8
    +---+---+----+---+-----+---+----+---+
    | 1   1 | M1  M1 | P/F | M2  M2  M2 |
    +---+---+----+---+-----+---+----+---+

    N(S) : sender send sequence number (Bit 2=lower-order-bit)
    N(R) : sender receive sequence number (Bit 10=lower-order-bit)
    S    : supervisory function bit
    M1/M2: modifier function bit
    P/F  : poll bit - command LLC PDUs
           final bit - response LLC PDUs
"""


import struct
from . import bpdu
from . import packet_base
from ryu.lib import stringify


SAP_BPDU = 0x42


class llc(packet_base.PacketBase):
    """LLC(IEEE 802.2) header encoder/decoder class.

    An instance has the following attributes at least.
    Most of them are same to the on-wire counterparts but in host byte
    order.
    __init__ takes the corresponding args in this order.

    .. tabularcolumns:: |l|L|

    =============== ===============================================
    Attribute       Description
    =============== ===============================================
    dsap_addr       Destination service access point address field \
                    includes I/G bit at least significant bit.
    ssap_addr       Source service access point address field \
                    includes C/R bit at least significant bit.
    control         Control field \
                    [16 bits for formats that include sequence \
                    numbering, and 8 bits for formats that do not]. \
                    Either ryu.lib.packet.llc.ControlFormatI or \
                    ryu.lib.packet.llc.ControlFormatS or \
                    ryu.lib.packet.llc.ControlFormatU object.
    =============== ===============================================
    """

    _PACK_STR = '!BB'
    _PACK_LEN = struct.calcsize(_PACK_STR)
    _CTR_TYPES = {}
    _CTR_PACK_STR = '!2xB'

    _MIN_LEN = _PACK_LEN

    @staticmethod
    def register_control_type(register_cls):
        llc._CTR_TYPES[register_cls.TYPE] = register_cls
        return register_cls

    def __init__(self, dsap_addr, ssap_addr, control):
        super(llc, self).__init__()

        assert getattr(control, 'TYPE', None) in self._CTR_TYPES

        self.dsap_addr = dsap_addr
        self.ssap_addr = ssap_addr
        self.control = control

    @classmethod
    def parser(cls, buf):
        assert len(buf) >= cls._PACK_LEN
        (dsap_addr, ssap_addr) = struct.unpack_from(cls._PACK_STR, buf)

        (control,) = struct.unpack_from(cls._CTR_PACK_STR, buf)
        ctrl = cls._get_control(control)
        control, information = ctrl.parser(buf[cls._PACK_LEN:])

        return (cls(dsap_addr, ssap_addr, control),
                cls.get_packet_type(dsap_addr), information)

    def serialize(self, payload, prev):
        addr = struct.pack(self._PACK_STR, self.dsap_addr, self.ssap_addr)
        control = self.control.serialize()
        return addr + control

    @classmethod
    def _get_control(cls, buf):
        key = buf & 0b1 if buf & 0b1 == ControlFormatI.TYPE else buf & 0b11
        return cls._CTR_TYPES[key]


@llc.register_control_type
class ControlFormatI(stringify.StringifyMixin):
    """LLC sub encoder/decoder class for control I-format field.

    An instance has the following attributes at least.
    Most of them are same to the on-wire counterparts but in host byte
    order.
    __init__ takes the corresponding args in this order.

    ======================== ===============================
    Attribute                Description
    ======================== ===============================
    send_sequence_number     sender send sequence number
    pf_bit                   poll/final bit
    receive_sequence_number  sender receive sequence number
    ======================== ===============================
    """
    TYPE = 0b0
    _PACK_STR = '!H'
    _PACK_LEN = struct.calcsize(_PACK_STR)

    def __init__(self, send_sequence_number=0, pf_bit=0,
                 receive_sequence_number=0):
        super(ControlFormatI, self).__init__()
        self.send_sequence_number = send_sequence_number
        self.pf_bit = pf_bit
        self.receive_sequence_number = receive_sequence_number

    @classmethod
    def parser(cls, buf):
        assert len(buf) >= cls._PACK_LEN
        (control,) = struct.unpack_from(cls._PACK_STR, buf)
        assert (control >> 8) & 0b1 == cls.TYPE

        send_sequence_number = (control >> 9) & 0b1111111
        pf_bit = (control >> 8) & 0b1
        receive_sequence_number = (control >> 1) & 0b1111111

        return cls(send_sequence_number, pf_bit,
                   receive_sequence_number), buf[cls._PACK_LEN:]

    def serialize(self):
        control = (self.send_sequence_number << 9 |
                   self.TYPE << 8 |
                   self.receive_sequence_number << 1 |
                   self.pf_bit)
        return struct.pack(self._PACK_STR, control)


@llc.register_control_type
class ControlFormatS(stringify.StringifyMixin):
    """LLC sub encoder/decoder class for control S-format field.

    An instance has the following attributes at least.
    Most of them are same to the on-wire counterparts but in host byte
    order.
    __init__ takes the corresponding args in this order.

    ======================== ===============================
    Attribute                Description
    ======================== ===============================
    supervisory_function     supervisory function bit
    pf_bit                   poll/final bit
    receive_sequence_number  sender receive sequence number
    ======================== ===============================
    """

    TYPE = 0b01
    _PACK_STR = '!H'
    _PACK_LEN = struct.calcsize(_PACK_STR)

    def __init__(self, supervisory_function=0, pf_bit=0,
                 receive_sequence_number=0):
        super(ControlFormatS, self).__init__()
        self.supervisory_function = supervisory_function
        self.pf_bit = pf_bit
        self.receive_sequence_number = receive_sequence_number

    @classmethod
    def parser(cls, buf):
        assert len(buf) >= cls._PACK_LEN
        (control,) = struct.unpack_from(cls._PACK_STR, buf)

        assert (control >> 8) & 0b11 == cls.TYPE

        supervisory_function = (control >> 10) & 0b11
        pf_bit = (control >> 8) & 0b1
        receive_sequence_number = (control >> 1) & 0b1111111

        return cls(supervisory_function, pf_bit,
                   receive_sequence_number), buf[cls._PACK_LEN:]

    def serialize(self):
        control = (self.supervisory_function << 10 |
                   self.TYPE << 8 |
                   self.receive_sequence_number << 1 |
                   self.pf_bit)
        return struct.pack(self._PACK_STR, control)


@llc.register_control_type
class ControlFormatU(stringify.StringifyMixin):
    """LLC sub encoder/decoder class for control U-format field.

    An instance has the following attributes at least.
    Most of them are same to the on-wire counterparts but in host byte
    order.
    __init__ takes the corresponding args in this order.

    ======================== ===============================
    Attribute                Description
    ======================== ===============================
    modifier_function1       modifier function bit
    pf_bit                   poll/final bit
    modifier_function2       modifier function bit
    ======================== ===============================
    """

    TYPE = 0b11
    _PACK_STR = '!B'
    _PACK_LEN = struct.calcsize(_PACK_STR)

    def __init__(self, modifier_function1=0, pf_bit=0, modifier_function2=0):
        super(ControlFormatU, self).__init__()
        self.modifier_function1 = modifier_function1
        self.pf_bit = pf_bit
        self.modifier_function2 = modifier_function2

    @classmethod
    def parser(cls, buf):
        assert len(buf) >= cls._PACK_LEN
        (control,) = struct.unpack_from(cls._PACK_STR, buf)

        assert control & 0b11 == cls.TYPE

        modifier_function1 = (control >> 2) & 0b11
        pf_bit = (control >> 4) & 0b1
        modifier_function2 = (control >> 5) & 0b111

        return cls(modifier_function1, pf_bit,
                   modifier_function2), buf[cls._PACK_LEN:]

    def serialize(self):
        control = (self.modifier_function2 << 5 |
                   self.pf_bit << 4 |
                   self.modifier_function1 << 2 |
                   self.TYPE)
        return struct.pack(self._PACK_STR, control)


llc.register_packet_type(bpdu.bpdu, SAP_BPDU)
llc.set_classes(llc._CTR_TYPES)
