'''
Created on Sep 9, 2011
@version: 0.1
@author: Dilum Bandara
@email: dilumb@engr.colostate.edu
@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.
'''

'''
Generate queries for CCN-Event query simulator
'''

import random
import sys
from operator import itemgetter
import Config as Config


#Users. Suppose NWS & EM users are placed within a grid & can be at any random 
#location within grid box. Others users are randomly deployed within entire 
#simulation area
#numNWS = [2, 2] #number of NWS users [divide x-axis, divide y-axis]
#numEM = [3, 4] #number of EMs
#numRES = [5] #Researches
#numMED = [8] #Media
numNWS = [6, 5] #number of NWS users [divide x-axis, divide y-axis]
numEM = [20, 16] #number of EMs
numRES = [30] #Researches
numMED = [120] #Media
numPUB = [0] #General public

genBaseTime = 300 #Starting time to generate queries }
#genEndTime = 7200 #Time for last query
genEndTime = 86400 * 3 #Time for last query
queryRate = 1/300.0 #Query rate = 1/(inter-arrival time)
dataValidTill = 1800
attNames = ['DD', 'ELEV', 'FF', 'P', 'RH', 'T']
sensorField = [1000000, 1000000] #Upper right corner


def genAOIs(numUsers, startUID):
    '''
    Generate AOI for given type of users
    @param numUsers: Number of users as a list [along x, along y] or [num]
    @param startUID: User ID for 1st user 
    '''
    users = {}
    uID = startUID
    if len(numUsers) == 2: #Queries are issued only to a region in sensor filed 
        deltaX = sensorField[0]/float(numUsers[0])
        deltaY = sensorField[1]/float(numUsers[1])
    
        for j in range(numUsers[0]):
            for k in range(numUsers[1]):
                ll = [j * deltaX, k * deltaY] #Lower left
                ur = [(j + 1) * deltaX, (k + 1) * deltaY] #Upper right
                AOI = [ll, ur]
                users[uID] = AOI
                uID += 1
    else:
        AOI = [[0, 0], sensorField]
        for i in range(numUsers[0]):
            users[uID] = AOI
            uID += 1
    return users


def genQuery(numAtt, selectivity, queryRate):
    '''
    Generate a query
    @param numAtt: Number of attributes in a query in addition to x & y
    @param selectivity: Query selectivity (range of attribute values)
    @param queryRate: Mean inter-arrival time of queries  
    '''
    attribs = []
    while True: #Generate attributes
        att = attNames[random.randint(0, len(attNames) - 1)]
        if att not in attribs: attribs.append(att)
        if len(attribs) == numAtt: break

    query = []
    for att in attribs: #Pick attribute values
        if att == 'DD':
            #Half of selectivity
            halfSelec = (Config.MAX_DD - Config.MIN_DD) * selectivity / 2  
            mid = random.uniform(Config.MIN_DD + halfSelec, \
                                                    Config.MAX_DD - halfSelec) 
            query.append([att, mid - halfSelec, mid + halfSelec])
        elif att == 'ELEV':
            halfSelec = (Config.MAX_ELEV - Config.MIN_ELEV) * selectivity / 2  
            mid = random.uniform(Config.MIN_ELEV + halfSelec, \
                                                    Config.MAX_ELEV - halfSelec) 
            query.append([att, mid - halfSelec, mid + halfSelec])
        elif att == 'FF':
            halfSelec = (Config.MAX_FF - Config.MIN_FF) * selectivity / 2  
            mid = random.uniform(Config.MIN_FF + halfSelec, \
                                                    Config.MAX_FF - halfSelec) 
            query.append([att, mid - halfSelec, mid + halfSelec])
        elif att == 'P':
            halfSelec = (Config.MAX_P - Config.MIN_P) * selectivity / 2  
            mid = random.uniform(Config.MIN_P + halfSelec, \
                                                    Config.MAX_P - halfSelec) 
            query.append([att, mid - halfSelec, mid + halfSelec])
        elif att == 'RH':
            halfSelec = (Config.MAX_RH - Config.MIN_RH) * selectivity / 2  
            mid = random.uniform(Config.MIN_RH + halfSelec, \
                                                    Config.MAX_RH - halfSelec) 
            query.append([att, mid - halfSelec, mid + halfSelec])
        elif att == 'T':
            halfSelec = (Config.MAX_T - Config.MIN_T) * selectivity / 2  
            mid = random.uniform(Config.MIN_T + halfSelec, \
                                                    Config.MAX_T - halfSelec) 
            query.append([att, mid - halfSelec, mid + halfSelec])
        
    return [query, random.expovariate(queryRate)]
    

def dumpQueries(queries, fOut):
    '''
    First sort queries cronologically then dump to file
    @param queries: List of queries
    @param fOut: File name  
    '''
    sortedQueries = sorted(queries, key=itemgetter(1))
    fdOut = open(fOut, 'w')
    for i in range(len(sortedQueries)):
        tmpStr = str(sortedQueries[i][0]) + '\t' + \
            str(round(sortedQueries[i][1], 4)) + '\t' + \
            str(sortedQueries[i][2])
        for j in sortedQueries[i][3]:
            tmpStr += '\t' + j[0] + '\t' + str(round(j[1], 4)) + '\t' + \
                                                    str(round(j[2], 4))
        fdOut.write(tmpStr + '\n')
    fdOut.close()


def main(*argv):
    '''
    Main function to indicate start of program
    '''
    if len(sys.argv) != 5:
        print 'Invalid argument. python genQuery.py <no of attributes in query>'\
            + '<query selectivity> <query file> <random seed>'
        sys.exit(0)
    numAttInQue = int(sys.argv[1]) #Number of attributes in a query except X & Y
    qSelectivity = float(sys.argv[2]) #As a fraction of the possible range
    fOut = sys.argv[3]
    random.seed(sys.argv[4]) #Set seed

    #Find AOIs of users
    uIDs = 0
    users = genAOIs(numNWS, uIDs)
    uIDs += numNWS[0] * numNWS[1]
    tmpUsers = genAOIs(numEM, uIDs)
    for i in tmpUsers: users[i] = tmpUsers[i]
    uIDs += numEM[0] * numEM[1]
    tmpUsers = genAOIs(numRES, uIDs)
    for i in tmpUsers: users[i] = tmpUsers[i]
    uIDs += numRES[0]
    tmpUsers = genAOIs(numMED, uIDs)
    for i in tmpUsers: users[i] = tmpUsers[i]
    uIDs += numMED[0]

    print 'No users', uIDs, len(users)
    
    #Generate queries
    queries = []
    for uID in users:
        t = genBaseTime
        while True:
            [que, delta]= genQuery(numAttInQue, qSelectivity, queryRate)
            t += delta
            if t > genEndTime: break #Exceed limit
            AOI = users[uID] 
            tmpQue = [['X', AOI[0][0], AOI[1][0]], ['Y', AOI[0][1], AOI[1][1]]]
            queries.append([uID, t, dataValidTill, tmpQue + que])  
    print 'Sorting'      
    dumpQueries(queries, fOut)
    print 'Done...'


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