'''
Process data
'''
import sys


class Nodes(object):
    '''
    Node Class. Track nodes (when added & removed) 
    '''

    def __init__(self, fileName):
        self.nodes = []
        self.numNodes = 0
        self.maxNodes = [0, 0] #[number, time]
        self.fileName = fileName


    def add(self, time, add):
        '''
        Node added or removed.
        @param time: Time of event
        @param add: Add or remove
        '''
        if add == True: self.numNodes += 1
        else: self.numNodes -= 1
        if self.numNodes > self.maxNodes[0]: 
            self.maxNodes = [self.numNodes, time]
        if add: self.nodes.append([time, 'Add', self.numNodes])
        else: self.nodes.append([time, 'Rem', self.numNodes])


    def dump(self):
        '''
        Dump data
        '''
        fdOut = open(self.fileName, 'w')
        fdOut.write('Time\tMax nodes\n')
        fdOut.write(str(self.maxNodes[1]) + '\t' + str(self.maxNodes[0]) + '\n\n')
        for entry in self.nodes:
            fdOut.write(entry[0] + '\t' + entry[1] + '\t' + str(entry[2]) + '\n')
        self.nodes = []
        fdOut.close()


class Cost(object):
    '''
    Class to track cost related to Adv, Rem, Que, & load check
    '''

    def __init__(self, bucketSize, fileName):
        self.adv = []
        self.rem = []
        self.que = []
        self.ldChk = []
        self.mini = [99999999, 99999999, 99999999]
        self.maxi = [0, 0, 0]
        self.cumTotal = [0, 0, 0, 0] #Cummulative cost
        self.count = [0, 0, 0, 0, 0] #Number of events
        self.bucketSize = bucketSize
        self.nextTime = bucketSize
        self.fdOut = open(fileName, 'w')
        tmpStr = 'Time\tAve Adv\tAve Rem\tAve Que\t\tTotal Adv\tTotal Rem\t'
        tmpStr += 'Total Que\tTotal LdChk\t\tCum Adv\tCum Rem\tCum Que\tCum LdChk'
        tmpStr += '\tMin Adv\tMax Adv\tMin Rem\tMax Rem\tMin Que\tMax Que\n'
        self.fdOut.write(tmpStr)
    
    
    def __del__(self):
        '''
        Destructor
        '''
        self.fdOut.close()
   
   
    def add(self, finish, eventType, time, cost, success = None):
        '''
        Add event
        @param eventType: Type of event
        @param time: Time of event
        @param cost: Cost (hops) associated with event
        @param success: Success or failure if relevant
        ''' 
        if (time > self.nextTime) or (finish == True):
            totalAdv = sum(self.adv)
            totalRem = sum(self.rem)
            totalQue = sum(self.que)
            totalLdChk = sum(self.ldChk)
            aveAdv = totalAdv/float(len(self.adv)) if (len(self.adv) > 0) else 0 
            aveRem = totalRem/float(len(self.rem)) if (len(self.rem) > 0) else 0
            aveQue = totalQue/float(len(self.que)) if (len(self.que) > 0) else 0                        
            tmpStr = str(self.nextTime) + '\t' + str(aveAdv) + '\t' + str(aveRem)
            tmpStr += '\t' + str(aveQue) + '\t\t' + str(totalAdv)
            tmpStr += '\t' + str(totalRem) + '\t' + str(totalQue)
            tmpStr += '\t' + str(totalLdChk) + '\t\t' + str(self.cumTotal[0])
            tmpStr += '\t' + str(self.cumTotal[1]) + '\t' + str(self.cumTotal[2])
            tmpStr += '\t' + str(self.cumTotal[3])
            if len(self.adv) > 0:
                tmpStr += '\t' + str(min(self.adv)) +  '\t' + str(max(self.adv))
            else: tmpStr += '\tNone\tNone'
            if len(self.rem) > 0:
                tmpStr +=  '\t' + str(min(self.rem)) + '\t' + str(max(self.rem))
            else: tmpStr += '\tNone\tNone'
            if len(self.que) > 0:
                tmpStr +=  '\t' + str(min(self.que)) + '\t' + str(max(self.que))
            else: tmpStr += '\tNone\tNone'
            self.fdOut.write(tmpStr + '\n')
            
            self.count[0] += len(self.adv)
            self.count[1] += len(self.rem)
            self.count[2] += len(self.que)
            self.count[4] += len(self.ldChk)
            self.nextTime += self.bucketSize #Next round
            self.adv = []
            self.rem = []
            self.que = []
            self.ldChk = []
        
        if finish == True: return

        if eventType == 'Adv':
            self.adv.append(cost)
            #self.adv.append([time, cost])
            if self.mini[0] > cost: self.mini[0] = cost
            if self.maxi[0] < cost: self.maxi[0] = cost
            self.cumTotal[0] += cost
        elif eventType == 'Rem':
            self.rem.append(cost) 
            #self.rem.append([time, cost])
            if self.mini[1] > cost: self.mini[1] = cost
            if self.maxi[1] < cost: self.maxi[1] = cost
            self.cumTotal[1] += cost
        elif eventType == 'Que':
            self.que.append(cost)
            #self.que.append([time, cost, success])
            if self.mini[2] > cost: self.mini[2] = cost
            if self.maxi[2] < cost: self.maxi[2] = cost
            self.cumTotal[2] += cost
            if success == 'T': self.count[3] += 1
        elif eventType == 'LdChk':
            self.ldChk.append(cost)
            #self.ldChk.append([time, cost])
            self.cumTotal[3] += cost
        else: assert(False), 'Unknown type ' + eventType


#    def summary(self, events):
#        '''
#        Find min, max, average of the given dataset
#        @param events: List of events 
#        @return: [min, max, average, total, success]
#        '''
#        tmpMin = 999999
#        tmpMax = -1
#        total = 0
#        success = 0
#        hasSuccess = True if len(events[0]) == 3 else False
#        for i in range(len(events)):
#            if events[i][1] < tmpMin: tmpMin = events[i][1]
#            if events[i][1] > tmpMax: tmpMax = events[i][1]
#            total += events[i][1]
#            if hasSuccess:
#                if events[i][2] == 'T': success += 1
#        numEvents = float(len(events))
#        return [tmpMin, tmpMax, total/numEvents, total, success/numEvents]


#    def spliBuckets(self, events):
#        '''
#        Split events into buckets
#        @param events: List of events
#        @return: List of buckets [time, min, max, average]
#        '''
#        tmpBuckets = {}
#        nextTime = self.bucketSize


#    def dump(self):
#        '''
#        First dump summary of data then dump raw stat if required
#        '''
#        fdOut = open(self.advFile, 'w')
#        results = self.summary(self.adv)
#        fdOut.write('Min\tMax\tAve\n')
#        fdOut.write(str(results[0]) + '\t' + str(results[1]) + '\t' + \
#                    str(results[2]) + '\n\n\n')
#        buckets = self.spliBuckets(self.adv)
#        for i in buckets:
#            fdOut.write(str(i[0]) + '\t' + str(i[1]) + '\t' + str(i[2]) + \
#                                                        '\t' + str(i[2]) + '\n')
#        fdOut.close()
#        
#        fdOut = open(self.remFile, 'w')
#        results = self.summary(self.rem)
#        fdOut.write('Min\tMax\tAve\n')
#        fdOut.write(str(results[0]) + '\t' + str(results[1]) + '\t' + \
#                    str(results[2]) + '\n\n\n')
#        buckets = self.spliBuckets(self.rem)
#        for i in buckets:
#            fdOut.write(str(i[0]) + '\t' + str(i[1]) + '\t' + str(i[2]) + \
#                                                        '\t' + str(i[2]) + '\n')
#        fdOut.close()
#        
#        fdOut = open(self.queFile, 'w')
#        results = self.summary(self.que)
#        fdOut.write('Min\tMax\tAve\tSuccess\n')
#        fdOut.write(str(results[0]) + '\t' + str(results[1]) + '\t' + \
#                    str(results[2]) + '\t' + str(results[3]) + '\n\n\n')
#        buckets = self.spliBuckets(self.que)
#        for i in buckets:
#            fdOut.write(str(i[0]) + '\t' + str(i[1]) + '\t' + str(i[2]) + \
#                                                        '\t' + str(i[2]) + '\n')        
#        fdOut.close()


class DHTOverlay(object):
    '''
    Track DHT & overlay stat
    '''
    
    def __init__(self, statInterval, fileNameDHT, fileNameOverlay):
        '''
        @param statInterval: Stat collection interval 
        @param fileNameDHT: 
        @param fileNameOverlay:
        '''
        self.DHT = []
        self.overlay = []        
        self.nextTime = statInterval
        self.statInterval = statInterval
        self.fdOutDHT = open(fileNameDHT, 'w')
        self.fdOutOverlay = open(fileNameOverlay, 'w')
    
    
    def __del__(self):
        self.fdOutDHT.close()
        self.fdOutOverlay.close()


    def add(self, finish, time, eventType, NID, data):
        '''
        @param time:
        @param eventType:
        @param NID: Node ID 
        @param data: List of parameters 
        '''
        if (self.nextTime <= time) or (finish == True):
            DHTMin = [99999999] * 18
            DHTMax = [0] * 18
            DHTTotal = [0] * 18
            OverlayMin = [99999999] * 8 
            OverlayMax = [0] * 8
            OverlayTotal = [0] * 8
            for i in self.DHT:
                self.fdOutDHT.write(str(i[0]) + '\t' + str(i[1]))
                for j in range(len(i[2])): 
                    self.fdOutDHT.write('\t' + str(i[2][j]))
                    if DHTMin[j] > i[2][j]: DHTMin[j] = i[2][j] 
                    if DHTMax[j] < i[2][j]: DHTMax[j] = i[2][j]
                    DHTTotal[j] += i[2][j]
                self.fdOutDHT.write('\n')
            self.fdOutDHT.write('Min\t')
            for j in DHTMin: self.fdOutDHT.write('\t' + str(j))
            self.fdOutDHT.write('\nMax\t')
            for j in DHTMax: self.fdOutDHT.write('\t' + str(j))
            self.fdOutDHT.write('\nAve\t')
            for j in DHTTotal:
                if len(self.DHT) > 0:  
                    self.fdOutDHT.write('\t' + str(j/float(len(self.DHT))))
                else: self.fdOutDHT.write('\t0')
            self.fdOutDHT.write('\n\n')
            
            for i in self.overlay:
                self.fdOutOverlay.write(str(i[0]) + '\t' + str(i[1]))
                for j in range(len(i[2])): 
                    self.fdOutOverlay.write('\t' + str(i[2][j]))
                    if OverlayMin[j] > i[2][j]: OverlayMin[j] = i[2][j] 
                    if OverlayMax[j] < i[2][j]: OverlayMax[j] = i[2][j]
                    OverlayTotal[j] += i[2][j]
                self.fdOutOverlay.write('\n')
            self.fdOutOverlay.write('Min\t')
            for j in OverlayMin: self.fdOutOverlay.write('\t' + str(j))
            self.fdOutOverlay.write('\nMax\t')
            for j in OverlayMax: self.fdOutOverlay.write('\t' + str(j))
            self.fdOutOverlay.write('\nAve\t')
            for j in OverlayTotal: 
                if len(self.DHT) > 0: 
                    self.fdOutOverlay.write('\t' + str(j/float(len(self.DHT))))
                else: self.fdOutOverlay.write('\t0')
            self.fdOutOverlay.write('\n\n')
            self.DHT = []
            self.overlay = []
            self.nextTime += self.statInterval

        if finish == True: return
        
        if eventType == 'DHT': self.DHT.append([time, NID, data])
        elif eventType == 'Ovlay': self.overlay.append([time, NID, data])
        else: assert(False), 'Unknown ecent type ' + eventType


def main(*argv):
    '''
    Main function to indicate start of program
    '''
    assert(len(sys.argv) == 2), \
        'Wrong parameters. Use \'python processData [file ID]\''
    fdIn = open('results_' + sys.argv[1] + '.txt', 'r')
    nodes = Nodes('nodes_' + sys.argv[1] + '.txt')
    cost = Cost(30, 'cost_' + sys.argv[1] + '.txt')
    dhtOv = DHTOverlay(30, 'dht_' + sys.argv[1] + '.txt', \
                                            'overlay_' + sys.argv[1] + '.txt')

    clust = []
    resources = {} #Resources stat
    for line in fdIn:
        tmp = line.split('\t')
        if tmp[0] == 'Adv': cost.add(False, tmp[0], float(tmp[1]), int(tmp[2]))
        elif tmp[0] == 'Rem': cost.add(False, tmp[0], float(tmp[1]), int(tmp[2]))
        elif tmp[0] == 'Que': cost.add(False, tmp[0], float(tmp[1]), \
                                                            int(tmp[2]), tmp[3])
        elif tmp[0] == 'LdChk': cost.add(False, tmp[0], float(tmp[1]), int(tmp[2]))
        elif tmp[0] == 'Clust': clust.append(tmp[1:])
        elif tmp[0] == 'AddN': nodes.add(tmp[1], True)
        elif tmp[0] == 'RemN': nodes.add(tmp[1], False)
        elif tmp[0] == 'Res': #Track only the final value
            resources[int(tmp[2])] = [int(tmp[3]), int(tmp[4])]
        elif tmp[0] == 'DHT':
            data = []
            for i in range(len(tmp) - 3): data.append(float(tmp[i + 3]))
            dhtOv.add(False, float(tmp[1]), 'DHT', int(tmp[2]), data)
        elif tmp[0] == 'Ovlay':
            data = []
            for i in range(len(tmp) - 3): data.append(float(tmp[i + 3]))
            dhtOv.add(False, float(tmp[1]), 'Ovlay', int(tmp[2]), data)
        elif tmp[0] == 'NodeKey': continue
        elif tmp[0] == 'Leave': continue
        elif tmp[0] == 'NIDs': continue
        elif tmp[0] == 'AddSuce': continue
        elif tmp[0] == 'AddPred': continue
        elif tmp[0] == 'AddRep': continue
        elif tmp[0] == 'AddFrag': continue
        elif tmp[0] == 'TranSuce': continue
        elif tmp[0] == 'TranPred': continue
        elif tmp[0] == 'RemDHT': continue
        elif tmp[0] == 'RemOvlay': continue
        else: assert(False), 'Unknown attribute ' + tmp[0]

    fdIn.close()
    cost.add(True, None, -1, -1)
    dhtOv.add(True, -1, None, -1, None)

    nodes.dump()
    #Dump cluster info
    fdOut = open('nodes_' + sys.argv[1] + '.txt', 'a')
    fdOut.write('\n\n')
    for i in clust: 
        tmpStr = i[0] + '\t' + i[1] + '\t'
        for x in i[2:]: tmpStr += ' ' + x  
        fdOut.write(tmpStr)
    fdOut.close()

    fdOut = open('resources_' + sys.argv[1] + '.txt', 'w')
    fdOut.write('NID\tAdv\tQue\n')    
    tmpMin = [99999999, 99999999]
    tmpMax = [0, 0]    
    for i in resources:
        if resources[i][0] < tmpMin[0]: tmpMin[0] = resources[i][0]
        if resources[i][1] < tmpMin[1]: tmpMin[1] = resources[i][1]
        if resources[i][0] > tmpMax[0]: tmpMax[0] = resources[i][0] 
        if resources[i][1] > tmpMax[1]: tmpMax[1] = resources[i][1]
        fdOut.write(str(i) + '\t' + str(resources[i][0]) + '\t' + \
                                                    str(resources[i][1]) + '\n')
    fdOut.write('Min\t' + str(tmpMin[0]) + '\t' + str(tmpMin[1]) + '\n')
    fdOut.write('Max\t' + str(tmpMax[0]) + '\t' + str(tmpMax[1]) + '\n')
    fdOut.close()
    
    fdOut = open('summary_' + sys.argv[1] + '.txt', 'w')
    fdOut.write('Total Adv cost\t' + str(cost.cumTotal[0]) + '\n')
    fdOut.write('Total Rem cost\t' + str(cost.cumTotal[1]) + '\n')
    fdOut.write('Total Que cost\t' + str(cost.cumTotal[2]) + '\n')
    fdOut.write('Total LdChk cost\t' + str(cost.cumTotal[3]) + '\n')
    fdOut.write('Sub Total\t' + str(sum(cost.cumTotal)) + '\n')
    numQue = float(cost.count[2]) if cost.count[2] > 0 else 1.0
    fdOut.write('Cost per query\t' + str(cost.cumTotal[2]/numQue) + '\n')
    fdOut.write('Successful queries %\t' + str(cost.count[3]/numQue) + '\n')
    fdOut.write('Total cost per query\t' + str(sum(cost.cumTotal)/numQue) + '\n')
    fdOut.write('Que min\t' + str(cost.mini[2]) + '\n')
    fdOut.write('Que max\t' + str(cost.maxi[2]) + '\n')
    fdOut.write('Max Nodes\t' + str(nodes.maxNodes[0]) + '\n')
    fdOut.write('Max Nodes time\t' + str(nodes.maxNodes[1])+ '\n')
    fdOut.close()


if __name__ == '__main__':
    sys.exit(main(*sys.argv))
