# Copyright 2017, 2018, Digi International Inc.
#
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
#
# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.

from digi.xbee.packets.base import *
from digi.xbee.packets.cellular import *
from digi.xbee.packets.common import *
from digi.xbee.packets.devicecloud import *
from digi.xbee.packets.network import *
from digi.xbee.packets.raw import *
from digi.xbee.packets.wifi import *
from digi.xbee.packets.aft import ApiFrameType
from digi.xbee.models.mode import OperatingMode


"""
This module provides functionality to build XBee packets from
bytearray returning the appropriate XBeePacket subclass.

All the API and API2 logic is already included so all packet reads are
independent of the XBee operating mode.

Two API modes are supported and both can be enabled using the ``AP``
(API Enable) command::

    API1 - API Without Escapes
    The data frame structure is defined as follows:

      Start Delimiter          Length                   Frame Data                   Checksum
          (Byte 1)            (Bytes 2-3)               (Bytes 4-n)                (Byte n + 1)
    +----------------+  +-------------------+  +--------------------------- +  +----------------+
    |      0x7E      |  |   MSB   |   LSB   |  |   API-specific Structure   |  |     1 Byte     |
    +----------------+  +-------------------+  +----------------------------+  +----------------+
                   MSB = Most Significant Byte, LSB = Least Significant Byte


API2 - API With Escapes
The data frame structure is defined as follows::

      Start Delimiter          Length                   Frame Data                   Checksum
          (Byte 1)            (Bytes 2-3)               (Bytes 4-n)                (Byte n + 1)
    +----------------+  +-------------------+  +--------------------------- +  +----------------+
    |      0x7E      |  |   MSB   |   LSB   |  |   API-specific Structure   |  |     1 Byte     |
    +----------------+  +-------------------+  +----------------------------+  +----------------+
                        \___________________________________  _________________________________/
                                                            \/
                                                Characters Escaped If Needed

                   MSB = Most Significant Byte, LSB = Least Significant Byte


When sending or receiving an API2 frame, specific data values must be
escaped (flagged) so they do not interfere with the data frame sequencing.
To escape an interfering data byte, the byte 0x7D is inserted before
the byte to be escaped XOR'd with 0x20.

The data bytes that need to be escaped:

- ``0x7E`` - Frame Delimiter - :attr:`.SpecialByte.
- ``0x7D`` - Escape
- ``0x11`` - XON
- ``0x13`` - XOFF

The length field has a two-byte value that specifies the number of
bytes that will be contained in the frame data field. It does not include the
checksum field.

The frame data  forms an API-specific structure as follows::

      Start Delimiter          Length                   Frame Data                   Checksum
          (Byte 1)            (Bytes 2-3)               (Bytes 4-n)                (Byte n + 1)
    +----------------+  +-------------------+  +--------------------------- +  +----------------+
    |      0x7E      |  |   MSB   |   LSB   |  |   API-specific Structure   |  |     1 Byte     |
    +----------------+  +-------------------+  +----------------------------+  +----------------+
                                               /                                                 \\
                                              /  API Identifier        Identifier specific data   \\
                                              +------------------+  +------------------------------+
                                              |       cmdID      |  |           cmdData            |
                                              +------------------+  +------------------------------+


The cmdID frame (API-identifier) indicates which API messages
will be contained in the cmdData frame (Identifier-specific data).

To unit_test data integrity, a checksum is calculated and verified on
non-escaped data.

.. seealso::
   | :class:`.XBeePacket`
   | :class:`.OperatingMode`
"""


def build_frame(packet_bytearray, operating_mode=OperatingMode.API_MODE):
    """
    Creates a packet from raw data.
    
    Args:
        packet_bytearray (Bytearray): the raw data of the packet to build.
        operating_mode (:class:`.OperatingMode`): the operating mode in which the raw data has been captured.

    .. seealso::
       | :class:`.OperatingMode`
    """
    frame_type = ApiFrameType.get(packet_bytearray[3])

    if frame_type == ApiFrameType.GENERIC:
        return GenericXBeePacket.create_packet(packet_bytearray, operating_mode)

    elif frame_type == ApiFrameType.AT_COMMAND:
        return ATCommPacket.create_packet(packet_bytearray, operating_mode)

    elif frame_type == ApiFrameType.AT_COMMAND_QUEUE:
        return ATCommQueuePacket.create_packet(packet_bytearray, operating_mode)

    elif frame_type == ApiFrameType.AT_COMMAND_RESPONSE:
        return ATCommResponsePacket.create_packet(packet_bytearray, operating_mode)

    elif frame_type == ApiFrameType.RECEIVE_PACKET:
        return ReceivePacket.create_packet(packet_bytearray, operating_mode)

    elif frame_type == ApiFrameType.RX_64:
        return RX64Packet.create_packet(packet_bytearray, operating_mode)

    elif frame_type == ApiFrameType.RX_16:
        return RX16Packet.create_packet(packet_bytearray, operating_mode)

    elif frame_type == ApiFrameType.REMOTE_AT_COMMAND_REQUEST:
        return RemoteATCommandPacket.create_packet(packet_bytearray, operating_mode)

    elif frame_type == ApiFrameType.REMOTE_AT_COMMAND_RESPONSE:
        return RemoteATCommandResponsePacket.create_packet(packet_bytearray, operating_mode)

    elif frame_type == ApiFrameType.TRANSMIT_REQUEST:
        return TransmitPacket.create_packet(packet_bytearray, operating_mode)

    elif frame_type == ApiFrameType.TRANSMIT_STATUS:
        return TransmitStatusPacket.create_packet(packet_bytearray, operating_mode)

    elif frame_type == ApiFrameType.MODEM_STATUS:
        return ModemStatusPacket.create_packet(packet_bytearray, operating_mode)

    elif frame_type == ApiFrameType.TX_STATUS:
        return TXStatusPacket.create_packet(packet_bytearray, operating_mode)

    elif frame_type == ApiFrameType.RX_IO_16:
        return RX16IOPacket.create_packet(packet_bytearray, operating_mode)

    elif frame_type == ApiFrameType.RX_IO_64:
        return RX64IOPacket.create_packet(packet_bytearray, operating_mode)

    elif frame_type == ApiFrameType.IO_DATA_SAMPLE_RX_INDICATOR:
        return IODataSampleRxIndicatorPacket.create_packet(packet_bytearray, operating_mode)

    elif frame_type == ApiFrameType.EXPLICIT_ADDRESSING:
        return ExplicitAddressingPacket.create_packet(packet_bytearray, operating_mode)

    elif frame_type == ApiFrameType.EXPLICIT_RX_INDICATOR:
        return ExplicitRXIndicatorPacket.create_packet(packet_bytearray, operating_mode)

    elif frame_type == ApiFrameType.TX_SMS:
        return TXSMSPacket.create_packet(packet_bytearray, operating_mode)

    elif frame_type == ApiFrameType.TX_IPV4:
        return TXIPv4Packet.create_packet(packet_bytearray, operating_mode)

    elif frame_type == ApiFrameType.RX_SMS:
        return RXSMSPacket.create_packet(packet_bytearray, operating_mode)

    elif frame_type == ApiFrameType.RX_IPV4:
        return RXIPv4Packet.create_packet(packet_bytearray, operating_mode)

    elif frame_type == ApiFrameType.REMOTE_AT_COMMAND_REQUEST_WIFI:
        return RemoteATCommandWifiPacket.create_packet(packet_bytearray, operating_mode)

    elif frame_type == ApiFrameType.SEND_DATA_REQUEST:
        return SendDataRequestPacket.create_packet(packet_bytearray, operating_mode)

    elif frame_type == ApiFrameType.DEVICE_RESPONSE:
        return DeviceResponsePacket.create_packet(packet_bytearray, operating_mode)

    elif frame_type == ApiFrameType.REMOTE_AT_COMMAND_RESPONSE_WIFI:
        return RemoteATCommandResponseWifiPacket.create_packet(packet_bytearray, operating_mode)

    elif frame_type == ApiFrameType.IO_DATA_SAMPLE_RX_INDICATOR_WIFI:
        return IODataSampleRxIndicatorWifiPacket.create_packet(packet_bytearray, operating_mode)

    elif frame_type == ApiFrameType.SEND_DATA_RESPONSE:
        return SendDataResponsePacket.create_packet(packet_bytearray, operating_mode)

    elif frame_type == ApiFrameType.DEVICE_REQUEST:
        return DeviceRequestPacket.create_packet(packet_bytearray, operating_mode)

    elif frame_type == ApiFrameType.DEVICE_RESPONSE_STATUS:
        return DeviceResponseStatusPacket.create_packet(packet_bytearray, operating_mode)

    elif frame_type == ApiFrameType.FRAME_ERROR:
        return FrameErrorPacket.create_packet(packet_bytearray, operating_mode)

    else:
        return UnknownXBeePacket.create_packet(packet_bytearray, operating_mode)
