'''
Created on Sep 12, 2012
@author: Dilum Bandara
@version: 0.1
@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.
'''

from AdvMsg import AdvMsg
from ResourceIndex import ResourceIndex
import Distance as Distance
import PeerManager as PM


class DHT(object):
    '''
    Distributed hash table (DHT) class. Maintain resource index, load stat & 
    responsible for load balancing.
    '''
    def __init__(self, PID):
        '''
        @param PID: DHT's peer ID
        @param cMembers: List of nodes in cluster 
        '''
        self.PID = PID #This should be in sink with overlay & resource PID
        self._idx = ResourceIndex() #Index
        self._numUpdatesRec = 0 #No of resource updates
        self._numQueFwd = 0
        self._numQueAns = 0


    def __del__(self):
        '''
        Destructor
        '''
        self._idx.reset(True)


    def recMsg(self, msg):
        '''
        Receive message from overlay. Handle accordingly
        @param msg: Message
        @return: Corresponding reply 
        '''
        if (msg.msgType == 'Put'): return self._addToIndex(msg)
        elif msg.msgType == 'Query': return self._queryIndex(msg)
        else: assert(False), 'Wrong message type ' + msg.msgType


    def _addToIndex(self, msg):
        '''
        Add given resource to index. If from a resource also inform to replicas.
        If from global predecessor first fragment & then inform replicas. If 
        from a cluster member just add - as cluster member is responsible for
        fragmentation & replication
        @param msg: Message
        @return: Reply message (if relevant)
        '''        
        if msg.msgType == 'Put':
            self._idx.add(msg.key, msg.payload[0], msg.payload[1], \
                          msg.payload[2], msg.payload[3], msg.payload[4])        
            if msg.source.NID != self.PID.NID: self._numUpdatesRec += 1
            return AdvMsg(self.PID, True, -1, -1, 'PutReply')


    def _queryIndex(self, msg, cTime):
        '''
        Check whether index has what is needed to satisfy query
        @param msg: Query message
        @return: Modified query message
        '''
        predeGKey = PM.OVERLAY[self.PID.NID].getPrede()[0].gKey 
        inQueryKey = msg.minKey if (Distance.inRangeR(msg.minKey, predeGKey, \
                    self.PID.gKey) == True) else -1 #Is this a direct/IN query
        lastNode = True if (Distance.inRangeR(msg.maxKey, predeGKey, \
            self.PID.gKey) == True) else False #Is last node query will reach?
        results = self._idx.query(msg.attToQue, msg.query, PM.EVENTS.now, \
                        msg.validTime, msg.resources, inQueryKey, msg.maxKey, \
                        self.PID.gKey, lastNode)        
        numRes = len(results)
        if numRes > 0:
            msg.resources += results
            msg.numResFound += numRes
            self._numQueAns += 1
        return msg


    def getStat(self):
        '''
        Dump node statistics
        @return: Stat
        '''
        return [self.PID.NID, self._numQueAns, self._numQueFwd, \
                   self._numUpdatesRec]