# Copyright 2021 The Pigweed Authors
#
# 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
#
#     https://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.
"""Wrapers for pyserial classes to log read and write data."""

from contextvars import ContextVar
import logging
import textwrap

import serial  # type: ignore

from pw_console.widgets.event_count_history import EventCountHistory

_LOG = logging.getLogger('pw_console.serial_debug_logger')


def _log_hex_strings(data, prefix=''):
    """Create alinged hex number and character view log messages."""
    # Make a list of 2 character hex number strings.
    hex_numbers = textwrap.wrap(data.hex(), 2)

    hex_chars = [
        ('<' + str(b.to_bytes(1, byteorder='big')) + '>')
        .replace("<b'\\x", '', 1)  # Remove b'\x from the beginning
        .replace("<b'", '', 1)  # Remove b' from the beginning
        .replace("'>", '', 1)  # Remove ' from the end
        .rjust(2)
        for b in data
    ] # yapf: disable

    # Replace non-printable bytes with dots.
    for i, num in enumerate(hex_numbers):
        if num == hex_chars[i]:
            hex_chars[i] = '..'

    hex_numbers_msg = ' '.join(hex_numbers)
    hex_chars_msg = ' '.join(hex_chars)

    _LOG.debug('%s%s',
               prefix,
               hex_numbers_msg,
               extra=dict(extra_metadata_fields={
                   'msg': hex_numbers_msg,
               }))
    _LOG.debug('%s%s',
               prefix,
               hex_chars_msg,
               extra=dict(extra_metadata_fields={
                   'msg': hex_chars_msg,
               }))


BANDWIDTH_HISTORY_CONTEXTVAR = (ContextVar('pw_console_bandwidth_history',
                                           default={
                                               'total':
                                               EventCountHistory(interval=3),
                                               'read':
                                               EventCountHistory(interval=3),
                                               'write':
                                               EventCountHistory(interval=3),
                                           }))


class SerialWithLogging(serial.Serial):  # pylint: disable=too-many-ancestors
    """pyserial with read and write wrappers for logging."""
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.pw_bps_history = BANDWIDTH_HISTORY_CONTEXTVAR.get()

    def read(self, *args, **kwargs):
        data = super().read(*args, **kwargs)
        self.pw_bps_history['read'].log(len(data))
        self.pw_bps_history['total'].log(len(data))

        if len(data) > 0:
            prefix = 'Read %2d B: ' % len(data)
            _LOG.debug('%s%s',
                       prefix,
                       data,
                       extra=dict(extra_metadata_fields={
                           'mode': 'Read',
                           'bytes': len(data),
                           'msg': str(data),
                       }))
            _log_hex_strings(data, prefix=prefix)

        return data

    def write(self, data, *args, **kwargs):
        self.pw_bps_history['write'].log(len(data))
        self.pw_bps_history['total'].log(len(data))

        if len(data) > 0:
            prefix = 'Write %2d B: ' % len(data)
            _LOG.debug('%s%s',
                       prefix,
                       data,
                       extra=dict(extra_metadata_fields={
                           'mode': 'Write',
                           'bytes': len(data),
                           'msg': str(data)
                       }))
            _log_hex_strings(data, prefix=prefix)

        super().write(data, *args, **kwargs)
