########################################################################
# $Header: /var/local/cvsroot/4Suite/Ft/Lib/ProgressIndicator.py,v 1.8 2005-04-18 23:17:57 jkloth Exp $
"""
Progress indicator

Copyright 2004 Fourthought, Inc. (USA).
Detailed license and copyright information: http://4suite.org/COPYRIGHT
Project home, documentation, distributions: http://4suite.org/
"""

import sys, time, os

from Ft.Lib import Terminal

class ProgressIndicator:
    """
    A progress indicator intended for terminal output (relies on ^H).

    Indicator style, given as constructor argument, can be
    0: percentage; 1: bar; or 2: both. Default is 0.

    If using styles 1 or 2, an optional width argument
    for the bar portion can also be given (default 60).

    Example usage:
    # First emit whatever prefaces the indicator, if desired
    print " status:",
    sys.__stdout__.flush()
    # Create a new indicator
    p = ProgressIndicator(2)
    p.newIndicator()
    # With each iteration through a task, or as often as you want,
    # call updateProgress(), passing 2 numbers: amount completed,
    # and total amount to do.
    limit = 300000
    for i in range(limit):
        p.updateProgress(i, limit)
    print
    """

    if os.name == 'nt' or os.name == 'dos':
        # dark/light shades... thanks Oleg Broytmann.
        # OK for all MS-DOS codepages except 864 (Arabic) & 874 (Thai)
        # and python doesn't seem to mind writing the non-ASCII chars.
        _hashchar = '\xb2'
        _blankchar = '\xb0'
    elif (os.environ.get('LANG', '').endswith('.UTF-8') and
          os.environ.get('TERM') == "xterm"):
        # UTF-8 enabled terminal
        #  25% => x2591
        #  50% => x2592
        #  75% => x2593
        # 100% => x2588
        _hashchar = u'\u2588'.encode('UTF-8')
        _blankchar = u'\u2591'.encode('UTF-8')
    else:
        _hashchar = '*'
        _blankchar = ' '

    _current = 0
    _total = 0

    def __init__(self, prefix, stream=sys.stdout):
        if type(prefix) == type(u''):
            # avoid possible UnicodeError during output
            self.prefix = prefix.encode('ascii','replace')
        else:
            self.prefix = prefix
        self._tty = Terminal.Terminal(stream)
        self._writetty = self._tty.writetty
        self._flushtty = self._tty.flush
        return

    def newIndicator(self, total):
        """
        Start a new indicator at 00%.
        Optional style and width arguments are same as constructor.
        """
        self._current = 0
        self._total = total
        self._showProgress()
        return

    def _erase(self):
        self._writetty("\r")

    def message(self,message):
        # left-justify message within column width, padding or trimming as
        # required.
        columns = self._tty.columns()
        message = '%-*.*s' % (columns, columns, message)

        self._erase()
        self._writetty(message + '\n');
        self._showProgress()

    def updateProgress(self, cur):
        """
        Update an existing indicator to reflect given progress.
        Arguments are amount completed so far, and total to do.
        For example, if 4 out of 30 have been completed, call
        updateProgress(4,30).
        """
        self._current = cur
        self._erase()
        self._showProgress()
        return

    def _showProgress(self):
        barwidth = self._tty.columns() - 3 - len(self.prefix) - 4
        hashwidth = int(float(self._current+1)/self._total * (barwidth - 2))
        pct = int((self._current+1)*100/self._total)
        self._writetty("%s |%s%s %s%%" % (
            self.prefix,
            self._hashchar * hashwidth,
            self._blankchar * (barwidth - hashwidth - 2) + "|",
            " " * (pct < 100) + "%02d" % pct))
        self._flushtty()
        return


class AutoProgressIndicator(ProgressIndicator):
    def __init__(self, prefix, total, step=1, stream=sys.stdout):
        ProgressIndicator.__init__(self, prefix, stream)
        self.newIndicator(total)
        self._step = 1
        return

    def advance(self):
        self.updateProgress(self._current + self._step)
        return
