
"""
downloadAgent

This download agent makes it possible to return the current status of a file
transfer.
"""
from __future__ import division

from twisted.internet.protocol import Protocol
from twisted.web.client import Agent
from twisted.python import log
from twisted.web.http_headers import Headers
from twisted.internet.defer import Deferred


class IOHandler(object):
    """
    IOHandler opens files, writes to them, and closes them.
    """
    def __init__(self):
        self._openedFile = None

    def open(self, path):
        """
        Open a file like object
        """
        self._openedFile = open(path, 'w')

    def close(self):
        """
        Close a file like object.
        """
        self._openedFile.close()

    def write(self, data):
        """
        Write to the file held in self.
        :param data: the data to write
        """
        self._openedFile.write(data)


class FileWriter(Protocol):
    """
    FileWriter writes the data to a file like object and can report the
    progress of a file transfer.
    """
    def __init__(self, finished, filesize, filename, ioHandler, progress):
        """
        :param finished: a callback to be called when transmission has finished
        :param filesize: the expected length of the file
        :param filepath: the path to where the new file should be saved.
        :param ioHandler: an object can open, write to, and close file-like
                          objects.
        """
        self._finished = finished
        self._written = 0
        self._filesize = filesize
        self._remaining = filesize
        self._ioHandler = ioHandler
        self._openedFile = self._ioHandler.open(filename)
        self._progress = progress

    def currentProgress(self):
        """
        Return the current percentage of the file transferred.
        """
        return (self._written / self._filesize) * 100

    def dataReceived(self, data):
        """
        Write the bites to a supplied file and update progress in the log.
        """
        if self._remaining > 0:
            self._written += len(data)
            self._remaining -= len(data)
            self._ioHandler.write(data)
            self._progress.add(self.currentProgress())
        else:
            self.connectionLost()

    def connectionLost(self, reason):
        """ Connectiong closed """
        log.msg('Finished receiving body:', reason.getErrorMessage(),
                system="FileWriter")
        self._ioHandler.close()
        self._finished.callback(None)


class DownloadProgress(object):
    """
    DownloadProgress is meant to report the status of a download. It does
    this by maintaining an internal progress counter.
    """
    def __init__(self):
        self._progress = [0.0]

    def add(self, increase):
        self._progress.append(increase)

    def current(self):
        return self._progress[-1]


def getFile(reactor, url, filename, ioHandler, progress):
    """
    Download file is similar to web.client.getPage except it is able
    the progress of the download in real time.

    :param reactor: the reactor being used by the system
    :param url: the url where the file is located
    :param filename: the filename to write the file to.
    :param progress: an object to which period progress updates can be added.

    :returns: a deferred object.
    """
    d = Agent(reactor).request('GET', url,
                               Headers({'User-Agent': ['Teiler']}),
                               None)

    def cbRequest(response):
        """
        This callback fires when the response headers have been received.
        """
        finished = Deferred()
        response.deliverBody(FileWriter(finished, response.length,
                                        filename, ioHandler,
                                        progress))
        return finished
    d.addCallback(cbRequest)
    return d

@@ -1466,16 +1466,61 @@
 bjects.%0A
+        :param progress: an object that can %0A
         
@@ -2660,36 +2660,27 @@
 ess 
-is meant to 
 report
+s
  the 
-statu
+progres
 s of
@@ -2903,32 +2903,123 @@
  current(self):%0A
+        %22%22%22%0A        current returns the most recently received progress state.%0A        %22%22%22%0A
         return s
@@ -3328,16 +3328,16 @@
 located%0A
-
     :par
@@ -3384,16 +3384,128 @@
 ile to.%0A
+    :param ioHandler: an object that can perform open, write, close on file-like%0A                      objects.%0A
     :par

fix docstrings up