# -*- coding: utf-8 -*-

# Copyright (C) 2011-2014 Avencall
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program.  If not, see <http://www.gnu.org/licenses/>

from builtins import object
OIP_WAITING = 'waiting'
OIP_PROGRESS = 'progress'
OIP_SUCCESS = 'success'
OIP_FAIL = 'fail'


class OperationInProgress(object):
    """Base class for operations in progress.
    
    An operation in progress is a monitor over an underlying operation. It's
    used to expose the status of an underlying operation in a standard way.
    
    Operation in progress instances have the following attributes:
      label -- the label identifying the underlying operation, or None
      state -- the state of the operation; one of waiting, progress, success
       or fail
      current -- a non-negative integer representing the current 'step' of
        the operation, or None
      end -- a positive integer representing the last 'step' of the operation,
        or None
      sub_oips -- a list of operation in progress instances representing sub
        operations of the underlying operation
    
    Here's some general rules to follow:
    - an operation state goes from waiting to progress to one of success or
      fail. From the exterior, the operation state MUST always follow this
      sequence.
    - an operation that has sub operations is completed (i.e. in state success
      or fail) only after all of its sub operations are completed.
    - you can add more sub operations to an operation but you can't remove
      already added sub operations.
    
    """
    def __init__(self, label=None, state=OIP_WAITING, current=None, end=None, sub_oips=[]):
        self.label = label
        self.state = state
        self.current = current
        self.end = end
        self.sub_oips = list(sub_oips)


def format_oip(oip):
    """Format an operation in progress to a string.
    
    The format is '[label|]state[;current[/end]](\(sub_oips\))*'.
    
    Here's some examples:
      progress
      download|progress
      download|progress;10
      download|progress;10/100
      download|progress(file_1|progress;20/100)(file_2|waiting;0/50)
      download|progress;20/150(file_1|progress)(file_2|waiting)
      op|progress(op1|progress(op11|progress)(op12|waiting))(op2|progress)
      
    """
    s = ''
    if oip.label is not None:
        s += '%s|' % oip.label
    s += oip.state
    if oip.current is not None:
        s += ';%s' % oip.current
        if oip.end:
            s += '/%s' % oip.end
    for sub_oip in oip.sub_oips:
        s += '(%s)' % format_oip(sub_oip)
    return s


def operation_in_progres_from_deferred(deferred, label=None):
    """Return an operation in progress where the underlying operation is
    determined by a deferred.
    
    """
    def callback(res):
        oip.state = OIP_SUCCESS
        return res
    def errback(err):
        oip.state = OIP_FAIL
        return err
    oip = OperationInProgress(label, OIP_PROGRESS)
    deferred.addCallbacks(callback, errback)
    return oip
