# Copyright (c) 2000-2001 LOGILAB S.A. (Paris, FRANCE).
# http://www.logilab.fr/ -- mailto:contact@logilab.fr
#
# This program is free software; you can redistribute it and/or modify it under
# the terms of the GNU General Public License as published by the Free Software
# Foundation; either version 2 of the License, or (at your option) any later
# version.
#
# This program is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License along with
# this program; if not, write to the Free Software Foundation, Inc.,
# 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.

"""EventScheduler XXX"""

__revision__ = "$Id: EventScheduler.py,v 1.9 2001/12/07 16:01:33 syt Exp $"

import time
import threading
import Queue
import bisect

from narval.services.BaseService import BaseService

class EventScheduler(BaseService) :
    """ EventScheduler XXX """

    queue_lenght = 20
    max_delay = 1
    
    def __init__(self, event_callback) :
        BaseService.__init__(self,'EventScheduler')
        self.in_queue = Queue.Queue(self.queue_lenght)
        self.sched_queue = []
        self.timefunc = time.time
        self.delayfunc = time.sleep
        self.event_cb = event_callback
        self.thread = None
    
    def schedule_event(self,args) :
        self.in_queue.put_nowait(args)

    def stop(self) :
        self.loop = 0
        self.schedule_event( ('stop_service',0,0,0) )
        self.thread.join()
        print 'Service',self.threadName,'stopped'

    # internals ################################################################
    
    def _event(self, event, period=0):
        self.event_cb(event)
        if period:
            self._enter(period,5,self.schedule_event,(event,period))

    def _run(self):
        while self.loop :
            self._process_in_queue()
            time, priority, action, argument = self.sched_queue[0]
            now = self.timefunc()
            if now < time:
                self.delayfunc(min(time-now, self.max_delay))
            else:
                del self.sched_queue[0]
                apply(action, argument)
                self.delayfunc(0) # Let other threads run

    def _process_in_queue(self) :
        try:
            while 1:
                if self.sched_queue :
                    event,when,period,absoluteDate = self.in_queue.get_nowait()
                else :
                    event,when,period,absoluteDate = self.in_queue.get()
                if absoluteDate:
                    self._enterabs(when, 5, self._event, (event, period))
                else:
                    self._enter(when, 5, self._event, (event, period))
        except Queue.Empty:
            pass

    def _enterabs(self, time, priority, action, argument):
        event = time, priority, action, argument
        bisect.insort(self.sched_queue, event)
        return event # The ID
    
    def _enter(self, delay, priority, action, argument):
        time = self.timefunc() + delay
        return self._enterabs(time, priority, action, argument)
