# -*- coding: utf-8 -
#
# This file is part of restkit released under the MIT license. 
# See the NOTICE for more information.


"""
TeeInput replace old FileInput. It use a file 
if size > MAX_BODY or memory. It's now possible to rewind
read or restart etc ... It's based on TeeInput from Gunicorn.

"""
import os
from StringIO import StringIO
import tempfile

from restkit.sock import MAX_BODY, CHUNK_SIZE

class TeeInput(object):
    
    def __init__(self, socket, parser, buf, maybe_close=None):
        self.buf = StringIO()
        self.parser = parser
        self._sock = socket
        self.maybe_close = maybe_close
        self._is_socket = True
        self._len = parser.content_len
        
        if self._len and self._len < MAX_BODY:
            self.tmp = StringIO()
        else:
            self.tmp = tempfile.TemporaryFile()
        
        if buf.len > 0:
            chunk, self.buf = parser.filter_body(buf)
            if chunk:
                self.tmp.write(chunk)
                self.tmp.flush()
            self._finalize()
            self.tmp.seek(0)
            del buf
                    
    @property
    def len(self):
        if self._len: return self._len
        
        if self._is_socket:
            self.tmp.seek(0, 2)
            while True:
                if not self._tee(CHUNK_SIZE):
                    break
        self._len = self._tmp_size()
        return self._len
        
    def seek(self, offset, whence=0):
        """ naive implementation of seek """
        if self._is_socket:
            self.tmp.seek(0, 2)
            while True:
                if not self._tee(CHUNK_SIZE):
                    break
        self.tmp.seek(offset, whence)

    def flush(self):
        self.tmp.flush()
        
    def read(self, length=-1):
        """ read """
        if not self._is_socket:
            return self.tmp.read(length)
            
        if length < 0:
            buf = StringIO()
            buf.write(self.tmp.read())
            while True:
                chunk = self._tee(CHUNK_SIZE)
                if not chunk: 
                    break
                buf.write(chunk)
            return buf.getvalue()
        else:
            diff = self._tmp_size() - self.tmp.tell()
            if not diff:
                dest = StringIO(self._tee(length))
                return self._ensure_length(dest, length)
            else:
                l = min(diff, length)
                dest = StringIO(self.tmp.read(l))
                return self._ensure_length(dest, length)
                
    def readline(self, size=-1):
        if not self._is_socket:
            return self.tmp.readline(size)
        
        orig_size = self._tmp_size()
        if self.tmp.tell() == orig_size:
            if not self._tee(CHUNK_SIZE):
                return ''
            self.tmp.seek(orig_size)
        
        # now we can get line
        line = self.tmp.readline()
        i = line.find("\n")
        if i == -1:
            while True:
                orig_size = self.tmp.tell()
                if not self._tee(CHUNK_SIZE):
                    break
                self.tmp.seek(orig_size)
                line += self.tmp.readline()
                i = line.find("\n")
                if i != -1: 
                    break                    
        return line
       
    def readlines(self, sizehint=0):
        total = 0
        lines = []
        line = self.readline()
        while line:
            lines.append(line)
            total += len(line)
            if 0 < sizehint <= total:
                break
            line = self.readline()
        return lines
    
    def next(self):
        r = self.readline()
        if not r:
            raise StopIteration
        return r
    __next__ = next
    
    def __iter__(self):
        return self    

    def _tee(self, length):
        """ fetch partial body"""
        buf2 = self.buf
        buf2.seek(0, 2) 
        while True:
            chunk, buf2 = self.parser.filter_body(buf2)
            if chunk:
                self.tmp.write(chunk)
                self.tmp.flush()
                self.tmp.seek(0, 2)
                self.buf = StringIO()
                self.buf.write(buf2.getvalue())
                return chunk

            if self.parser.body_eof():
                break

            data = self._sock.recv(length)
            buf2.write(data)
                
        self._finalize()
        return ""
        
    def _finalize(self):
        """ here we wil fetch final trailers
        if any."""

        if self.parser.body_eof():
            if callable(self.maybe_close):
                self.maybe_close()
            
            del self.buf
            self._is_socket = False

    def _tmp_size(self):
        if isinstance(self.tmp, StringIO):
            return self.tmp.len
        else:
            return int(os.fstat(self.tmp.fileno())[6])
            
    def _ensure_length(self, dest, length):
        if not dest.len or not self._len:
            return dest.getvalue()
        while True:
            if dest.len >= length: 
                break
            data = self._tee(length - len(dest))
            if not data: 
                break
            dest.write(data)
        return dest.getvalue()

@@ -5185,17 +5185,16 @@
 h - 
-len(dest)
+dest.len
 )%0A  

fix ensure_length