#!/usr/bin/env python
# -*- coding: Latin-1 -*-
"""
@file    ParamEffectsProcessedFCD.py
@author  Sascha Krieg
@author  Daniel Krajzewicz
@author  Michael Behrisch
@date    2008-07-26
@version $Id: ParamEffectsProcessedFCD.py 20482 2016-04-18 20:49:42Z behrisch $

Creates files with a comparison of speeds for each edge between the taxis and the average speed from the current edge.
Dependent of the frequency and the taxi quota.

Needed files (have a look at Path.py):
    FQedgeDump
    FQedgeDumpPickle
    FQvtype
    FQvtypePickle
    FQvehPickle
    FQoutput
SUMO, Simulation of Urban MObility; see http://sumo.dlr.de/
Copyright (C) 2008-2016 DLR (http://www.dlr.de/) and contributors

This file is part of SUMO.
SUMO is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 3 of the License, or
(at your option) any later version.
"""
from __future__ import absolute_import
from __future__ import print_function

import random
import time
import os.path
import cProfile
import util.Path as path
from cPickle import dump
from cPickle import load
from util import BinarySearch
import util.CalcTime as calcTime


# cons
# write vehicles which are chosen as taxis to a raw-fcd-file to process them
W_FCD = 1
U_FCD = 2  # use processed FCD to create output for readPlot
U_RAW = 3  # use the vtypeprobe-data directly to create output for readPlot


#global vars
mode = W_FCD  # choose the mode
simStartTime = 21600  # =6 o'clock  ->begin in edgeDump
aggInterval = 900  # aggregation Interval of the edgeDump
# [5,10]#[20,40,60,80,100,150,200,250,300]#[1,2,5,10,20,50,100,200,500]#[20,50,100,200,500] #period in seconds | single element or a hole list
period = [20, 40, 60, 80, 100, 150, 200, 250, 300]
# [0.1, 0.2, 0.5, 1.0, 2.0, 5.0, 10.0, 20., 50.] #how many taxis in percent of the total vehicles | single element or a hole list
quota = [0.1, 0.2, 0.5, 1.0, 2.0, 5.0, 10.0, 20., 50.]
iteration = 2

vehId = 0
vehIdDict = {}
edgeDumpDict = None
vtypeDict = None
vehList = None
vehSum = None
procFcdDict = None


def main():
    global edgeDumpDict, vtypeDict, vehList, vehSum, period, quota, procFcdDict

    print("start program")
    edgeDumpDict = make(path.FQedgeDumpPickle, path.FQedgeDump, readEdgeDump)
    vtypeDict = make(path.FQvtypePickle, path.FQvtype, readVtype)
    vehList = make(
        path.FQvehPickle, path.FQvtypePickle, getVehicleList, False, vtypeDict)
    vehSum = len(vehList)
    if mode == U_FCD:
        print("load source: ", os.path.basename(
            path.FQprocessedFCD), "...", end=' ')
        procFcdDict = readProcessedFCD()
        print("Done!")

    orgPath = path.FQoutput
    if mode == W_FCD:
        orgPath = path.FQrawFCD
    orgPeriod = period
    orgQuota = quota
    for i in range(iteration):
        print("iteration: ", i)
        period = orgPeriod
        quota = orgQuota
        path.FQoutput = orgPath + \
            "interval900s_iteration" + str(i) + ".out.xml"
        path.FQrawFCD = orgPath + \
            "interval900s_iteration" + str(i) + ".out.dat"
        if mode == W_FCD:
            writeRawFCD()
        else:
            createOutput()

    print("end")


def generatePeriodQuotaSets(stopByPeriod=False):
    global period, quota

    """Generates all period-quota-sets (with creation of new Taxis for each set). 
    You can iterate over that generator and gets for each step the period and quota.
    If stopByPeriod=True it stops not only in the quota block but in the period block to-> have a look at the code.
    """
    if type(period) != list:
        period = [period]
    if type(quota) != list:
        quota = [quota]
    pList = period
    qList = quota
    for period in pList:
        if stopByPeriod:
            yield (period, None, None, None)
        for quota in qList:
            print("create output for: period ", period, " quota ", quota)
            taxis = chooseTaxis(vehList)
            taxiSum = len(taxis)
            if mode == U_FCD:
                vtypeDictR = procFcdDict[(period, quota)]
            else:
                vtypeDictR = reduceVtype(taxis)
            del taxis
            yield(period, quota, vtypeDictR, taxiSum)


def readEdgeDump():
    """Get for each interval all edges with corresponding speed."""
    edgeDumpDict = {}
    begin = False
    interval = 0
    inputFile = open(path.FQedgeDump, 'r')
    for line in inputFile:
        words = line.split('"')
        if not begin and words[0].find("<end>") != -1:
            words = words[0].split(">")
            interval = int(words[1][:-5])
            edgeDumpDict.setdefault(interval, [])
        elif words[0].find("<interval") != -1 and int(words[1]) >= simStartTime:
            interval = int(words[1])
            begin = True
        if begin and words[0].find("<edge id") != -1:
            edge = words[1]
            if edge[0] != ':':
                speed = float(words[13])
                entered = int(words[15])
                # if no vehicle drove of the edge ignore the edge
                if entered == 0:
                    continue
                edgeDumpDict.setdefault(interval, []).append((edge, speed))
    inputFile.close()
    return edgeDumpDict


def readVtype():
    """Gets all necessary information of all vehicles."""
    vtypeDict = {}
    timestep = 0
    begin = False
    inputFile = open(path.FQvtype, 'r')
    for line in inputFile:
        words = line.split('"')
        if words[0].find("<timestep ") != -1 and int(words[1]) >= simStartTime:
            timestep = int(words[1])
            begin = True
        if begin and words[0].find("<vehicle id=") != -1:
            if words[3][0] != ':':  # except inner edges
                edge = words[3][:-2]
                # del / Part of edge
                if edge.find("/") != -1:
                    edge = edge.split("/")[0]
                # time                 id    edge           speed
                # x           y
                vtypeDict.setdefault(timestep, []).append(
                    (words[1], edge, float(words[15]), words[13], words[11]))

    inputFile.close()
    return vtypeDict


def readProcessedFCD():
    """Reads the processed FCD and creates a List of vtypeDict fakes with can be used similarly."""
    procFcdDict = {}
    pqDateDict = {}  # each date is a period / quota tupel assigned
    simDate = '2007-07-18 '
    day = 0
    # create keys for the procFcdDict
    for p in period:
        for q in quota:
            day += 86400
            date, time = calcTime.getDateFromDepart(day).split(" ")
            pqDateDict.setdefault(date, (p, q))
            procFcdDict.setdefault((p, q), {})
            # print date,p,q

    inputFile = open(path.FQprocessedFCD, 'r')
    for line in inputFile:
        timestamp, edge, speed, cover, id = line.split('\t')
        date, time = calcTime.getNiceTimeLabel(timestamp).split(" ")
        # add values to actual Dict
        timestep = calcTime.getTimeInSecs(simDate + time)
        procFcdDict[pqDateDict[date]].setdefault(
            timestep, []).append((id, edge, float(speed) / 3.6))
    inputFile.close()

    return procFcdDict


def getVehicleList(vtypeDict):
    """Collects all vehicles used in the simulation."""
    vehSet = set()
    for timestepList in vtypeDict.values():
        for elm in timestepList:
            vehSet.add(elm[0])
    return list(vehSet)


def make(source, dependentOn, builder, buildNew=False, *builderParams):
    """Fills the target (a variable) with Information of source (pickelt var).
       It Checks if the pickle file is up to date in comparison to the dependentOn file.
       If not the builder function is called.
       If buildNew is True the builder function is called anyway.     
    """
    # check if pickle file exists
    if not os.path.exists(source):
        buildNew = True
    # check date
    # if source is newer
    if not buildNew and os.path.getmtime(source) > os.path.getmtime(dependentOn):
        print("load source: ", os.path.basename(source), "...", end=' ')
        target = load(open(source, 'rb'))
    else:
        print("build source: ", os.path.basename(source), "...", end=' ')
        target = builder(*builderParams)
        # pickle the target
        dump(target, open(source, 'wb'), 1)
    print("Done!")
    return target


def chooseTaxis(vehList):
    """ Chooses from the vehicle list random vehicles with should act as taxis."""
    # calc absolute amount of taxis
    taxiNo = int(round(quota * len(vehList) / 100))

    random.shuffle(vehList)
    return vehList[:taxiNo]


def reduceVtype(taxis):
    """Reduces the vtypeDict to the relevant information."""
    taxis.sort()  # sort it for binary search
    newVtypeDict = {}
    for timestep in vtypeDict:
        # timesteps which are a multiple of the period
        if timestep % period == 0:
            newVtypeDict[timestep] = (
                [tup for tup in vtypeDict[timestep] if BinarySearch.isElmInList(taxis, tup[0])])
    return newVtypeDict


def writeRawFCD():
    """Creates a file in the raw-fcd-format of the chosen taxis"""
    global vehId, vehIdDict
    vehIdDict = {}
    vehId = 0
    day = 0

    def getVehId(orgId):
        """creates new vehicle id's which consists only numerics"""
        global vehId, vehIdDict
        value = vehIdDict.get(orgId, vehId)
        if value is vehId:
            vehIdDict[orgId] = vehId
            vehId = (vehId + 1) % 65500
        return value

    outputFile = open(path.FQrawFCD, 'w')

    for period, quota, vtypeDictR, taxiSum in generatePeriodQuotaSets():
        day += 86400
        # reset dict so that every taxi (even if the vehicle is chosen several
        # times) gets its own id
        vehIdDict = {}
        # dataset=0
        sortedKeys = vtypeDictR.keys()
        sortedKeys.sort()
        for timestep in sortedKeys:
            taxiList = vtypeDictR[timestep]
            for tup in taxiList:  # all elements in this timestep
                # calc timestep ->for every period /quota set a new day
                time = timestep + day
                time = calcTime.getDateFromDepart(time)
                # dataset+=1
                # print ouptut
                # veh_id         date (time to simDate+time)    x (remove and
                # set comma new)
                outputFile.write(str(getVehId(tup[0])) + '\t' + time + '\t' + tup[3][0:2] + '.' + tup[3][2:7] + tup[3][8:] +
                                 # y (remove and set comma new)
                                 # status      speed form m/s in km/h
                                 '\t' + tup[4][0:2] + '.' + tup[4][2:7] + tup[4][8:] + '\t' + "90" + '\t' + str(int(round(tup[2] * 3.6))) + '\n')
        # print dataset, time
        print(vehId)
    outputFile.close()


def createOutput():
    """Creates a file with a comparison of speeds for each edge 
    between the taxis and the average speed from the current edge."""

    firstPeriod = True
    # get edge No
    edgesNo = 0
    edgesSet = set()
    for timestep, taxiList in vtypeDict.iteritems():
        for tup in taxiList:
            edgesSet.add(tup[1])
    edgesNo = len(edgesSet)

    outputFile = open(path.FQoutput, 'w')
    outputFile.write('<?xml version="1.0"?>\n')
    outputFile.write('<paramEffects aggregationInterval="%d" vehicles="%d" edges="%d">\n' % (
        aggInterval, vehSum, edgesNo))
    for period, quota, vtypeDictR, taxiSum in generatePeriodQuotaSets(True):
        if quota == None:
            if not firstPeriod:
                outputFile.write("\t</periods>\n")
            else:
                firstPeriod = False
            outputFile.write('\t<periods period="%d">\n' % (period))
        else:
            simpleTaxiMeanVList = [0, 1]
            simpleEdgeMeanVList = [0, 1]
            drivenEdgesSet = set()

            if len(vtypeDictR) == 0:  # if the processed FCD returns no Values
                print("noData p", period, " q", quota)
                drivenEdgesSet.add(0)
            else:  # create mean from all taxi speed values
                for timestep, taxiList in vtypeDictR.iteritems():
                    for tup in taxiList:  # all elements in this timestep
                        simpleTaxiMeanVList[0] += tup[2]
                        simpleTaxiMeanVList[1] += 1
                        drivenEdgesSet.add(tup[1])
            # create mean from all edge speed values which are driven by the
            # chosen taxis
            drivenEdgesList = list(drivenEdgesSet)
            drivenEdgesList.sort()
            # print "dataSets ",simpleTaxiMeanVList[1]

            #--edgeDump--#
            """
            for i in edgeDumpDict.keys(): #all intervals
                for edge,v in edgeDumpDict[i]:                   
                   if BinarySearch.isElmInList(drivenEdgesList,edge):
                       simpleEdgeMeanVList[0]+=v
                       simpleEdgeMeanVList[1]+=1
            """
            #--vtype--#

            for timestep, taxiList in vtypeDict.iteritems():
                for tup in taxiList:
                    if BinarySearch.isElmInList(drivenEdgesList, tup[1]):
                        simpleEdgeMeanVList[0] += tup[2]
                        simpleEdgeMeanVList[1] += 1

            # calc values for output
            detectedEdges = len(drivenEdgesSet)
            relDetectedEdges = detectedEdges * 100.0 / edgesNo
            vSim = simpleEdgeMeanVList[0] / simpleEdgeMeanVList[1]
            vSimFCD = simpleTaxiMeanVList[0] / simpleTaxiMeanVList[1]
            vAbsDiff = vSimFCD - vSim
            if vSim != 0:
                vRelDiff = vAbsDiff / vSim * 100
            else:
                vRelDiff = 100
            if vRelDiff < -40:
                vRelDiff = -35

            outputFile.write('\t\t<values taxiQuota="%f" taxis="%d" simMeanSpeed="%f" simFcdMeanSpeed="%f" ' % (
                quota, taxiSum, vSim, vSimFCD,))
            outputFile.write('detectedEdges="%d" notDetectedEdges="%d" ' % (
                detectedEdges, edgesNo - detectedEdges))
            outputFile.write('absSpeedDiff="%f" relSpeedDiff="%f" relDetectedEdges="%f" relNotDetectedEdges="%f"/>\n' %
                             (vAbsDiff, vRelDiff, relDetectedEdges, 100 - relDetectedEdges))
    outputFile.write("\t</periods>\n</paramEffects>")
    outputFile.close()


def getMean(list):
    """Returns a median value from the values in the given list."""
    return sum(list) / len(list)

# start the program
# cProfile.run('main()')
main()
