"""
"""
from enum import IntEnum, Enum
from .exceptions import ProtocolError, FrameSizeError, FlowControlError
import struct
MAX_FRAME_SIZE = (2 ** 14) - 1
MAX_WINDOW_UPDATE = (2 ** 31) - 1
DEFAULT_PRIORITY = (2 ** 30)
class ConnectionSetting(Enum):
    HEADER_TABLE_SIZE = 0x01
    ENABLE_PUSH = 0x02
    MAX_CONCURRENT_STREAMS = 0x03
    INITIAL_WINDOW_SIZE = 0x04
class FrameType(Enum):
    DATA = 0x00
    HEADERS = 0x1
    PRIORITY = 0x2
    RST_STREAM = 0x3
    SETTINGS = 0x4
    PUSH_PROMISE = 0x5
    PING = 0x6
    GO_AWAY = 0x7
    WINDOW_UPDATE = 0x8
    CONTINUATION = 0x9
class ErrorCode(Enum):
    NO_ERROR = 0x0
    PROTOCOL_ERROR = 0x01
    INTERNAL_ERROR = 0x02
    FLOW_CONTROL_ERROR = 0x04
    SETTINGS_TIMEOUT = 0x08
    STREAM_CLOSED = 0x10
    FRAME_SIZE_ERROR = 0x20
    REFUSED_STREAM = 0x40
    CANCEL = 0x80
    COMPRESSION_ERROR = 0x100
    CONNECT_ERROR = 0x200
    ENHANCE_YOUR_CALM = 0x400
    INADEQUATE_SECURITY = 0x800
# TODO(roasbeef): Think of better name? And/or better way to handle the
# redundancy.
class SpecialFrameFlag(Enum):
    ACK = 0x1
    END_PUSH_PROMISE = 0x4
class FrameFlag(Enum):
    END_STREAM = 0x1
    END_SEGMENT = 0x2
    END_HEADERS = 0x4
    PRIORITY = 0x8
    PAD_LOW = 0x10
    PAD_HIGH = 0x20
    @staticmethod
    def create_flag_set(*flag_names):
        return {FrameFlag[flag_name] for flag_name in flag_names}
class FrameHeader(object):
    """
     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
     +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
     | R |     Length (14)           |   Type (8)    |   Flags (8)   |
     +-+-+-----------+---------------+-------------------------------+
     |R|                 Stream Identifier (31)                      |
     +-+-------------------------------------------------------------+
     |                   Frame Payload (0...)                      ...
     +---------------------------------------------------------------+
    """
    def __init__(self, length, frame_type, flags, stream_id):
        self.length = length
        self.frame_type = frame_type
        self.raw_flag_bits = flags
        self.stream_id = stream_id
    def __len__(self):
        """ Return the length of the header's payload, in bytes. """
        return self.length
    def __repr__(self):
        return '<FrameHeader length:{}, frame_type:{}, flags:{}, stream_id:{}>'.format(
            self.length,
            FRAME_TYPE_TO_FRAME[self.frame_type].__name__,
            '<{}>'.format(','.join(str(flag_type.name) for flag_type in FrameFlag if self.flags & flag_type.value)),
            self.stream_id
        )
    @classmethod
    def from_raw_bytes(cls, frame_bytes):
        header_fields = struct.unpack('!HBBL', frame_bytes)
        # Knock off the first 2 bits, they are reserved, and currently unused.
        payload_length = header_fields[0] & 0x3FFF
        frame_type = header_fields[1]
        raw_flags = header_fields[2]
        stream_id = header_fields[3]
        return cls(payload_length, FrameType(frame_type), raw_flags, stream_id)
    @classmethod
    def from_frame(cls, frame):
        raw_flags = 0
        for flag_type in frame.flags:
            raw_flags |= flag_type.value
        return cls(len(frame), frame.frame_type, raw_flags, frame.stream_id)
    def serialize(self):
        return struct.pack(
            '!HBBL',
            self.length & 0x3FFF,  # Knock off first two bits.
            self.frame_type.value,
            self.raw_flag_bits,
            self.stream_id & 0x7FFFFFFF  # Make sure it's 31 bits.
        )
class Frame(object):
    frame_type = None
    defined_flags = set()
    def __init__(self, stream_id, flags=None, length=0):
        self.stream_id = stream_id
        self.flags = flags if flags is not None else set()
        self.length = length
    def __len__(self):
        # TODO(roasbeef): Delete this method?
        return self.length
    def __repr__(self):
        return '<{}| length: {}, flags: {}, stream_id: {}, data: {}>'.format(
            FRAME_TYPE_TO_FRAME[self.frame_type].__name__,
            len(self),
            '<{}>'.format(','.join(str(flag_type.name) for flag_type in self.defined_flags if flag_type in self.flags)),
            self.stream_id,
            (self.data if isinstance(self, DataFrame) else b''),
        )
    @staticmethod
    def from_frame_header(frame_header):
        frame_klass = FRAME_TYPE_TO_FRAME[frame_header.frame_type]
        parsed_frame = frame_klass(frame_header.stream_id)
        parsed_frame.parse_flags(frame_header.raw_flag_bits)
        return parsed_frame
    def parse_flags(self, flag_byte):
        for flag_type in self.defined_flags:
            if flag_byte & flag_type.value:
                self.flags.add(flag_type)
    def deserialize(self, frame_payload):
        raise NotImplementedError
    def serialize(self):
        raise NotImplementedError
class DataFrame(Frame):
    """
    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
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
    | [Pad High(8)] | [Pad Low (8)] |          Data (*)             .
    +---------------+---------------+-------------------------------+
    .                            Data (*)                         ...
    +---------------------------------------------------------------+
    |                           Padding (*)                       ...
    +---------------------------------------------------------------+
    """
    frame_type = FrameType.DATA
    defined_flags = FrameFlag.create_flag_set('END_STREAM', 'END_SEGMENT',
                                              'PAD_LOW', 'PAD_HIGH')
    def __init__(self, stream_id, **kwargs):
        if stream_id == 0:
            raise ProtocolError()
        super().__init__(stream_id, **kwargs)
        self.data = b''
        self.pad_high = None
        self.pad_low = None
        self.total_padding = 0
    def __len__(self):
        return 2 + len(self.data) + self.total_padding
    def deserialize(self, frame_payload):
        self.pad_high = frame_payload[0] if FrameFlag.PAD_HIGH in self.flags else 0
        self.pad_low = frame_payload[1] if FrameFlag.PAD_LOW in self.flags else 0
        self.total_padding = (self.pad_high * 256) + self.pad_low
        if self.total_padding > len(frame_payload[2:]):
            raise ProtocolError()
        # TODO(roasbeef): Enforce max frame size, tests and such.
        self.data = frame_payload[2:len(frame_payload) - self.total_padding]
    def serialize(self, pad_low=0, pad_high=0):
        frame_header = FrameHeader.from_frame(self).serialize()
        padding_bytes = ((pad_high * 256) + pad_low) * struct.pack('!x')


Answer:         pad_low_and_high = struct.pack('!BB', pad_high, pad_low)