"""
Input buffering code for AsyncThreadedAppServer.

This module creates a file interface to data being received through
an asynconous socket.

Based entirely on code contributed by Vladimir Kralik.
Tweaked by Jay Love.

"""


import string

class ATASStreamIn:
    # methods to simulating read file-access to ayncore.dispatcher,
    # idea from StringIO

    def __init__(self, dispatcher, buffersize=8192):
        self._dispatcher=dispatcher
        self._buffersize=buffersize
        self.reset()

    def reset(self):
        self.len = 0
        self.buflist = []
        self.closed = 0
        self.softspace = 0
        self._hasMore=1     # in socket
        self._notQueued=1   # in request queue
 
    def dataAvailable(self):
        return not self._hasMore

 
    def canRecv(self):
        return self._hasMore and self._notQueued
        
    def recv(self):
        assert self._hasMore and self._notQueued
        
        data = self._dispatcher.recv(8192)
        lendata=len(data)
        self.len=self.len+lendata

        if lendata > 0:
            self.buflist.append(data)
        else:
            self.buflist.append(data)
            self._hasMore=0 # all is readed

        if self._hasMore and self.len < self._buffersize:
            pass
        else:
            self._notQueued=0
            self._dispatcher.server.requestQueue.put(self._dispatcher)
            
    # file access 
    def close(self):
        if not self.closed:
            self.closed = 1
            del self.buflist, self.len

    def isatty(self):
        if self.closed:
            raise ValueError, "I/O operation on closed file"
        return 0
    
    def read(self, n = -1):
        assert not self._notQueued

        if self.closed:
            raise ValueError, "I/O operation on closed file"
        if n==0: return ''
        elif n < 0: # read all data
            if self._hasMore: # has more data in socket, read it 
                sock=self._dispatcher.socket
                try :
                    sock.setblocking(1)
                    while 1:
                        data=sock.recv(8192)
                        if not data: break
                        self.buflist.append(data)
                finally:
                    sock.setblocking(0)
                    self._hasMore=0
            return string.joinfields(self.buflist,'')
        elif self.buflist:
            outlist=[]
            outlen=0
            while self.buflist:
                data=self.buflist.pop(0)
                lendata=len(data)
                outlen=outlen+lendata
                if outlen>n: break
                outlist.append(data)
            if outlen>n:
                diff=n-outlen+lendata
                outlist.append(data[:diff])
                self.buflist.insert(0, data[diff:])
            elif self._hasMore and outlen<n:
                outlist.append(self.read(n-outlen))
            return string.joinfields(outlist,'')
        elif self._hasMore:
            sock=self._dispatcher.socket
            try :
                sock.setblocking(1)
                data=sock.recv(n)
            finally:
                sock.setblocking(0)
                self._hasMore=0
            return data
        else: return ''

    def _readlinedata(self,data,length,outlist):
        i = string.find(data, '\n')
        if i<0: # not found
            if length and len(data)>=length:
                i=length
            else:
                outlist.append(data)
                return (0,length and length-len(data))
        elif length and i>=length: i=length
        else: i=i+1
        
        dd=data[:i]
        outlist.append(dd)
        self.buflist.insert(0,data[i:])
        return (1,length and length-len(data)) # read all 
        

    def readline(self, length=None):
        assert not self._notQueued
        if self.closed:
            raise ValueError, "I/O operation on closed file"

        if length==0: return ''
        elif self.buflist:
            outlist=[]
            while self.buflist:
                data=self.buflist.pop(0)
                con,length = self._readlinedata(
                    data,length,outlist)
                if con: break
            if self._hasMore and not con:
                outlist.append(self.readline(length))
            return string.joinfields(outlist,'')
        elif self._hasMore:
            sock=self._dispatcher.socket
            try :
                outlist=[]
                sock.setblocking(1)
                while 1:
                    if self.buflist:data=self.buflist.pop(0)
                    else:
                        data=sock.recv(8192)
                        if not data:
                            self._hasMore=0
                            break
                    con,length = self._readlinedata(
                        data,length,outlist)
                    if con: break
                return string.joinfields(outlist,'')
            finally:
                sock.setblocking(0)
        else: return ''

    def readlines(self):
        lines = []
        line = self.readline()
        while line:
            lines.append(line)
            line = self.readline()
        return lines

    def flush(self):
        if self.closed:
            raise ValueError, "I/O operation on closed file"