"""
Provides a set of wrapping classes to make simple use of named pipes
in Windows very easy.

"""

__author__ = "Mathieu Fenniak <mfenniak@pobox.com>"
__credits__ = \
"""
Some of the code here was helped greatly by Mark Hammond's presentation notes
and sample code for a pipe based service.
    http://starship.python.net/crew/mhammond/conferences/ora99/
"""
__date__ = "2004-08-26"

import win32pipe
import win32file
import win32event
import pywintypes
import winerror

class NamedPipeReader(object):
    r"""
    Encapsulates some very basic functionality of a Windows named pipe.
    Currently this class only supports 'serving' a named pipe.  To use this
    class, construct an instance using a specific name.

    This class is specifically designed for a single connection to the pipe.
    Iterate over an instance of this class, and you'll get back the data this
    pipe has received from it's first connection (and it will block until it
    receives that first connection).

    This pipe can be written to by any other process - just make it write it's
    output to \\.\pipe\%(name)s
    """

    def __init__(self, name):
        self.name = name
        self.createPipe()
        self.connected = False

    def createPipe(self):
        # Create an ACL, but leave it empty to allow full access.
        sa = pywintypes.SECURITY_ATTRIBUTES()
        sa.SetSecurityDescriptorDacl(1, None, 0)

        self.overlapped = pywintypes.OVERLAPPED()
        self.overlapped.hEvent = win32event.CreateEvent(None, 0, 0, None)

        self.pipeHandle = win32pipe.CreateNamedPipe(
                "\\\\.\\pipe\\" + self.name,
                win32pipe.PIPE_ACCESS_DUPLEX | win32file.FILE_FLAG_OVERLAPPED,
                win32pipe.PIPE_TYPE_MESSAGE,
                win32pipe.PIPE_UNLIMITED_INSTANCES,
                0, 0, 6000, sa)
        hr = win32pipe.ConnectNamedPipe(self.pipeHandle, self.overlapped)
        if hr == winerror.ERROR_PIPE_CONNECTED:
            # client connected very quickly, signal connection event.
            win32event.SetEvent(self.overlapped.hEvent)

    def blockForConnection(self):
        hr = win32event.WaitForSingleObject(self.overlapped.hEvent, win32event.INFINITE)
        self.connected = True

    def __iter__(self):
        return self

    def next(self):
        if not self.connected:
            self.blockForConnection()
        try:
            hr, data = win32file.ReadFile(self.pipeHandle, 1024)
            return data
        except win32file.error, details:
            if details.args[0] == winerror.ERROR_BROKEN_PIPE:
                # client has disconnected
                raise StopIteration
            else:
                raise


class AnonymousNamedPipeReader(NamedPipeReader):
    """
    This class functions identically to NamedPipe, but provides a default name
    that can be retrieved through the 'name' property.  This can be useful
    when your pipe server is responsible for telling your client what pipe
    to use, and you don't really want to make sure that the name you're using
    is unique.
    """
    def __init__(self):
        import random
        super(AnonymousNamedPipeReader, self).__init__("anonpipe%s" % random.randint(0,2000000000))

