import math
import sys
import time


def human_readable_duration(seconds):
    units = ['s', 'm', 'h']
    humanradix = 60
    humanfmt = '%.1f%s'
    size = seconds

    if size < humanradix:
        return humanfmt % (size, units[0])

    for u in units[:-1]:
        if size < humanradix:
            return humanfmt % (size, u)
        size /= humanradix

    return humanfmt % (size, units[-1])


class Progress:

    def __init__(self, maximum, entries):
        self.index = 0
        self.maximum = maximum
        self.entries = entries
        self.moments = time.time()
        self.when = 0

        self.goto(0)

    @property
    def proportion(self):
        return min(1, self.index / self.maximum)

    @property
    def duration(self):
        return time.time() - self.moments

    def draw(self):
        n = time.time()
        if self.index not in [0, self.maximum]:
            if n - self.when < 0.17:
                return
        self.when = n
        keys = []
        for entry in self.entries:
            entry.progress = self
            keys.append(entry.description())
        sys.stdout.write('\r' + ' '.join(keys))

    def goto(self, index):
        self.index = index
        self.draw()

    def next(self, n=1):
        self.goto(self.index + n)

    def done(self):
        self.goto(self.maximum)
        sys.stdout.write('\n')

    def iter(self, it):
        for x in it:
            yield x
            self.next()


class Percent:

    def __init__(self):
        self.progress = None

    def description(self):
        return str(int(self.progress.proportion * 100)).rjust(3) + '%'


class Line:

    def __init__(self, width=None):
        self.progress = None
        self.width = width or 47

    def description(self):
        width = self.width * self.progress.proportion
        width_integer = math.floor(width)
        line = '=' * width_integer + '>' + ' ' * (self.width - width_integer)
        line = '[' + line + ']'
        return line


class PercentLine:

    def __init__(self, width=None):
        self.progress = None
        self.width = width

    def description(self):
        percent = Percent()
        percent.progress = self.progress
        line = Line(width=self.width)
        line.progress = self.progress
        return percent.description() + line.description()


class String:

    def __init__(self, string):
        self.progress = None
        self.string = string

    def description(self):
        return self.string


class SLD:

    def __init__(self):
        self.progress = None

    def description(self):
        return human_readable_duration(self.progress.duration).rjust(5)


class ETA:

    def __init__(self):
        self.progress = None

    def description(self):
        if self.progress.index == 0:
            return ' ' * 5
        rest = self.progress.duration / self.progress.index * (self.progress.maximum - self.progress.index)
        return human_readable_duration(rest).rjust(5)


if __name__ == '__main__':
    progress = Progress(maximum=500, entries=[PercentLine(), ETA()])
    for i in range(500):
        time.sleep(0.01)
        progress.next()
    progress.done()
