# -*- coding: utf8 -*-

import math
import time

from monotonic import monotonic
from error import ClientError


class DelayReader(object):
  def __init__(self, data, bytes_per_second, times_per_second=20, sleep_func=time.sleep):
    self._data = data
    self._bytes_per_second = bytes_per_second
    self._times_per_second = times_per_second
    self._sleep = sleep_func
    self._per_elapsed = 1.0 / times_per_second
    if self._bytes_per_second < 1024:
      self._bytes_per_second = 1024
    self._chunk = self._bytes_per_second // times_per_second
    self._now = None
    self._next_time = None
    self._bucket = self._chunk
    #print(self._chunk)

  def __iter__(self):
    return self
  
  def __next__(self):
    return self.next()

  def next(self):
    content = self.read(self._chunk)

    if content:
      return content

    return StopIteration

  def _update_elapsed(self):
    now = monotonic()
    if now > self._next_time:
      elapsed = now - self._next_time
      ticks = int(math.ceil(elapsed / self._per_elapsed))
      self._bucket += ticks * self._chunk
      self._next_time += ticks * self._per_elapsed
  
  def _read(self, size=None):
    if hasattr(self._data, 'read'):
      return self._data.read(size)
    else:
      raise ClientError('{0} is not a file object, '.format(data.__class__.__name__))

  def read(self, size=None):
    if self._now is None:
      self._now = monotonic()
      self._next_time = self._now + self._per_elapsed
    
    if size is None:
      size = self._chunk

    self._update_elapsed()
    
    if size > self._bucket:
      now = monotonic()
      ticks = (size - self._bucket) / self._chunk
      time_df = self._next_time - now - self._per_elapsed
      delay = ticks * self._per_elapsed
      #print('ticks={0}, time_diff={1}, delay={2}'.format(ticks, time_df, delay))
      if time_df > 0:
        delay += time_df
      self._sleep(delay)
      self._update_elapsed()

    data = self._read(size)
    self._bucket -= size
    return data
  
  @property
  def chunk_size(self):
    return self._chunk


if __name__ == "__main__":
  with open('20m.vbox', 'rb') as f:
    dr = DelayReader(f, 1024000, 20)

    t_zero = time.time()
    while True:
      data = dr.read(2048000)
      print("Got %d at %0.2f" % (len(data),  time.time() - t_zero))
      if len(data) == 0:
        break
  
