# -*- coding: utf-8 -*-
# Priority queue to be used in AF
# Copyright (C) 2011 Matěj Grégr, Michal Kajan, Libor Polčák, Vladimír Veselý
# 
# 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/>.

import heapq
import threading
import time

TPQ_EARLIEST = 0
TPQ_TIME = 0
TPQ_DATA = 2

class TimePriorityQueue(object):
    """ Thread safe priority queue based on time """

    def __init__(self, queueFile):
        """ Constructor
        
        queueFile
          Name of file where to store current content of the queue
        """
        self.__file = queueFile
        self.__counter = 0 # Achieve different priorities even with same time
        self.__heap = []
        self.__heapChangedE = threading.Event()
        self.__heapL = threading.Lock()
        self.__saveQueueContent()

    def erase(self, filterFunc):
        """ Removes data for which the filterFunc evaluates to False from queue

        filterFunc Function dataItem -> Boolean used to filter data from queue
        """
        with self.__heapL:
            orig = len(self.__heap)
            self.__heap = list(filter(lambda d: filterFunc(d[TPQ_DATA]), self.__heap))
            diff = orig - len(self.__heap)
            if diff:
                heapq.heapify(self.__heap)
                self.__heapChangedE.set()
            self.__saveQueueContent()
            return diff

    def getContent(self):
        """ Get all items in the queue in a list """
        c = []
        with self.__heapL:
            for item in self.__heap:
                c.append(item[TPQ_DATA])
        return c

    def push(self, t, data):
        """ Push data to the heap

        t
            Time (number of seconds - unix time) that determines the earliest
            time when the data can be popped
        data
            data to be stored in the queue
        """
        with self.__heapL:
            heapq.heappush(self.__heap, (t, self.__counter, data))
            self.__saveQueueContent()
            self.__counter += 1
            self.__heapChangedE.set()

    def pop(self):
        """ Pops an item from the queue

        Note that this method blocks until an item is ready to be processed.

        Returns data stored by push method
        """
        while True: # Exit with return statement
            timeout = None
            with self.__heapL:
                self.__heapChangedE.clear()
                if len(self.__heap) > 0:
                    now = time.mktime(time.localtime())
                    timeout = self.__heap[TPQ_EARLIEST][TPQ_TIME] - now
                    if timeout <= 0:
                        item = heapq.heappop(self.__heap)
                        self.__saveQueueContent()
                        return item[TPQ_DATA]
            self.__heapChangedE.wait(timeout)

    def __saveQueueContent(self):
        if self.__file != None:
            with open(self.__file, "w") as f:
                for item in self.__heap:
                    intercept = item[TPQ_DATA]
                    if intercept == None:
                        continue
                    p = intercept.getInterceptionPeriod()
                    f.write(
                            intercept.getLIID() + "\t" + \
                            repr(intercept.getNID()) + "\t" + \
                            str(intercept.getLevel()) + "\t" + \
                            time.strftime("%c", p[0]) + "\t" + \
                            time.strftime("%c", p[1]) + "\t" + \
                            str(intercept.interceptCC()) + "\n"
                    )
