"""
This module specializes Processor to calculate the frequency of the topic
"""
from __future__ import absolute_import
from __future__ import division
from __future__ import print_function

import collections
import sys

from .processor import Processor


class Bandwidth(Processor):
    """
    Provides a Processor to obtain the topic's bandwidth.
    """
    def __init__(self, queue,
                 clear = False,
                 print_frequency_hz = 1,
                 window = 100,
                 timestamp_accessor = "msg.META.publish_time_ns"):
        super(Bandwidth, self).__init__(queue)
        self._clear = clear
        self.print_period_ns = (1/print_frequency_hz)*1e9
        self.last_printed = 0
        self._sizes = collections.deque(maxlen=window)
        self._times = collections.deque(maxlen=window)
        self._timestamp_accessor = self._get_timestamp_accesor(
            timestamp_accessor)

    def _print_bw_info(self,
                       time_since_last_print,
                       current_time):
        """ Prints the topic's bandwidth information."""
        if time_since_last_print >= self.print_period_ns:
            if self._clear:
                # ANSI excape sequence
                # \x1b[2J: Clear the screen
                # \x1b[H:  Reset cursor to 0,0
                print('\x1b[2J\x1b[H')
            n = len(self._sizes)
            t0 = self._times[0]
            tn = self._times[-1]
            timespan = (tn-t0)/1e9
            total = sum(self._sizes)
            mean = total/n
            max_s = max(self._sizes)
            min_s = min(self._sizes)
            bytes_per_s = total / timespan
            if bytes_per_s < 1000:
                bw, mean, min_s, max_s = ["%.2fB"%v for v in
                                          [bytes_per_s, mean, min_s, max_s]]
            elif bytes_per_s < 1000000:
                bw, mean, min_s, max_s = ["%.2fKB"%(v/1000) for v in
                                          [bytes_per_s, mean, min_s, max_s]]
            else:
                bw, mean, min_s, max_s = ["%.2fMB"%(v/1000000) for v in
                                          [bytes_per_s, mean, min_s, max_s]]
            print("average: %s/s\n\tmean: %s min: %s max: %s window: %s"%
                  (bw, mean, min_s, max_s, n))
            self.last_printed = current_time

    @staticmethod
    def _get_timestamp_accesor(string_time_accessor):
        split_time_accessor = string_time_accessor.split('.')
        if (len(split_time_accessor) != 3 or
            split_time_accessor[0] != "msg" or
            split_time_accessor[1] != "META"):
            raise ValueError("Wrong time accesor {}. It must be provided in the"
                             " format \"msg.META.publish_time_ns\".".format(
                             string_time_accessor))
        else:
            return split_time_accessor[2]

    def process(self, msg):
        """
        Process the queue, obtaining the timestamp of the last message and
        adding its size to tthe queue.
        """
        timestamp = getattr(msg.META, self._timestamp_accessor)
        data_size = len(msg.SERIALIZED)
        time_size = 8
        # 2 bytes magic
        # 2 bytes version
        # 4 bytes header size
        # 4 bytes type_size
        # 12 bytes data_size
        overhead_size = 20
        header_size = len(msg.META.type_name) + \
            len(msg.META.publisher_id) + \
            len(msg.META.frame_id)+ \
            time_size + \
            time_size + \
            overhead_size
        self._sizes.append(data_size+header_size)
        self._times.append(msg.META.publish_time_ns)
        time_since_last_print = timestamp - self.last_printed
        self._print_bw_info(time_since_last_print, timestamp)
