__author__ = 'Dilum Bandara'
__version__ = '0.1'
__date__ = '1/24/2011'
__copyright__ = 'Copyright(c) 2011, Dilum Bandara'
__license__ = 'GPL'

import sys
sys.path.append('../../BaseClasses')
from Node import Node
from Resources import Resources
from Queries import Queries
from DES import DES
from Overlay import Overlay
import BaseConfig
import Config
import random
import sys
import time
import math

if len(sys.argv) < 2:
    print 'Incorrect parameters. Input should be \'python main.py [seed for rng]\''
    sys.exit(0)
print str(time.asctime(time.localtime(time.time()))) + '\tStarting'

random.seed(sys.argv[1]) #Set seed
event_list = DES() #initialize event list
node_list = [] #List of nodes
results = []

#Add all resource advertise events at once as their order matters. Data is already ordered
res = Resources()
while 1:
    tmp_att = res.get_attribs() #Get advs 1 at a time
    if len(tmp_att) > 0:
        if (int(tmp_att[0][1]) + 1) > Config.NUM_NODES:
            print 'Error: No of nodes & attributes don\'t match'
            sys.exit(0)
        #Add to event list
        event_list.add_event(float(tmp_att[1][1]), 1, int(tmp_att[0][1]), tmp_att[2:], False)
    else: 
        break
print str(time.asctime(time.localtime(time.time()))) + '\t' + str(event_list.now) + '\tFinish adding all the events'

overlay = Overlay() #Initilize overlay
print str(time.asctime(time.localtime(time.time()))) + '\t' + str(event_list.now) + '\tFinish inilializing Overlay'

#Generate list of nodes
for i in range(0, Config.NUM_NODES):
    #keys = []
    node_list.append(Node(i, overlay.node_key_list[i], Config.NUM_RINGS))
    event_list.add_event(1 + random.expovariate(1.0/Config.QUERY_RATE), 2, i, [], True)
print str(time.asctime(time.localtime(time.time()))) + '\t' + str(event_list.now) + '\tNode list generated'

que = Queries() #query object

#Process the event list
print str(time.asctime(time.localtime(time.time()))) + '\t' + str(event_list.now) + '\tStarting to process event list'
while 1:
    event = event_list.get_next_event()
    if len(event) == 0: break #No more events

    if event[1] == 1: #Adv event
        tmp_adv = node_list[event[2]].advertise(event[3], True, False) #Use threshold, Need all attribs
        if len(tmp_adv[1]) > 0: #Remove old Ads 1st
            hops = overlay.route_old_ads(event[2], tmp_adv[1], node_list)
            if hops == -1: print 'Error: Unable to route remove advertisment %s' % tmp_adv
            else: results.append(['rem', event_list.now, hops]) # Track stat
        if len(tmp_adv[0]) > 0: #Send new Ads
            hops = overlay.route_new_ads(event[2], tmp_adv[0], node_list) #Advertise updated attributes
            if hops == -1: print 'Error: Unable to route resource advertisment %s' % tmp_adv
            else: results.append(['adv', event_list.now, hops]) # Track stat
    elif event[1] == 2: #Query event
        tmp_query = que.get_query() #Get a new query
        if len(tmp_query) == 0: continue #If no more queires
        tmp_query = node_list[event[2]].query(tmp_query)
        hops = overlay.route_query(event[2], tmp_query, node_list) # Route query
        if hops == -1: print 'Error: Unable to route query %s' % tmp_query
        else: results.append(['que', event_list.now, hops]) # Track stat
        #Reschedule timer
        event_list.add_event(event_list.now + random.expovariate(1/Config.QUERY_RATE), 2, event[2], [], True)
    else:
        print 'Error: Unknown event type \'%s\'' % event

print str(time.asctime(time.localtime(time.time()))) + '\t' + str(event_list.now) + '\tFinish processing events. Generating stattistics'

#Put adv & query cost into buckets
fdOut = open(BaseConfig.RESULT_FILE, 'w')
bucket_end_time = Config.BUCKET_SIZE
adv_cost = que_cost = rem_cost = 0
cost_bucket = []
#Inilialize bucket. [Bucket time, adv, rem, que]
for i in range(0, int(math.ceil(event_list.now/Config.BUCKET_SIZE)) + 1):
    cost_bucket.append([i * Config.BUCKET_SIZE, 0, 0, 0])
for i in range(0, len(results)):
    cost = 0
    if results[i][0] == 'adv' : 
        for j in range(0, len(results[i][2])): cost = cost + results[i][2][j]
        cost_bucket[int(results[i][1]/Config.BUCKET_SIZE)][1] = \
            cost_bucket[int(results[i][1]/Config.BUCKET_SIZE)][1] + cost 
    elif results[i][0] == 'rem' :
        for j in range(0, len(results[i][2])): cost = cost + results[i][2][j]
        cost_bucket[int(results[i][1]/Config.BUCKET_SIZE)][2] = \
            cost_bucket[int(results[i][1]/Config.BUCKET_SIZE)][2] + cost 
    elif results[i][0] == 'que' : 
        for j in range(0, len(results[i][2])): cost = cost + results[i][2][j]
        cost_bucket[int(results[i][1]/Config.BUCKET_SIZE)][3] = \
            cost_bucket[int(results[i][1]/Config.BUCKET_SIZE)][3] + cost 
    else: print 'Unknown results attribute : ' + str(results[i][0])

#Dumpu bucket
total_adv_cost = total_rem_cost = total_que_cost = 0
for i in range(0, len(cost_bucket)):
    total_adv_cost = total_adv_cost + cost_bucket[i][1]
    total_rem_cost = total_rem_cost + cost_bucket[i][2]
    total_que_cost = total_que_cost + cost_bucket[i][3]
    fdOut.write('Bucket\t' + str(cost_bucket[i][0]) + '\t' + str(cost_bucket[i][1]) + '\t' + str(cost_bucket[i][2]) \
                + '\t' + str(cost_bucket[i][3]) + '\t' + str(total_adv_cost) + '\t' + str(total_rem_cost) + '\t' \
                + str(total_que_cost) + '\n')
print 'Adv cost: %d' % total_adv_cost
print 'Rem cost: %d' % total_rem_cost
print 'Query cost: %d' % total_que_cost

#Dump Adv & query cost
for i in range(0, len(results)):
    if results[i][0] == 'adv': tmp_str = 'Adv\t' + str(results[i][1]) + '\t' + str(results[i][2])
    elif results[i][0] == 'rem': tmp_str = 'Rem\t' + str(results[i][1]) + '\t' + str(results[i][2])
    elif results[i][0] == 'que': tmp_str = 'Que\t' + str(results[i][1]) + '\t' + str(results[i][2])
    else: print 'unknown results attribute %s' % results[i][0]
    fdOut.write(tmp_str + '\n')

#Dump node statistics
for i in range(0, Config.NUM_NODES):
    tmp_str = 'NStat'
    stat = node_list[i].get_stat()
    for j in range(0, len(stat)): tmp_str = tmp_str + '\t' + str(stat[j])
    fdOut.write(tmp_str + '\n')

fdOut.close()
