'''
Created on Aug 7, 2011
@author: Dilum Bandara
@contact: dilumb@engr.colostate.edu
@version: 0.1
@copyright: Copyright(c) 2011, Dilum Bandara
@license: GPL
'''

'''
Calculate latency for each message
First read routing table, AS table, & node community information
Then for each message calculate latency 
'''

import math;
import random;
import sys;

#geoCommun = [] #List of geographic community IDs & size. If NULL assign randomly
#geoCommun = [['10', 4800]] #List of geographic community IDs & size
geoCommun = [['1', 600], ['2', 600], ['3', 600], ['4', 1200], ['5', 1200], ['6', 1200],\
             ['7', 1200], ['8', 1200], ['9', 2400], ['10', 4800]] #List of geographic community IDs & size
bucketSize = 30.0
latencyLAN = 1.0 #Latency for LAN link
latencyWAN = 40.0 #Latency for WAN link - what we want to achieve
aveLatencyWAN =  1.20658896205  #Use to scale to latencyWAN 
speedLight = 299.792458 #speed of light weighted
numNodesPerAS = 4800 #Number of nodes per AS
#python Simulate.py [topology no] [query no]
fInRoute = 'RoutingTable_10_750_' + sys.argv[1] + '.txt'
fInAS = 'ASTable_10_750_' + sys.argv[1] + '.txt'
fInQuery = 'QueryTrace_' + sys.argv[2] + '.txt'
fInCommun = 'Nodes' + sys.argv[2] + '.txt'
fOut = 'results_' + sys.argv[1] + '_' + sys.argv[2] + '.txt' #topolgy query
fOutBucket = 'results_bucket_' + sys.argv[1] + '_' + sys.argv[2] #Base file name

routingTb = {} #Routing table
ASTb = {} #AS table
nodeTb = {} #Node community information table
scaleWAN = latencyWAN/aveLatencyWAN #Scale
 

def initializeTables():
    '''
    Read routing, AS, and node community data initialize tables
    Original routing table has distance not latency 
    @return: None 
    '''
    #Read routing table
    fdIn = open(fInRoute, 'r')
    for line in fdIn: #Extract information
        tmp = line.split()
        entryList = {}
        for i in range((len(tmp) - 1)/2):
            #latency = distance/speed_of_light. 
            #scaled latency = layency * scale
            entryList[tmp[i * 2 + 1]] = (float(tmp[i * 2 + 2])/speedLight) * scaleWAN
        routingTb[tmp[0]] = entryList
    fdIn.close()

    #Read AS table   
    fdIn = open(fInAS, 'r')
    for line in fdIn: #Extract information
        tmp = line.split()
        if tmp[1] in ASTb: ASTb[tmp[1]].append(tmp[0])
        else: ASTb[tmp[1]] = [tmp[0]]
    fdIn.close()

    #Read AS table   
    fdIn = open(fInCommun, 'r')
    for line in fdIn: #Extract information
        tmp = line.split()
        nodeTb[tmp[0]] = [tmp[1], -1] #[Community ID & assigned router]
    fdIn.close()


def assignNodes():
    '''
    Assign nodes to routers. Communities belong to same geographic region
    are assigned to routers of a specific AS while other nodes are assigned
    to remaining routers randomly
    @return: None  
    '''
    #select which ASs are used for geographic communities
    tmpASLst = {}
    allocatedASs = []
    unAllocatedASs = []
    geoCommunList = []
    for i in range(len(geoCommun)): geoCommunList.append(geoCommun[i][0])
    
    for i in range(len(geoCommun)):
        numASs = math.ceil(geoCommun[i][1] / float(numNodesPerAS))
        while True:
            rndAS = random.choice(ASTb.keys()) #Pick a random AS
            if rndAS not in allocatedASs: #Not already allocated
                allocatedASs.append(rndAS)
                if geoCommun[i][0] not in tmpASLst: #Adding for 1st time 
                    tmpASLst[geoCommun[i][0]] = [rndAS]
                else:
                    tmpASLst[geoCommun[i][0]].append(rndAS)
                if len(tmpASLst[geoCommun[i][0]]) == numASs: #Found enough 
                    break
    for i in ASTb.keys(): #Find unallocated ASs
        if i not in allocatedASs: unAllocatedASs.append(i)
    #For each node assign to an AS
    for j in nodeTb:
        if nodeTb[j][0] in geoCommunList: #Has a specific AS
            tmpAS = random.choice(tmpASLst[nodeTb[j][0]])
            nodeTb[j][1] = random.choice(ASTb[tmpAS])
        else: 
            tmpAS = random.choice(unAllocatedASs)
            nodeTb[j][1] = random.choice(ASTb[tmpAS]) 


def findLatency(msg):
    '''
    Find latency for entire message
    @param msg: Message path as a list
    @return: Latency
    '''
    latency = 0.0
    for i in range(len(msg) -1): #For each pair
        latency += routingTb[nodeTb[msg[i]][1]][nodeTb[msg[i + 1]][1]]
        if msg[i] != msg[i + 1]: #Not same src & dest
            latency += 2 * latencyLAN #Add latency to go to router
    return latency 

def summarizeCommunity(tmpList, fd, time, cID):
    '''
    Summarize data from a community
    @param tmpList: List of results
    @param fd: File descripter
    @param time: Event time
    @param cID: Community ID
    '''
    if len(tmpList) == 0: return
    totalHops = 0
    totalLatency = 0.0
    
    for k in range(len(tmpList)): 
        totalHops += int(tmpList[k][0])
        totalLatency += tmpList[k][1]

    fd.write('C' + str(cID) + '\t' + str(time) + '\t' +  \
                        str(float(totalHops)/len(tmpList)) + '\t' + \
                        str(totalLatency/len(tmpList))+ '\n')


def summarize(results):
    '''
    Summarize stat and dump results
    '''
    numQueries = len(results)
    totalHops = 0
    totalLatency = 0.0
    fdOut = open(fOut, 'w')
     
    for i in range(len(results)):
        totalHops += int(results[i][1])
        totalLatency += results[i][2]
        fdOut.write(str(results[i][0]) + '\t' + results[i][1] + '\t' + \
                    str(results[i][2]) + '\t' + results[i][3] + '\n')
    print "No of queries", numQueries
    print "Ave hops", totalHops/float(numQueries)
    print "Ave latency", totalLatency/numQueries
    fdOut.close()
    
    fdOutBuck1 = open(fOutBucket + '_1.txt', 'w')
    fdOutBuck2 = open(fOutBucket + '_2.txt', 'w')
    fdOutBuck3 = open(fOutBucket + '_3.txt', 'w')
    fdOutBuck4 = open(fOutBucket + '_4.txt', 'w')
    fdOutBuck5 = open(fOutBucket + '_5.txt', 'w')
    fdOutBuck6 = open(fOutBucket + '_6.txt', 'w')
    fdOutBuck7 = open(fOutBucket + '_7.txt', 'w')
    fdOutBuck8 = open(fOutBucket + '_8.txt', 'w')
    fdOutBuck9 = open(fOutBucket + '_9.txt', 'w')
    fdOutBuck10 = open(fOutBucket + '_10.txt', 'w')
    beginTime = results[0][0]
    tmpList1 = []
    tmpList2 = []
    tmpList3 = []
    tmpList4 = []
    tmpList5 = []
    tmpList6 = []
    tmpList7 = []
    tmpList8 = []
    tmpList9 = []
    tmpList10 = []
    i = 0
    for i in range(len(results)):
        if results[i][0] < (beginTime + bucketSize) :
            if results[i][3] == '1': tmpList1.append([results[i][1], results[i][2]])
            elif results[i][3] == '2': tmpList2.append([results[i][1], results[i][2]])
            elif results[i][3] == '3': tmpList3.append([results[i][1], results[i][2]])
            elif results[i][3] == '4': tmpList4.append([results[i][1], results[i][2]])
            elif results[i][3] == '5': tmpList5.append([results[i][1], results[i][2]])
            elif results[i][3] == '6': tmpList6.append([results[i][1], results[i][2]])
            elif results[i][3] == '7': tmpList7.append([results[i][1], results[i][2]])
            elif results[i][3] == '8': tmpList8.append([results[i][1], results[i][2]])
            elif results[i][3] == '9': tmpList9.append([results[i][1], results[i][2]])
            elif results[i][3] == '10': tmpList10.append([results[i][1], results[i][2]])
            else : tmpList1.append([results[i][1], results[i][2]])
        else:
            summarizeCommunity(tmpList1, fdOutBuck1, results[i - 1][0], 1)
            summarizeCommunity(tmpList2, fdOutBuck2, results[i - 1][0], 2)
            summarizeCommunity(tmpList3, fdOutBuck3, results[i - 1][0], 3)
            summarizeCommunity(tmpList4, fdOutBuck4, results[i - 1][0], 4)
            summarizeCommunity(tmpList5, fdOutBuck5, results[i - 1][0], 5)
            summarizeCommunity(tmpList6, fdOutBuck6, results[i - 1][0], 6)
            summarizeCommunity(tmpList7, fdOutBuck7, results[i - 1][0], 7)
            summarizeCommunity(tmpList8, fdOutBuck8, results[i - 1][0], 8)
            summarizeCommunity(tmpList9, fdOutBuck9, results[i - 1][0], 9)
            summarizeCommunity(tmpList10, fdOutBuck10, results[i - 1][0], 10)
            tmpList1 = []
            tmpList2 = []
            tmpList3 = []
            tmpList4 = []
            tmpList5 = []
            tmpList6 = []
            tmpList7 = []
            tmpList8 = []
            tmpList9 = []
            tmpList10 = []
            if results[i][3] == '1': tmpList1.append([results[i][1], results[i][2]])
            elif results[i][3] == '2': tmpList2.append([results[i][1], results[i][2]])
            elif results[i][3] == '3': tmpList3.append([results[i][1], results[i][2]])
            elif results[i][3] == '4': tmpList4.append([results[i][1], results[i][2]])
            elif results[i][3] == '5': tmpList5.append([results[i][1], results[i][2]])
            elif results[i][3] == '6': tmpList6.append([results[i][1], results[i][2]])
            elif results[i][3] == '7': tmpList7.append([results[i][1], results[i][2]])
            elif results[i][3] == '8': tmpList8.append([results[i][1], results[i][2]])
            elif results[i][3] == '9': tmpList9.append([results[i][1], results[i][2]])
            elif results[i][3] == '10': tmpList10.append([results[i][1], results[i][2]])
            else : tmpList1.append([results[i][1], results[i][2]])
            beginTime = results[i][0]

    summarizeCommunity(tmpList1, fdOutBuck1, results[i - 1][0], 1)
    summarizeCommunity(tmpList2, fdOutBuck2, results[i - 1][0], 2)
    summarizeCommunity(tmpList3, fdOutBuck3, results[i - 1][0], 3)
    summarizeCommunity(tmpList4, fdOutBuck4, results[i - 1][0], 4)
    summarizeCommunity(tmpList5, fdOutBuck5, results[i - 1][0], 5)
    summarizeCommunity(tmpList6, fdOutBuck6, results[i - 1][0], 6)
    summarizeCommunity(tmpList7, fdOutBuck7, results[i - 1][0], 7)
    summarizeCommunity(tmpList8, fdOutBuck8, results[i - 1][0], 8)
    summarizeCommunity(tmpList9, fdOutBuck9, results[i - 1][0], 9)
    summarizeCommunity(tmpList10, fdOutBuck10, results[i - 1][0], 10)
    fdOutBuck1.close()
    fdOutBuck2.close()
    fdOutBuck3.close()
    fdOutBuck4.close()
    fdOutBuck5.close()
    fdOutBuck6.close()
    fdOutBuck7.close()
    fdOutBuck8.close()
    fdOutBuck9.close()
    fdOutBuck10.close()
    
if __name__ == '__main__':
    random.seed(123) #TODO set seed
    initializeTables()
    assignNodes()
    
    results = []
    fdIn = open(fInQuery, 'r')    
    #Dump [hops, latency, community ID]
    for line in fdIn: #For each query
        tmp = line.split()
        results.append([float(tmp[0]), tmp[-1], findLatency(tmp[2:-1]), nodeTb[tmp[2]][0]])
    fdIn.close()
    summarize(results)
