#!/usr/bin/env python
# Eclipse SUMO, Simulation of Urban MObility; see https://eclipse.dev/sumo
# Copyright (C) 2007-2026 German Aerospace Center (DLR) and others.
# This program and the accompanying materials are made available under the
# terms of the Eclipse Public License 2.0 which is available at
# https://www.eclipse.org/legal/epl-2.0/
# This Source Code may also be made available under the following Secondary
# Licenses when the conditions for such availability set forth in the Eclipse
# Public License 2.0 are satisfied: GNU General Public License, version 2
# or later which is available at
# https://www.gnu.org/licenses/old-licenses/gpl-2.0-standalone.html
# SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later

# @file    edgeDataFromFlow.py
# @author  Jakob Erdmann
# @author  Mirko Barthauer
# @date    2020-02-27

"""
This script converts a flow file in csv-format to XML
(generalized meandata format : http://sumo.dlr.de/xsd/meandata_file.xsd)
"""
from __future__ import absolute_import
from __future__ import print_function
import sys
import os

from collections import defaultdict

import detector

SUMO_HOME = os.environ.get('SUMO_HOME',
                           os.path.join(os.path.dirname(os.path.abspath(__file__)), '..', '..'))
sys.path.append(os.path.join(SUMO_HOME, 'tools'))
import sumolib  # noqa
from sumolib.xml import parse  # noqa
from sumolib.options import ArgumentParser  # noqa
DEBUG = False


def get_options(args=None):
    parser = ArgumentParser(description="Convert detector flow file to edgeData format")
    parser.add_argument("-d", "--detector-file", dest="detfile", category="input", type=ArgumentParser.additional_file,
                        help="read detectors from FILE", metavar="FILE")
    parser.add_argument("-f", "--detector-flow-file", dest="flowfile", category="input", type=ArgumentParser.file,
                        help="read detector flows to compare to from FILE (mandatory)", metavar="FILE")
    parser.add_argument("-o", "--output-file", dest="output", category="output", type=ArgumentParser.edgedata_file,
                        help="output edgeData FILE (mandatory)", metavar="FILE")
    parser.add_argument("--id-column", default="Detector", dest="detcol",
                        help="Read detector ids from the given column")
    parser.add_argument("--time-column", default="Time", dest="timecol",
                        help="Read detector time from the given column")
    parser.add_argument("--time-scale", type=int, default=60, dest="timescale",
                        help="Interpretation of time units in seconds (default 60)")
    parser.add_argument("--time-format", dest="timeFormat",
                        help="Format string for interpreting custom time values")
    parser.add_argument("--time-offset", dest="timeOffset",
                        help="time value to subbtract from parsed times")
    parser.add_argument("-q", "--flow-columns", dest="flowcols", default="qPKW,qLKW", type=str,
                        help="which columns contains flows (specified via column header)", metavar="STRING")
    parser.add_argument("-b", "--begin", default=0, type=ArgumentParser.time,
                        help="custom begin time (minutes or H:M:S)")
    parser.add_argument("-e", "--end", default=1440, type=ArgumentParser.time,
                        help="custom end time (minutes or H:M:S)")
    parser.add_argument("-i", "--interval", default=1440, type=ArgumentParser.time,
                        help="custom aggregation interval (minutes or H:M:S)")
    parser.add_argument("--cadyts", action="store_true",
                        default=False, help="generate output in cadyts format")
    parser.add_argument("-v", "--verbose", action="store_true", dest="verbose",
                        default=False, help="tell me what you are doing")
    options = parser.parse_args(args=args)
    if not options.flowfile or not options.output:
        parser.print_help()
        sys.exit()

    return options


class LaneMap:
    def get(self, key, default):
        return key[0:-2]


def main(options):
    readers = {}
    flowcols = options.flowcols.split(',')
    tMin = None
    tMax = None
    for flowcol in flowcols:
        detReader = detector.DetectorReader(options.detfile, LaneMap())
        tMin, tMax = detReader.findTimes(
                options.flowfile, tMin, tMax,
                options.detcol, options.timecol,
                options.timeFormat, options.timeOffset)
        hasData = detReader.readFlows(options.flowfile, flow=flowcol, det=options.detcol,
                                      time=options.timecol, timeVal=tMin,
                                      timeMax=tMin + 1000,
                                      timeFormat=options.timeFormat,
                                      timeOffset=options.timeOffset)
        if options.verbose:
            print("flowColumn: %s hasData: %s" % (flowcol, hasData))
        readers[flowcol] = detReader

    if options.verbose:
        print("found data from minute %s to %s" % (int(tMin), int(tMax)))

    ts = options.timescale
    beginM = int(sumolib.miscutils.parseTime(options.begin, ts) / ts)
    intervalM = int(sumolib.miscutils.parseTime(options.interval, ts) / ts)
    endM = min(int(sumolib.miscutils.parseTime(options.end, ts) / ts), tMax)

    with open(options.output, "w") as outf:
        root = "measurements" if options.cadyts else "data"
        sumolib.xml.writeHeader(outf)
        outf.write('<%s>\n' % root)
        while beginM <= endM:
            iEndM = beginM + intervalM
            edges = defaultdict(dict)  # edge : {attr:val}
            maxGroups = defaultdict(lambda: 0)  # edge : nGroups

            for flowcol in flowcols:
                detReader = detector.DetectorReader(options.detfile, LaneMap())
                detReader.readFlows(options.flowfile, flow=flowcol, det=options.detcol, time=options.timecol,
                                    timeVal=beginM, timeMax=iEndM,
                                    addDetectors=(options.detfile is None),
                                    timeFormat=options.timeFormat,
                                    timeOffset=options.timeOffset)
                for edge, detData in detReader._edge2DetData.items():
                    maxFlow = 0
                    nGroups = 0
                    for group in detData:
                        if group.isValid:
                            maxFlow = max(maxFlow, group.totalFlow)
                            nGroups += 1
                    # if options.verbose:
                    #    print("flowColumn: %s edge: %s flow: %s groups: %s" % (
                    #        flowcol, edge, maxFlow, nGroups))
                    edges[edge][flowcol] = maxFlow
                    maxGroups[edge] = max(maxGroups[edge], nGroups)

            if options.cadyts:
                for edge in sorted(edges.keys()):
                    print('    <singlelink link="%s" start="%s" end="%s" value="%s" stddev="8" type="COUNT_VEH"/>' %
                          (edge, beginM * ts, iEndM * ts, sum(edges[edge].values())), file=outf)
            else:
                outf.write('    <interval id="flowdata" begin="%s" end="%s">\n' % (beginM * ts, iEndM * ts))
                for edge in sorted(edges.keys()):
                    attrs = ' '.join(['%s="%s"' % (k, v) for k, v in sorted(edges[edge].items())])
                    outf.write('        <edge id="%s" %s groups="%s"/>\n' % (edge, attrs, nGroups))
                outf.write('    </interval>\n')
            beginM += intervalM
        outf.write('</%s>\n' % root)


if __name__ == "__main__":
    main(get_options())
