'''
Created on Jan 5, 2011
Modified on Apr 24, 2011
@author: Dilum Bandara
@version: 0.21
@copyright: Copyright(c) 2011, Dilum Bandara
@license: GPL
'''

import BaseClasses.BaseConfig as BaseConfig
from BaseClasses.DES import DES
from BaseClasses.ReadData import ReadData
from Peer import Peer
from Overlay import Overlay
from BaseClasses.BaseStat import BaseStat
import Config
import sys
import random
import time
import os

in_data = ReadData(BaseConfig.RESOURCE_FILE, BaseConfig.QUERY_FILE) #Input data object
events = DES() #initialize event list
stat = BaseStat(BaseConfig.RESULT_FILE, Config.TRACK_ATT_COST)


def init_peers(num_peers):    
    '''
    Initialize peers
    @param num_peers: Number of peers 
    '''
    for i in range(num_peers):
        result = in_data.get_resource() #Get resource attributes
        if len(result) == 0:
            print 'ERROR: Not enough resources: ' + str(i) + ' Terminating...'
            sys.exit(0)
        if (result[0][1] + 1) > Config.NUM_PEERS:
            print 'ERROR: No of peers in resource file don\'t match'
            sys.exit(0)
        BaseConfig.PEERS[result[0][1]] = Peer(result[0][1], \
                    BaseConfig.OVERLAY.node_key_list[i], Config.NUM_RINGS) #No key, use rings
        events.add_event(result[1][1], 1, result[0][1], result[2:], True)
        #Schedule query timer. make sure resources are advertised first
        #Use Poison distribution
        events.add_event(Config.DELAY_BEFORE_QUE + \
                    random.expovariate(1.0/Config.QUE_RATE), 2, result[0][1], [], True)
    print str(time.asctime(time.localtime(time.time()))) + '\tNode list generated'


def handle_adv_event(event):
    '''
    Handle advertise event
    @param event: Event to handle
    '''
    #Use threshold, Need only changed attr
    result = BaseConfig.PEERS[event[2]].advertise(event[3], True, False)
    if len(result) == 0:
        print 'ERROR: Peer ' + str(event[2]) + ' unable to advertise. Terminating'
        sys.exit(0)
    else: 
        stat.track_stat('adv', events.now, result)
    #No need to schedule timer as advertisment events are initiated by another timer


def handle_query_event(event):
    '''
    Handle query event
    @param event: Event to handle 
    '''
    tmp_query = in_data.get_query() #Get a new query
    if len(tmp_query) == 0: return #If no more queries
    result = BaseConfig.PEERS[event[2]].query(tmp_query)
    if result[0] == -1: #No of hops
        print 'Error: Unable to route query %s' % tmp_query
        sys.exit(0)
    else: stat.track_stat('que', events.now, result) # Track stat
    #Reschedule timer
    events.add_event(events.now + random.expovariate(1/Config.QUE_RATE), \
                         2, event[2], [], True)
    
    
def handle_resource_update(event, num_peers):
    '''
    Time to read the next set of resource attributes from file
    @param event: Event to handle
    @param num_peers: Number of peers
    '''    
    for i in range(num_peers):
        result = in_data.get_resource() #Get resource attributes
        if len(result) == 0: return #No more resources
        if (result[0][1] + 1) > num_peers:
            print 'ERROR: No of peers in resources file don\'t match'
            sys.exit(0)
        events.add_event(result[1][1], 1, (result[0][1]), result[2:], True)
    #reschedule timer
    events.add_event(events.now + Config.SAMPLING_INT, 3, -1, -1, True)


def main(*argv):
    '''
    Main function to indicate start of program
    '''
    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

    BaseConfig.OVERLAY = Overlay() #Initialize overlay
    init_peers(Config.NUM_PEERS) #Initialize peers
    #Add update resource attributes timer
    events.add_event(Config.SAMPLING_INT, 3, -1, -1, True) 
    #Process the event list
    print str(time.asctime(time.localtime(time.time()))) + '\tStarting to process event list'
    while 1:
        event = events.get_next_event() #Get next event
        if len(event) == 0: break #No more events
        if event[1] == 1: handle_adv_event(event) #Adv event
        elif event[1] == 2: handle_query_event(event) #Query event
        elif event[1] == 3: handle_resource_update(event, Config.NUM_PEERS) #read resources
        else:
            print 'ERROR: Unknown event type \'%s\'' % event
            break
    print str(time.asctime(time.localtime(time.time()))) + '\t' + str(events.now)\
              + '\tFinish processing events. Generating statistics.'
    stat.summ_stat() #Generate statistics
    print os.getcwd() #Print to see where the results are stored
    return 0


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