# 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."""

import collections
import logging
from dataclasses import dataclass, field
import time
from typing import Optional

_LOG = logging.getLogger('pw_console')


@dataclass
class EventCountHistory:
    """Track counts of events over time.

    Example usage: ::

        events = EventCountHistory(
            base_count_units='Bytes',
            display_unit_title='KiB/s',
            display_unit_factor=0.001,
            interval=1.0,
            show_sparkline=True)

        # Log 1 event now.
        events.log(1)
        time.sleep(1)

        # Log 100 events at this time.
        events.log(100)
        time.sleep(1)

        events.log(200)
        time.sleep(1)
        events.log(400)
        print(events)
        ▂▄█ 0.400 [KiB/s]

    """

    base_count_units: str = 'Bytes'
    display_unit_title: str = 'KiB/s'
    display_unit_factor: float = 0.001
    interval: float = 1.0  # Number of seconds per sum of events.
    history_limit: int = 20
    scale_characters = ' ▁▂▃▄▅▆▇█'
    history: collections.deque = field(default_factory=collections.deque)
    show_sparkline: bool = False
    _this_count: int = 0
    _last_count: int = 0
    _last_update_time: float = field(default_factory=time.time)

    def log(self, count: int) -> None:
        self._this_count += count

        this_time = time.time()
        if this_time - self._last_update_time >= self.interval:
            self._last_update_time = this_time
            self._last_count = self._this_count
            self._this_count = 0
            self.history.append(self._last_count)

            if len(self.history) > self.history_limit:
                self.history.popleft()

    def last_count(self) -> float:
        return self._last_count * self.display_unit_factor

    def last_count_raw(self) -> int:
        return self._last_count

    def last_count_with_units(self) -> str:
        return '{:.3f} [{}]'.format(
            self._last_count * self.display_unit_factor, self.display_unit_title
        )

    def __repr__(self) -> str:
        sparkline = ''
        if self.show_sparkline:
            sparkline = self.sparkline()
        return ' '.join([sparkline, self.last_count_with_units()])

    def __pt_formatted_text__(self):
        return [('', self.__repr__())]

    def sparkline(
        self, min_value: int = 0, max_value: Optional[int] = None
    ) -> str:
        msg = ''.rjust(self.history_limit)
        if len(self.history) == 0:
            return msg

        minimum = min_value
        maximum = max_value if max_value else max(self.history)
        max_minus_min = maximum - min_value
        if max_minus_min == 0:
            return msg

        msg = ''
        for i in self.history:
            # (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min
            index = int(
                (((1.0 * i) - minimum) / max_minus_min)
                * len(self.scale_characters)
            )
            if index >= len(self.scale_characters):
                index = len(self.scale_characters) - 1
            msg += self.scale_characters[index]
        return msg.rjust(self.history_limit)
