import time

class AverageMeter(object):
    """ Computes and stores the average and current value"""
    def __init__(self, name, fmt=':f'):
        self.name = name
        self.fmt = fmt
        self.reset()

    def reset(self):
        self.val = 0
        self.avg = 0
        self.sum = 0
        self.count = 0

    def update(self, val, n=1):
        self.val = val
        self.sum += val * n
        self.count += n
        self.avg = self.sum / self.count

    def __str__(self):
        fmtstr = '{name} {val' + self.fmt + '} ({avg' + self.fmt + '})'
        return fmtstr.format(**self.__dict__)

class Timer(object):
    def __init__(self, name):
        self.name = name
        self.reset()
    
    def reset(self):
        self.val = 0
        self.avg = 0
        self.sum = 0
        self.count = 0
        self.start = time.time()
    
    def update(self):
        self.val = time.time() - self.start
        self.start = time.time()
        self.sum += self.val
        self.count += 1
        self.avg = self.sum / self.count
    
    def s2dhms(self, times):
        days = int(times / 3600 / 24)
        wait_times = times - 3600 * 24 * days
        hours = int(wait_times / 3600)
        wait_times = wait_times - 3600 * hours
        minutes = int(wait_times / 60)
        seconds = int(wait_times)
        return days, hours, minutes, seconds
    
    def predict(self, n_epochs):
        od, oh, om, os = self.s2dhms(self.sum)
        p_times = self.acg * n_epochs
        d, h, m, s = self.s2dhms(p_times)
        return '{}d {}h {}m {}s'.format(od, oh, om, os), '{}d {}h {}m {}s'.format(d, h, m, s)

class ProgressMeter(object):
    def __init__(self, num_batches, meters, prefix=""):
        self.batch_fmtstr = self._get_batch_fmtstr(num_batches)
        self.meters = meters
        self.prefix = prefix

    def display(self, batch, logger):
        entries = [self.prefix + self.batch_fmtstr.format(batch)]
        entries += [str(meter) for meter in self.meters]
        logger.info('\t'.join(entries))

    def _get_batch_fmtstr(self, num_batches):
        num_digits = len(str(num_batches // 1))
        fmt = '{:' + str(num_digits) + 'd}'
        return '[' + fmt + '/' + fmt.format(num_batches) + ']'
