# coding=utf8
# This file is part of Xpra.
# Copyright (C) 2011 Serviware (Arthur Huillet, <ahuillet@serviware.com>)
# Copyright (C) 2010-2013 Antoine Martin <antoine@devloop.org.uk>
# Copyright (C) 2008 Nathaniel Smith <njs@pobox.com>
# Xpra is released under the terms of the GNU GPL v2, or, at your option, any
# later version. See the file COPYING for details.

#how many historical records to keep
#for the various statistics we collect:
#(cannot be lower than DamageBatchConfig.MAX_EVENTS)
NRECS = 100

from xpra.deque import maxdeque
from xpra.simple_stats import add_list_stats


class DamageBatchConfig(object):
    """
    Encapsulate all the damage batching configuration into one object.
    """
    ALWAYS = False
    MAX_EVENTS = min(50, NRECS)         #maximum number of damage events
    MAX_PIXELS = 1024*1024*MAX_EVENTS   #small screen at MAX_EVENTS frames
    TIME_UNIT = 1                       #per second
    MIN_DELAY = 5                       #lower than 5 milliseconds does not make sense, just don't batch
    START_DELAY = 50
    MAX_DELAY = 500
    TIMEOUT_DELAY = 15000
    RECALCULATE_DELAY = 0.04            #re-compute delay 25 times per second at most
                                        #(this theoretical limit is never achieved since calculations take time + scheduling also does)

    def __init__(self):
        self.always = self.ALWAYS
        self.max_events = self.MAX_EVENTS
        self.max_pixels = self.MAX_PIXELS
        self.time_unit = self.TIME_UNIT
        self.min_delay = self.MIN_DELAY
        self.max_delay = self.MAX_DELAY
        self.timeout_delay = self.TIMEOUT_DELAY
        self.delay = self.START_DELAY
        self.locked = False                             #to force a specific delay
        self.last_delays = maxdeque(64)                 #the delays we have tried to use (milliseconds)
        self.last_actual_delays = maxdeque(64)          #the delays we actually used (milliseconds)
        self.last_updated = 0
        self.wid = 0
        #the metrics derived from statistics which we use for calculating the new batch delay:
        #(see batch delay calculator)
        self.factors = []


    def add_stats(self, info, prefix, suffix=""):
        info[prefix+"batch.min-delay"] = self.min_delay
        info[prefix+"batch.max-delay"] = self.max_delay
        info[prefix+"batch.timeout-delay"] = self.timeout_delay
        info[prefix+"batch.locked"] = self.locked
        if len(self.last_delays)>0:
            batch_delays = [x for _,x in list(self.last_delays)]
            add_list_stats(info, prefix+"batch.delay"+suffix, batch_delays)
        if len(self.last_actual_delays)>0:
            batch_delays = [x for _,x in list(self.last_actual_delays)]
            add_list_stats(info, prefix+"batch.actual_delay"+suffix, batch_delays, show_percentile=[9])
        for name, details, factor, weight in self.factors:
            key = prefix+"batch."+name
            info[key+suffix] = (int(100.0*factor), int(100.0*weight))
            for k,v in details.items():
                info[key+"."+k+suffix] = v

    def clone(self):
        c = DamageBatchConfig()
        for x in ["always", "max_events", "max_pixels", "time_unit",
                  "min_delay", "max_delay", "timeout_delay", "delay"]:
            setattr(c, x, getattr(self, x))
        return c

    def __str__(self):
        return  "DamageBatchConfig(wid=%s, always=%s, min=%s, max=%s, current=%s, locked=%s, max events=%s, max pixels=%s, time unit=%s)" % \
                (self.wid, self.always, self.min_delay, self.max_delay, self.delay, self.locked, self.max_events, self.max_pixels, self.time_unit)
