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

# Copyright (c) 2002, 2003 Detlev Offenbach <detlev@die-offenbachs.de>
# Copyright (c) 2000 Phil Thompson <phil@river-bank.demon.co.uk>
#

"""
Module implementing an asynchronous file like socket interface for the debugger.
"""

import socket


def AsyncPendingWrite(file):
    """
    Module function to check for data to be written.
    
    Arguments
    
        file -- the file object to be checked (file)
        
    Returns
    
        flag indicating if there is data wating (int)
    """
    try:
        pending = file.pendingWrite()
    except:
        pending = 0

    return pending


class AsyncFile:
    """
    Class wrapping a socket object with a file interface.
    """
    def __init__(self,sock,mode,name):
        """
        Constructor
        
        Arguments
        
            sock -- the socket object being wrapped
            
            mode -- mode of this file (string)
            
            name -- name of this file (string)
        """
        # Initialise the attributes.
        self.closed = 0
        self.sock = sock
        self.mode = mode
        self.name = name
        self.softspace = 0

        self.wpending = ''

    def checkMode(self,mode):
        """
        Private method to check the mode.
        
        This method checks, if an operation is permitted according to
        the mode of the file. If it is not, an IOError is raised.
        
        Arguments
        
            mode -- the mode to be checked (string)
        """
        if mode != self.mode:
            raise IOError, '[Errno 9] Bad file descriptor'

    def nWrite(self,n):
        """
        Private method to write a specific number of pending bytes.
        
        Arguments
        
            n -- the number of bytes to be written (int)
        """
        if n:
            try :
                sent = self.sock.send(self.wpending[:n])
                self.wpending = self.wpending[sent:]
            except:
                pass

    def pendingWrite(self):
        """
        Public method that returns the number of bytes waiting to be written.
        
        Returns
        
            the number of bytes to be written (int)
        """
        return self.wpending.rfind('\n') + 1

    def close(self):
        """
        Public method to close the file.
        """
        if not self.closed:
            self.flush()
            self.sock.close()
            self.closed = 1

    def flush(self):
        """
        Public method to write all pending bytes.
        """
        self.nWrite(len(self.wpending))

    def isatty(self):
        """
        Public method to indicate whether a tty interface is supported.
        
        Returns
        
            always false
        """
        return 0

    def fileno(self):
        """
        Public method returning the file number.
        
        Returns
        
            file number (int)
        """
        return self.sock.fileno()

    def read(self,size=-1):
        """
        Public method to read bytes from this file.
        
        Arguments
        
            size -- maximum number of bytes to be read (int)
            
        Returns
        
            the bytes read (any)
        """
        self.checkMode('r')

        if size < 0:
            size = 20000

        return self.sock.recv(size)

    def readline(self,size=-1):
        """
        Public method to read a line from this file.
        
        Arguments
        
            size -- maximum number of bytes to be read (int)
            
        Returns
        
            one line of text up to size bytes (string)
            
        Note
        
            This method will not block and may return only a part of a
            line if that is all that is available.
        """
        self.checkMode('r')

        if size < 0:
            size = 20000

        # The integration of the debugger client event loop and the connection
        # to the debugger relies on the two lines of the debugger command being
        # delivered as two separate events.  Therefore we make sure we only
        # read a line at a time.
        line = self.sock.recv(size,socket.MSG_PEEK)

        eol = line.find('\n')

        if eol >= 0:
            size = eol + 1
        else:
            size = len(line)

        # Now we know how big the line is, read it for real.
        return self.sock.recv(size)

    def readlines(self,sizehint=-1):
        """
        Public method to read all lines from this file.
        
        Arguments
        
            sizehint -- hint of the numbers of bytes to be read (int)
            
        Returns
        
            list of lines read (list of strings)
        """
        lines = []
        room = sizehint

        line = self.readline(room)
        linelen = len(line)

        while linelen > 0:
            lines.append(line)

            if sizehint >= 0:
                room = room - linelen

                if room <= 0:
                    break

            line = self.readline(room)
            linelen = len(line)

        return lines

    def seek(self,offset,whence=0):
        """
        Public method to move the filepointer.
        
        This method is not supported and always raises an
        IOError.
        """
        raise IOError, '[Errno 29] Illegal seek'

    def tell(self):
        """
        Public method to get the filepointer position.
        
        This method is not supported and always raises an
        IOError.
        """
        raise IOError, '[Errno 29] Illegal seek'

    def truncate(self,size=-1):
        """
        Public method to truncate the file.
        
        This method is not supported and always raises an
        IOError.
        """
        raise IOError, '[Errno 29] Illegal seek'

    def write(self,str):
        """
        Public method to write a string to the file.
        
        Arguments
        
            str -- bytes to be written (string)
        """
        self.checkMode('w')
        self.wpending = self.wpending + str
        self.nWrite(self.pendingWrite())

    def writelines(self,list):
        """
        Public method to write a list of strings to the file.
        
        Arguments
        
            list -- the list to be written (list of string)
        """
        map(self.write,list)
