'''
Created on Sep 6, 2012
@version: 0.1
@author: Dilum Bandara
@email: dilumb@engr.colostate.edu
@license: Apache License v2.0

   Copyright 2012 H. M. N. Dilum Bandara and Anura P. Jayasumana, Colorado State
   University

   Licensed under the Apache License, Version 2.0 (the "License");
   you may not use this file except in compliance with the License.
   You may obtain a copy of the License at

       http://www.apache.org/licenses/LICENSE-2.0

   Unless required by applicable law or agreed to in writing, software
   distributed under the License is distributed on an "AS IS" BASIS,
   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   See the License for the specific language governing permissions and
   limitations under the License.
'''

import Config as Config


class PIT(object):
    '''
    Pending Interest Table (PIT) class
    '''


    def __init__(self, maxSize):
        '''
        Constructor
        '''
        self._maxSize = maxSize
        self._PIT = {} #{name: [[list of faces interested in content], expireTime]}
        self.numPITTimeouts = 0 #Number of PIT entries timed out
        self.numPITIntChecks = 0 #Number of interest messages
        self.numPITIntHits = 0 #Number of interest message hits
        self.numPITDataChecks = 0
        self.numPITDataHits = 0 #Number of data messages
        self.lastTimeOutCheck = -1 #When was PIT timeout checked


    def put(self, name, source, cTime, vTime):
        '''
        Add to PIT.
        @param msg: Content name without timeout
        @param source: Source node
        @param cTime: Current time
        @param vTime: Content should be fresh than this time
        @return: 0 - no need to forward, 1 - need to forward, No pending 
        messages, -1 PIT size exceed  
        '''
        self.numPITIntChecks += 1
        if self.lastTimeOutCheck + 5 < cTime: #Time to check PIT timeouts
            expiredNames = [] #remove any expired entries
            for i in self._PIT: #Check for timeouts in PIT            
                if self._PIT[i][1] < cTime: expiredNames.append(i)
            for j in expiredNames: #remove expired ones
                self.numPITTimeouts += 1
                del self._PIT[j]
            self.lastTimeOutCheck = cTime
                 
        if (name in self._PIT) and (cTime <= self._PIT[name][1]): #In PIT
            newSource = True
            moreResent = False
            largestVTime = -1.0
            for i in range(len(self._PIT[name][0])):
                #Is source already in PIT & need to be more resent? 
                if self._PIT[name][0][i][0] == source:
                    if self._PIT[name][0][i][1] < vTime: 
                        moreResent = True
                    newSource = False
                    break
                if self._PIT[name][0][i][1] > largestVTime:
                    largestVTime = self._PIT[name][0][i][1]
            if newSource == True: #New source? add it 
                self._PIT[name][0].append([source, vTime, True, False])
            self._PIT[name][1] = cTime + Config.PIT_TIMEOUT #Update timer
            self.numPITIntHits += 1
            if moreResent == True: return 1
            if newSource == True and largestVTime < vTime: return 1
            return 0 
        else: #Not in PIT or timeout. Will size exceed?
            if (name in self._PIT) and (cTime > self._PIT[name][1]): #expired
                del self._PIT[name]
                self.numPITTimeouts += 1
            if (self._maxSize > 0) and (len(self._PIT) + 1 > self._maxSize):\
                return -1
            self._PIT[name] = [[[source, vTime, True, False]], \
                                cTime + Config.PIT_TIMEOUT]
            return 1


    def get(self, name, cTime, genTime):
        '''
        Get sources from PIT. Remove PIT entry
        @param name: Content name without timeout
        @param cTime: Current time
        @param genTime: Time that content/data was generated 
        @return: [list of nodes to forward data] 
        '''
        self.numPITDataChecks += 1
        if name not in self._PIT: return []
        if cTime > self._PIT[name][1]: #entry expired
            del self._PIT[name]
            self.numPITTimeouts += 1
            return []
        #Has entry in PIT & not expired
        self.numPITDataHits += 1
        nodes = []
        for i in range(len(self._PIT[name][0])):
            if self._PIT[name][0][i][2] == False: continue #Not active
            if self._PIT[name][0][i][1] > genTime: continue #old content 
            nodes.append(self._PIT[name][0][i][0])
            self._PIT[name][0][i][2] = False  #Mark as no longer active
        remAll = True
        for i in range(len(self._PIT[name][0])):
            if self._PIT[name][0][i][2] == True:
                remAll = False
                break
        if remAll == True: del self._PIT[name]        
        return list(set(nodes)) #Remove duplicates
                        

    def getStat(self):
        '''
        Get statistics
        '''
        return [len(self._PIT), self.numPITTimeouts, self.numPITIntHits, \
                self.numPITIntChecks, self.numPITDataHits, self.numPITDataChecks]