# -*- coding: utf-8 -*-

"""
    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 3 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, see <http://www.gnu.org/licenses/>.

    @author: mkaay
"""

from time import time
from heapq import heappop, heappush
from thread import start_new_thread
from threading import Lock

class AlreadyCalled(Exception):
    pass


class Deferred:
    def __init__(self):
        self.call = []
        self.result = ()

    def addCallback(self, f, *cargs, **ckwargs):
        self.call.append((f, cargs, ckwargs))

    def callback(self, *args, **kwargs):
        if self.result:
            raise AlreadyCalled
        self.result = (args, kwargs)
        for f, cargs, ckwargs in self.call:
            args += tuple(cargs)
            kwargs.update(ckwargs)
            f(*args ** kwargs)


class Scheduler:
    def __init__(self, core):
        self.core = core

        self.queue = PriorityQueue()

    def addJob(self, t, call, args=[], kwargs={}, threaded=True):
        d = Deferred()
        t += time()
        j = Job(t, call, args, kwargs, d, threaded)
        self.queue.put((t, j))
        return d


    def removeJob(self, d):
        """
        :param d: defered object
        :return: if job was deleted
        """
        index = -1

        for i, j in enumerate(self.queue):
            if j[1].deferred == d:
                index = i

        if index >= 0:
            del self.queue[index]
            return True

        return False

    def work(self):
        while True:
            t, j = self.queue.get()
            if not j:
                break
            else:
                if t <= time():
                    j.start()
                else:
                    self.queue.put((t, j))
                    break


class Job:
    def __init__(self, time, call, args=[], kwargs={}, deferred=None, threaded=True):
        self.time = float(time)
        self.call = call
        self.args = args
        self.kwargs = kwargs
        self.deferred = deferred
        self.threaded = threaded

    def run(self):
        ret = self.call(*self.args, **self.kwargs)
        if self.deferred is None:
            return
        else:
            self.deferred.callback(ret)

    def start(self):
        if self.threaded:
            start_new_thread(self.run, ())
        else:
            self.run()


class PriorityQueue:
    """ a non blocking priority queue """

    def __init__(self):
        self.queue = []
        self.lock = Lock()

    def __iter__(self):
        return iter(self.queue)

    def __delitem__(self, key):
        del self.queue[key]

    def put(self, element):
        self.lock.acquire()
        heappush(self.queue, element)
        self.lock.release()

    def get(self):
        """ return element or None """
        self.lock.acquire()
        try:
            el = heappop(self.queue)
            return el
        except IndexError:
            return None, None
        finally:
            self.lock.release()