# -*- coding: utf-8 -*-
"""open shortest path first

:mod:`pcapkit.protocols.link.ospf` contains
:class:`~pcapkit.protocols.link.ospf.OSPF` only,
which implements extractor for Open Shortest Path
First (OSPF) [*]_, whose structure is described
as below:

+========+=======+====================+=================================+
| Octets | Bits  | Name               | Description                     |
+========+=======+====================+=================================+
| 0      |     0 | ``ospf.version``   | Version Number                  |
+--------+-------+--------------------+---------------------------------+
| 0      |     0 | ``ospf.type``      | Type                            |
+--------+-------+--------------------+---------------------------------+
| 0      |     1 | ``ospf.len``       | Packet Length (header included) |
+--------+-------+--------------------+---------------------------------+
| 0      |     2 | ``ospf.router_id`` | Router ID                       |
+--------+-------+--------------------+---------------------------------+
| 0      |     4 | ``ospf.area_id``   | Area ID                         |
+--------+-------+--------------------+---------------------------------+
| 0      |     6 | ``ospf.chksum``    | Checksum                        |
+--------+-------+--------------------+---------------------------------+
| 0      |     7 | ``ospf.autype``    | Authentication Type             |
+--------+-------+--------------------+---------------------------------+
| 1      |     8 | ``ospf.auth``      | Authentication                  |
+--------+-------+--------------------+---------------------------------+

.. [*] https://en.wikipedia.org/wiki/Open_Shortest_Path_First

"""
import ipaddress
from typing import TYPE_CHECKING

from pcapkit.const.ospf.authentication import Authentication as RegType_Authentication
from pcapkit.const.ospf.packet import Packet as RegType_Packet
from pcapkit.protocols.data.link.ospf import OSPF as DataType_OSPF
from pcapkit.protocols.data.link.ospf import \
    CrytographicAuthentication as DataType_CrytographicAuthentication
from pcapkit.protocols.link.link import Link

if TYPE_CHECKING:
    from ipaddress import IPv4Address
    from typing import Any, NoReturn, Optional

    from typing_extensions import Literal

__all__ = ['OSPF']


class OSPF(Link):
    """This class implements Open Shortest Path First."""

    #: Parsed packet data.
    _info: 'DataType_OSPF'

    ##########################################################################
    # Properties.
    ##########################################################################

    @property
    def name(self) -> 'str':
        """Name of current protocol."""
        return f'Open Shortest Path First version {self._info.version}'

    @property
    def alias(self) -> 'str':
        """Acronym of current protocol."""
        return f'OSPFv{self._info.version}'

    @property
    def length(self) -> 'Literal[24]':
        """Header length of current protocol."""
        return 24

    @property
    def type(self) -> 'RegType_Packet':
        """OSPF packet type."""
        return self._info.type

    ##########################################################################
    # Methods.
    ##########################################################################

    def read(self, length: 'Optional[int]' = None, **kwargs: 'Any') -> 'DataType_OSPF':
        """Read Open Shortest Path First.

        Structure of OSPF header [:rfc:`2328`]:

        .. code-block:: text

            0                   1                   2                   3
            0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
           +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
           |   Version #   |     Type      |         Packet length         |
           +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
           |                          Router ID                            |
           +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
           |                           Area ID                             |
           +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
           |           Checksum            |             AuType            |
           +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
           |                       Authentication                          |
           +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
           |                       Authentication                          |
           +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

        Args:
            length: Length of packet data.

        Keyword Args:
            **kwargs: Arbitrary keyword arguments.

        Returns:
            Parsed packet data.

        """
        if length is None:
            length = len(self)

        _vers = self._read_unpack(1)
        _type = self._read_unpack(1)
        _tlen = self._read_unpack(2)
        _rtid = self._read_id_numbers()
        _area = self._read_id_numbers()
        _csum = self._read_fileng(2)
        _autp = self._read_unpack(2)

        ospf = DataType_OSPF(
            version=_vers,
            type=RegType_Packet.get(_type),
            len=_tlen,
            router_id=_rtid,
            area_id=_area,
            chksum=_csum,
            autype=RegType_Authentication.get(_autp),
        )

        if ospf.autype == RegType_Authentication.Cryptographic_authentication:
            ospf.__update__([
                ('auth', self._read_encrypt_auth()),
            ])
        else:
            ospf.__update__([
                ('auth', self._read_fileng(8)),
            ])
        return self._decode_next_layer(ospf, length - self.length)  # type: ignore[return-value]

    def make(self, **kwargs: 'Any') -> 'NoReturn':
        """Make (construct) packet data.

        Keyword Args:
            **kwargs: Arbitrary keyword arguments.

        Returns:
            bytes: Constructed packet data.

        """
        raise NotImplementedError

    ##########################################################################
    # Data models.
    ##########################################################################

    def __length_hint__(self) -> 'Literal[24]':
        """Return an estimated length for the object."""
        return 24

    ##########################################################################
    # Utilities.
    ##########################################################################

    def _read_id_numbers(self) -> 'IPv4Address':
        """Read router and area IDs.

        Returns:
            Parsed IDs as an IPv4 address.

        """
        #_byte = self._read_fileng(4)
        #_addr = '.'.join(str(_) for _ in _byte)
        return ipaddress.ip_address(self._read_fileng(4))

    def _read_encrypt_auth(self) -> 'DataType_CrytographicAuthentication':
        """Read Authentication field when Cryptographic Authentication is employed,
        i.e. :attr:`~OSPF.autype` is ``2``.

        Structure of Cryptographic Authentication [:rfc:`2328`]::

             0                   1                   2                   3
             0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
            +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
            |              0                |    Key ID     | Auth Data Len |
            +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
            |                 Cryptographic sequence number                 |
            +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

        Args:
            length: packet length

        Returns:
            Parsed packet data.

        """
        _resv = self._read_fileng(2)
        _keys = self._read_unpack(1)
        _alen = self._read_unpack(1)
        _seqn = self._read_unpack(4)

        auth = DataType_CrytographicAuthentication(
            key_id=_keys,
            len=_alen,
            seq=_seqn,
        )
        return auth

# -*- coding: utf-8 -*-
"""open shortest path first

:mod:`pcapkit.protocols.link.ospf` contains
:class:`~pcapkit.protocols.link.ospf.OSPF` only,
which implements extractor for Open Shortest Path
First (OSPF) [*]_, whose structure is described
as below:

+========+=======+====================+=================================+
| Octets | Bits  | Name               | Description                     |
+========+=======+====================+=================================+
| 0      |     0 | ``ospf.version``   | Version Number                  |
+--------+-------+--------------------+---------------------------------+
| 0      |     0 | ``ospf.type``      | Type                            |
+--------+-------+--------------------+---------------------------------+
| 0      |     1 | ``ospf.len``       | Packet Length (header included) |
+--------+-------+--------------------+---------------------------------+
| 0      |     2 | ``ospf.router_id`` | Router ID                       |
+--------+-------+--------------------+---------------------------------+
| 0      |     4 | ``ospf.area_id``   | Area ID                         |
+--------+-------+--------------------+---------------------------------+
| 0      |     6 | ``ospf.chksum``    | Checksum                        |
+--------+-------+--------------------+---------------------------------+
| 0      |     7 | ``ospf.autype``    | Authentication Type             |
+--------+-------+--------------------+---------------------------------+
| 1      |     8 | ``ospf.auth``      | Authentication                  |
+--------+-------+--------------------+---------------------------------+

.. [*] https://en.wikipedia.org/wiki/Open_Shortest_Path_First

"""
import ipaddress
from typing import TYPE_CHECKING

from pcapkit.const.ospf.authentication import Authentication as RegType_Authentication
from pcapkit.const.ospf.packet import Packet as RegType_Packet
from pcapkit.protocols.data.link.ospf import OSPF as DataType_OSPF
from pcapkit.protocols.data.link.ospf import \
    CrytographicAuthentication as DataType_CrytographicAuthentication
from pcapkit.protocols.link.link import Link

if TYPE_CHECKING:
    from ipaddress import IPv4Address
    from typing import Any, NoReturn, Optional

    from typing_extensions import Literal

__all__ = ['OSPF']


class OSPF(Link):
    """This class implements Open Shortest Path First."""

    #: Parsed packet data.
    _info: 'DataType_OSPF'

    ##########################################################################
    # Properties.
    ##########################################################################

    @property
    def name(self) -> 'str':
        """Name of current protocol."""
        return f'Open Shortest Path First version {self._info.version}'

    @property
    def alias(self) -> 'str':
        """Acronym of current protocol."""
        return f'OSPFv{self._info.version}'

    @property
    def length(self) -> 'Literal[24]':
        """Header length of current protocol."""
        return 24

    @property
    def type(self) -> 'RegType_Packet':
        """OSPF packet type."""
        return self._info.type

    ##########################################################################
    # Methods.
    ##########################################################################

    def read(self, length: 'Optional[int]' = None, **kwargs: 'Any') -> 'DataType_OSPF':
        """Read Open Shortest Path First.

        Structure of OSPF header [:rfc:`2328`]:

        .. code-block:: text

            0                   1                   2                   3
            0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
           +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
           |   Version #   |     Type      |         Packet length         |
           +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
           |                          Router ID                            |
           +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
           |                           Area ID                             |
           +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
           |           Checksum            |             AuType            |
           +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
           |                       Authentication                          |
           +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
           |                       Authentication                          |
           +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

        Args:
            length: Length of packet data.

        Keyword Args:
            **kwargs: Arbitrary keyword arguments.

        Returns:
            Parsed packet data.

        """
        if length is None:
            length = len(self)

        _vers = self._read_unpack(1)
        _type = self._read_unpack(1)
        _tlen = self._read_unpack(2)
        _rtid = self._read_id_numbers()
        _area = self._read_id_numbers()
        _csum = self._read_fileng(2)
        _autp = self._read_unpack(2)

        ospf = DataType_OSPF(
            version=_vers,
            type=RegType_Packet.get(_type),
            len=_tlen,
            router_id=_rtid,
            area_id=_area,
            chksum=_csum,
            autype=RegType_Authentication.get(_autp),
        )

        if ospf.autype == RegType_Authentication.Cryptographic_authentication:
            ospf.__update__([
                ('auth', self._read_encrypt_auth()),
            ])
        else:
            ospf.__update__([
                ('auth', self._read_fileng(8)),
            ])
        return self._decode_next_layer(ospf, length - self.length)  # type: ignore[return-value]

    def make(self, **kwargs: 'Any') -> 'NoReturn':
        """Make (construct) packet data.

        Keyword Args:
            **kwargs: Arbitrary keyword arguments.

        Returns:
            bytes: Constructed packet data.

        """
        raise NotImplementedError

    ##########################################################################
    # Data models.
    ##########################################################################

    def __length_hint__(self) -> 'Literal[24]':
        """Return an estimated length for the object."""
        return 24

    ##########################################################################
    # Utilities.
    ##########################################################################

    def _read_id_numbers(self) -> 'IPv4Address':
        """Read router and area IDs.

        Returns:
            Parsed IDs as an IPv4 address.

        """
        #_byte = self._read_fileng(4)
        #_addr = '.'.join(str(_) for _ in _byte)
        return ipaddress.ip_address(self._read_fileng(4))

    def _read_encrypt_auth(self) -> 'DataType_CrytographicAuthentication':
        """Read Authentication field when Cryptographic Authentication is employed,
        i.e. :attr:`~OSPF.autype` is ``2``.

        Structure of Cryptographic Authentication [:rfc:`2328`]::

             0                   1                   2                   3
             0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
            +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
            |              0                |    Key ID     | Auth Data Len |
            +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
            |                 Cryptographic sequence number                 |
            +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

        Args:
            length: packet length

        Returns:
            Parsed packet data.

        """
        _resv = self._read_fileng(2)
        _keys = self._read_unpack(1)
        _alen = self._read_unpack(1)
        _seqn = self._read_unpack(4)

        auth = DataType_CrytographicAuthentication(
            key_id=_keys,
            len=_alen,
            seq=_seqn,
        )
        return auth

# -*- coding: utf-8 -*- open shortest path first  :mod:`pcapkit.protocols.link.ospf` contains :class:`~pcapkit.protocols.link.ospf.OSPF` only, which implements extractor for Open Shortest Path First (OSPF) [*]_, whose structure is described as below:  +========+=======+====================+=================================+ | Octets | Bits  | Name               | Description                     | +========+=======+====================+=================================+ | 0      |     0 | ``ospf.version``   | Version Number                  | +--------+-------+--------------------+---------------------------------+ | 0      |     0 | ``ospf.type``      | Type                            | +--------+-------+--------------------+---------------------------------+ | 0      |     1 | ``ospf.len``       | Packet Length (header included) | +--------+-------+--------------------+---------------------------------+ | 0      |     2 | ``ospf.router_id`` | Router ID                       | +--------+-------+--------------------+---------------------------------+ | 0      |     4 | ``ospf.area_id``   | Area ID                         | +--------+-------+--------------------+---------------------------------+ | 0      |     6 | ``ospf.chksum``    | Checksum                        | +--------+-------+--------------------+---------------------------------+ | 0      |     7 | ``ospf.autype``    | Authentication Type             | +--------+-------+--------------------+---------------------------------+ | 1      |     8 | ``ospf.auth``      | Authentication                  | +--------+-------+--------------------+---------------------------------+  .. [*] https://en.wikipedia.org/wiki/Open_Shortest_Path_First This class implements Open Shortest Path First. #: Parsed packet data. ########################################################################## # Properties. ########################################################################## Name of current protocol. Acronym of current protocol. Header length of current protocol. OSPF packet type. ########################################################################## # Methods. ########################################################################## Read Open Shortest Path First.          Structure of OSPF header [:rfc:`2328`]:          .. code-block:: text              0                   1                   2                   3             0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1            +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+            |   Version #   |     Type      |         Packet length         |            +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+            |                          Router ID                            |            +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+            |                           Area ID                             |            +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+            |           Checksum            |             AuType            |            +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+            |                       Authentication                          |            +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+            |                       Authentication                          |            +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+          Args:             length: Length of packet data.          Keyword Args:             **kwargs: Arbitrary keyword arguments.          Returns:             Parsed packet data. # type: ignore[return-value] Make (construct) packet data.          Keyword Args:             **kwargs: Arbitrary keyword arguments.          Returns:             bytes: Constructed packet data. ########################################################################## # Data models. ########################################################################## Return an estimated length for the object. ########################################################################## # Utilities. ########################################################################## Read router and area IDs.          Returns:             Parsed IDs as an IPv4 address. #_byte = self._read_fileng(4) #_addr = '.'.join(str(_) for _ in _byte) Read Authentication field when Cryptographic Authentication is employed,         i.e. :attr:`~OSPF.autype` is ``2``.          Structure of Cryptographic Authentication [:rfc:`2328`]::               0                   1                   2                   3              0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1             +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+             |              0                |    Key ID     | Auth Data Len |             +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+             |                 Cryptographic sequence number                 |             +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+          Args:             length: packet length          Returns:             Parsed packet data.