"""



   - maybe only be fetching if someone is watching...?

"""

from __future__ import with_statement

import sys
import urllib2
import re
import time
import threading

class EndOfStream (Exception):
    pass

lag = 0

# just (timestamp, data)
# [0] is most recent
# truncate after some small amount
frame_history = []
frame_history_lock = threading.Lock()

def get_latest_frame():
    global frame_history
    global frame_history_lock
    
    with frame_history_lock:
        try:
            result = frame_history[0]
        except:
            result = (0, "")

    return result


chars_on_line = 0
def same_line(text):
    global chars_on_line
    sys.stdout.write((chr(8)+" "+chr(8)) * chars_on_line + text)
    chars_on_line = len(text)
    sys.stdout.flush()


pat = re.compile(r'''((\r\n)?--boundarydonotcross\r\n(Content-Type: image/jpeg\r\nContent-Length: (\d+)\r\n\r\n))?''')
def read_frame(input_stream):

   b = input_stream.read(1000)
   if len(b) == 0:
       raise EndOfStream

   #sys.stdout.write("r")
   #sys.stdout.flush()

   m = pat.match(b)
   if m is None:
       print "boundary marker not found"
       print "instead, got: ", `b`
       raise RuntimeError
   
   if m.group(4) == None:
       raise EndOfStream
       
   header_length = len(m.group(1))
   data_length = int(m.group(4))
   
   b = b[header_length:]
   bytes_needed = data_length - len(b)
   data = b + input_stream.read(bytes_needed)

   return data

def push_frame(data):
    global frame_history
    global frame_history_lock

    with frame_history_lock:
        now = time.time()
        frame_history = [ (now, data) ] + frame_history[0:50]

def save_frame(data, frame_count):

    frame_file_stream = open("frame/f_%06d.jpg" % frame_count, "w")
    frame_file_stream.write(data)
    frame_file_stream.close()

def fps():
    global frame_history
    global frame_history_lock

    with frame_history_lock:

        if len(frame_history) < 2:
            return 1.0
        now = frame_history[0][0]
        for i in range(1, len(frame_history)):
            then = frame_history[i][0]
            if (now-then) > 2 or i == (len(frame_history)-1):
                return (1.0 * i) / (now-then)
    
def note_frame(frame_count):
    same_line(("frame %06d  avg-fps=%4.1f" % (frame_count, fps())) + xy()+ " lag: "+str(lag))

def xy():
    return ""

    global frame_history
    global frame_history_lock

    #  http://www.pythonware.com/library/pil/handbook/introduction.htm
    import StringIO
    import Image
    im = Image.open(StringIO.StringIO(get_latest_frame()[1]))
    return " size="+str(im.size)


class Retriever (threading.Thread):

    def run(self):
        # caller sets self.url
        self._keep_running = True
        self._keep_running_lock = threading.Lock()
        frame_count = 0
        retry = 0

        error_image = []
        for i in range(1,20):
            try:
                # should use size, to match expected stream size...
                # since browsers (firefox 3, at least) wont change
                # size....
                f = open('no-camera-%d-320.jpg' % i, 'r')
                error_image.append(f.read())
                f.close()
            except:
                pass

        while self.keep_running():

           try:
               input_stream = urllib2.urlopen(self.url)
           except urllib2.URLError, e:
               if (e.reason[0] == 111 or e.reason[0]==104):
                   same_line('%s; retrying %d...' % (str(e), retry))
                   time.sleep(3)
                   retry += 1
                   push_frame(error_image[retry % len(error_image)])
                   continue
               else:
                   raise e

           retry = 0
           print 
           print "Connection established."


           try:
               while self.keep_running():
                   data = read_frame(input_stream)
                   push_frame(data)
                   note_frame(frame_count)
                   if (frame_count % 100) == 0:
                       save_frame(data, frame_count)
                   frame_count += 1
           except EndOfStream:
               print 
               print "Stream closed."

        print
        print 'Image stream retriever stopped.'

    def keep_running(self):
        with self._keep_running_lock:
            return self._keep_running

    def stop(self):
        with self._keep_running_lock:
            self._keep_running = False

def next_frame(state, fps):

    '''Return a jpeg of the next frame this client should see; waiting
    as long as necessary until it's ready, given the target FPS for
    this client.

    maybe set kbps?

    '''
    global lag

    #print
    #print
    now = time.time()
    seconds_ago = now - getattr(state, 'frame_when', 0)
    #print "last image had timestamp  %f seconds ago" % seconds_ago
    sleep_time = ( (1.0 / fps) - seconds_ago)
    #print "that means I should sleep for %f seconds" % sleep_time
    if sleep_time > 0.001:
        #print "sleeping until next frame we might want, %f seconds" % sleep_time
        time.sleep(sleep_time)

    # wait until there's a new frame we haven't sent before
    while True:
        (when, data) = get_latest_frame()
        if when == getattr(state, "frame_when", None):
            time.sleep(0.01)  # should use a semaphore, I know...
            continue
        break

    state.frame_when = when
    now = time.time()
    state.time_last_served = now
    #print 'returnin frame that is %f seconds old' % (now - when)
    #print
    lag = now-when
    return data

if __name__ == '__main__':
    retriever = Retriever()
    retriever.start()
 
