import time
import threading
import warnings
from collections import namedtuple
import Queue

from .utils import CallbackList

ButtonEvent = namedtuple('ButtonEvent', ('state', 'timestamp',))
ButtonAction = namedtuple('ButtonAction', ('type', 'timestamp',))

event_types = ['press','hold','down','up']

class Button(object):
    def __init__(self):
        self.event_queue = Queue.Queue()
        self.last_event = None
        self.hold_time = 1.0
        self.last_hold_check_time = 0
        self.callbacks = {x:CallbackList() for x in event_types}
        self.actions = []

    def process_events(self, time):
        while True:
            try:
                event = self.event_queue.get(block=False)
            except Queue.Empty:
                break

            self.fire_hold_if_due(event.timestamp)

            if event.state == 'down':
                self.fire(ButtonAction('down', timestamp=event.timestamp))
            elif event.state == 'up':
                self.fire(ButtonAction('up', timestamp=event.timestamp))

                # check to see if press should be fired
                if self.last_event.state == 'down':
                    press_duration = event.timestamp - self.last_event.timestamp

                    if press_duration < self.hold_time:
                        self.fire(ButtonAction('press', timestamp=event.timestamp))

            self.last_event = event

        self.fire_hold_if_due(time)

    def fire_hold_if_due(self, time):
        ''' check to see if the hold event needs to be fired '''

        if self.last_event and self.last_event.state == 'down':
            hold_trigger_time = self.last_event.timestamp + self.hold_time

            if self.last_hold_check_time < hold_trigger_time <= time:
                self.fire(ButtonAction('hold', timestamp=hold_trigger_time))

        self.last_hold_check_time = time

    def fire(self, action):
        self.actions.append(action)
        
    def run_callbacks(self):
        for action in self.actions:
            self.callbacks[action.type]()
        self.actions = []
        
    def add_event(self,action,timestamp=None):
        if timestamp is None:
            timestamp = time.time()
        self.event_queue.put(ButtonEvent(action,timestamp))

    def add_callback(self,event,func):
        ensure_setup()
        self.callbacks[event].add(func)
        
    def __call__(self,f):
        print "calling"
        self.down(f)
       
    def down(self,f):
        self.add_callback('down',f)

    def up(self,f):
        self.add_callback('up',f)

    def press(self,f):
        self.add_callback('press',f)

    def hold(self,f):
        self.add_callback('hold',f)

#create buttons
button_names = ('left','midleft','midright','right')
buttons = {x:Button() for x in button_names}
(left_button,midleft_button,midright_button,right_button) = [buttons[x] for x in button_names]

is_setup = False

def ensure_setup():
    global is_setup
    if not is_setup:
        setup()
    is_setup = True
    
class press(object):
    def __init__(self,button):
        warnings.warn(
            'press is deprecated. Use <button_name>.down (or up, press, or hold) instead.',
            DeprecationWarning,
            stacklevel=2)
        self.button = buttons[button]
    
    def __call__(self,f):
        self.button.down(f)


def setup():
    from platform_specific import register_button_callback
    register_button_callback(button_callback)

    from .run_loop import main_run_loop
    main_run_loop.add_wait_callback(wait)

def button_callback(button_index, action):
    button_name = button_names[button_index]
    button = buttons[button_name]

    button.add_event(action)

def wait():
    for button in buttons.values():
        button.process_events(time.time())
        button.run_callbacks()

@@ -1939,24 +1939,16 @@
 action)%0A
-        
 %0A    def
@@ -2073,24 +2073,16 @@
 ns = %5B%5D%0A
-        
 %0A    def
@@ -2097,23 +2097,25 @@
 nt(self,
+ 
 action,
+ 
 timestam
@@ -2237,16 +2237,17 @@
 (action,
+ 
 timestam
@@ -2281,14 +2281,16 @@
 elf,
+ 
 event,
+ 
 func
@@ -2355,24 +2355,16 @@
 d(func)%0A
-        
 %0A    def
@@ -2370,32 +2370,33 @@
 f __call__(self,
+ 
 f):%0A        prin
@@ -2428,23 +2428,16 @@
 down(f)%0A
-       
 %0A    def
@@ -2443,24 +2443,25 @@
 f down(self,
+ 
 f):%0A        
@@ -2485,16 +2485,17 @@
 ('down',
+ 
 f)%0A%0A    
@@ -2502,24 +2502,25 @@
 def up(self,
+ 
 f):%0A        
@@ -2542,16 +2542,17 @@
 ck('up',
+ 
 f)%0A%0A    
@@ -2562,24 +2562,25 @@
  press(self,
+ 
 f):%0A        
@@ -2605,16 +2605,17 @@
 'press',
+ 
 f)%0A%0A    
@@ -2624,24 +2624,25 @@
 f hold(self,
+ 
 f):%0A        
@@ -2670,13 +2670,15 @@
 ld',
+ 
 f)%0A%0A#
+ 
 crea
@@ -2707,24 +2707,25 @@
 s = ('left',
+ 
 'midleft','m
@@ -2722,16 +2722,17 @@
 idleft',
+ 
 'midrigh
@@ -2734,16 +2734,17 @@
 dright',
+ 
 'right')
@@ -2789,17 +2789,16 @@
 _names%7D%0A
-(
 left_but
@@ -2801,16 +2801,17 @@
 _button,
+ 
 midleft_
@@ -2817,16 +2817,17 @@
 _button,
+ 
 midright
@@ -2834,16 +2834,17 @@
 _button,
+ 
 right_bu
@@ -2847,17 +2847,16 @@
 t_button
-)
  = %5Bbutt
@@ -2886,16 +2886,17 @@
 names%5D%0A%0A
+%0A
 is_setup
@@ -3002,20 +3002,16 @@
  = True%0A
-    
 %0Aclass p
@@ -3046,16 +3046,17 @@
 __(self,
+ 
 button):
@@ -3267,20 +3267,16 @@
 button%5D%0A
-    
 %0A    def
@@ -3290,16 +3290,17 @@
 __(self,
+ 
 f):%0A    

Make whitespace more consistent with rest of project