# Eclipse SUMO, Simulation of Urban MObility; see https://eclipse.dev/sumo
# Copyright (C) 2016-2026 German Aerospace Center (DLR) and others.
# hybridPY module
# Copyright (C) 2012-2026 University of Bologna - DICAM, Technical University of Munich
# 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    tripdata_analysis_without_hybridpy_py3.py
# @author  Joerg Schweizer
# @author  Fabian Schuhmann
# @author  Ngoc An Nguyen
# @author  Cristian Poliziani
# @date    2012

import xml.etree.ElementTree as ET
import csv
import matplotlib.pyplot as plt
import os
import sys


# TRIP DATA ANALYIS SCRIPT FOR SUMO simulations
# This Python 3 script works with SUMO simulation data created with and without SUMOPy

# Author:                   Maximilian Sievers, B. Sc.
# Start of development:     29-05-2023
# Latest update:            12-08-2023


# Detailed info on how to script works in the print-outs in the functions below.
# These functions may be called at launch, depending on launch arguments.

def print_script_description_part1():
    print('\n\n\n                   TRIP DATA ANALYIS SCRIPT FOR SUMO simulations')
    print('                 *************************************************')
    print('')
    print(' Author:                   Maximilian Sievers, B.Sc.')
    print(' Start of development:     29-05-2023')
    print(' Latest update:            12-08-2023')
    print('\n This Python 3 script works with SUMO simulation data created with and without SUMOPy.')
    print(' (tested with Python 3.7)\n')
    print(' It analyzes the trip data and calculates several KPIs (key performance indicators),')
    print(' plots the data and saves it to a .csv file.')


def print_script_description_part2():
    print('\n\n a) HOW to CREATE the PROPER xml file for this script TO ANALYZE TRIP DATA:\n'
          + '\n'
          + '    I)   If you are launching SUMO simulations from SUMOPy:\n'
          + '         In order for SUMOPy to generate the correct SUMO files, you MUST select the options:\n'
          + '          - Output Trip Data\n'
          + '          - Include Unifinished Trip Data\n'
          + '         After the SUMO simulation launched via SUMOPy has finished,\n'
          + '         the file containing the necessary trip data xml-file will have been generated\n'
          + '         in the same folder as your SUMOPy scenario\n'
          + '         ( SUMOPy scenarios are stored as  .obj  files)\n'
          + '         With SUMOPy, this tripdata file has the file-ending  .tripdata.xml \n'
          + '\n'
          + '    II)  If you are running SUMO simulation created WIHOUT SUMOPy:\n'
          + '         Before running the simulation, ensure the following parameters are included in\n'
          + '         the main SUMO config file. This file usually ends with .sumocfg \n'
          + '         The .sumocfg file should contain the following lines of code:\n'
          + '        \n'
          + '         <configuration ...>\n'
          + '             ...\n'
          + '             <output>\n'
          + '	                <tripinfo-output value="output\\tripinfo_output.xml"/>\n'
          + '	                <tripinfo-output.write-unfinished value="true"/>\n'
          + '             </output>\n'
          + '             ...\n'
          + '         </configuration>\n'
          + '        \n'
          + '         Note: the name of the .xml file containing the trip data is arbitrary wrt to this script.\n'
          + '              However: SUMO and SUMOPy name this same file differently.\n'
          + '              The SUMO website refers to it as: tripinfo file    or  tripinfo_output.xml\n'
          + '              https://sumo.dlr.de/docs/Simulation/Output/TripInfo.html\n'
          + '        \n'
          + '         Note: SUMOPy, on the other hand, names it the file:                    .out.tripdata.xml\n'
          + '               Given a SUMOPy scenario file named myscenario.obj,\n'
          + '               the tripdata file will be named:                       myscenario.out.tripdata.xml\n\n'
          + '         NAMING CONVENTION FOR THIS SCRIPT:\n'
          + '         The file containing the tripdata/tripinfo is REFERRRED TO AS THE TRIPDATA(.XML) FILE.\n'
          + '        \n'
          + '    III) Info for running SUMO simulations generated by SUMOPy scenarios on PCs without SUMOPy:\n'
          + '         If you have a (powertful) computer for running SUMO simulations,\n'
          + '         and you want to run a SUMOPy simulation on it, but SUMOPy is not installed there:\n'
          + '         you can run SUMO simulation files generated by SUMOPy without needing to install SUMOPy.\n'
          + '         This workaround does give access to many SUMOPy-specific-simulation features,\n'
          + '         but it can be a useful if SUMOPy is not installed.v\n'
          + '         Note: Usually, the main file you run with SUMO has the file ending .sumocfg\n'
          + '               When SUMOPy generates SUMO files, this same file has the file ending .netc\n'
          + '               E.g., the sumopy scenario myscenario.obj will generate the file myscenario.netc\n'
          + '               The .sumocfg and .netc files are exactly the same xml file type.\n'
          + '               Both can be opened and run with SUMO.\n'
          + '         \n'
          + '     IV) In addition to the tripdata.xml file (1st argument), containing the executed trips,\n'
          + '         this script also requires the xml file with all the planned trips (2nd argument).\n'
          + '         This second file is one of the SUMO simulation input files, required for SUMO to run.\n'
          + '         When generating SUMO scenarios via SUMOPy (e.g. using the SUMOPy scenario myscenario.obj),\n'
          + '         this xml file has the ending .rou.xml (e.g. myscenario.rou.xml).\n'
          + '         If you are not generating SUMO files via SUMOPy,\n'
          + '         this file may be called .rou.xml, .trips.xml, .demand.xml, etc., \n'
          + '         dpending on local/team/company conventions.\n'
          + '         This .rou.xml file is necessary because the vehicle id of unfinished ride stages\n'
          + '         of a person trip is often listed as "NULL".\n'
          + '         If this is the case, the .tripdata.xml file provides no information\n'
          + '         on what vehicle this is.\n'
          + '         By using the person id from the tripdata.xml file\n'
          + '         and looking up that id in the .rou.xml file,\n'
          + '         the originally intended vehicle can be retrieved,\n'
          + '         which is essential for calculating the modal split.\n'
          + '         \n'
          + '      V) SUMO XML common file endings and names recap:\n'
          + '         No binding SUMO conventions exist, file names and file endings fully customizable.\n'
          + '         The following table merely contains common file names and endings.\n'
          + '         ( --> If in doubt, check xml file contents to be sure.)\n'
          + '         (     Example .tripdata.xml and .rou.xml file included in this /scripts folder)\n'
          + '         \n'
          + '                                              |          SUMO         |       SUMOPy\n'
          + '         _______________________________________________________________________________\n'
          + '          File containing executed trip data: |  tripinfo_output.xml, |   .tripdata.xml\n'
          + '           (1st argument to run this script)  |         etc.          |\n'
          + '         _______________________________________________________________________________\n'
          + '          File containing planned trip data:  | .rou.xml, .trips.xml, |    .rou.xml\n'
          + '           (2nd argument to run this script)  |    .demand.xml,  etc. |\n'
          + '         _______________________________________________________________________________\n'
          + '           SUMO simulation config file:       |      .sumocfg         |     .netc\n'
          + '           (main file to run the SUMO sim)    |                       |\n\n'
          + ''
          + ' ***************************************************************************************************\n')
    print(' b) IN GENERAL, ONLY the INTERNAL DEMAND is ANALYZED by this script,\n'
          + '     \n'
          + '    i.e. data saved in personinfo nodes in the tripdata.xml file created by SUMO.\n'
          + '    A personinfo node contains all trip stages,of member of the virtual population.\n'
          + '    Of course, external demand may affect the trips of the internal demand,\n'
          + '    e.g. through traffic jams.\n'
          + '    However, since these external trips are only passing through the network,\n'
          + '    values such as door-door-travel times cannot be calculated for them.\n'
          + '    Furthermore, since the modal split of the external demand cannot be influenced by SUMOPy,\n'
          + '    the modal split of the external demand is not accounted for in the modal split\n'
          + '    calcuted by this script.\n'
          + '    Only the modal split of the internal demand\n'
          + '    can be adjusted by SUMOPy optimization algorithms,\n'
          + '    e.g. by iterating the simulation and subsequently reselecting the mode\n'
          + '    for each virtual person after each simulation iteration,\n'
          + '    e.g., based on the shortes executed travel time.\n'
          + '    Use the script runSimulation.py to find the optimum mode choice\n'
          + '    for the virtual population/internal demand\n'
          + '    by iterating SUMOPy simulations with the following script:\n'
          + '    runSimulation.\n'
          + '    The runSimulation.py script uses python 2.7 (like SUMOPy).\n'
          + '    It is located in this git repository under:\n'
          + '    Simulation/runSimulation.py\n'
          + '     \n'
          + '    RECAP: MODAL SPLIT = modal split OF INTERNAL DEMAND (= from virtual population),\n'
          + '    ******************   BASED ON the MODE OF transport\n'
          + '                         from the FIRST RIDE STAGE\n'
          + '                         OR LACK OF RIDE STAGES\n'
          + '                         of each personinfo node in the tripdata.xml file.\n'
          + '                         If there are no ride stages,\n'
          + '                         the trip is counted as a walk trip in the modal split\n'
          + '                         Both finished and unfinished trips are used\n'
          + '                         for calculating the model split.\n'
          + '                         (unfinished trips explained in part d) )\n'
          + '     \n'
          + '    IMPORTANT INFO regarding UNEXPECTED TAXIS / SAVs (SUMOPy only):\n'
          + '    Even if NO SAVs (shared autonomous vehicles) and/or NO PRIVATE TAXIS\n'
          + '    have been added to a SUMOPy scenario,\n'
          + '    SOME SAV or PRIVATE TAXI trips may still appear in the modal split.\n'
          + '    This is due to a backup-transport-mode feature of SUMOPy:\n'
          + '    If the selected mode of transport of a virtual person\n'
          + '    is passenger vehicle (a normal private car),\n'
          + '    but a normal passenger vehicle cannot access the origin or destination,\n'
          + '    the backup mode of tranport is chosen: the taxi mode.\n'
          + '    Since private taxis and SAVs are each a separate taxi fleet,\n'
          + '    either an SAV or a private Taxi may spawn in the simulation as the backup vehicle,\n'
          + '    parked in the virtual persons home or place of work like a private passenger vehicle.\n'
          + '    This private Taxi or SAV acts entirely like a private passenger vehicle\n'
          + '    belonging to that person, except that the vehicle looks\n'
          + '    like a private Taxi or SAV. No other persons are picked up.\n'
          + '    These backup private taxis / SAVs are quite rare, but they may appear.\n'
          + '    If the person makes use of such a backup taxi,\n'
          + '    their trip is accounted for as any normal private Taxi or SAV trip \n'
          + '    in the modal split calculated by this script.\n')
    #
    # TODO: True? Is backup mode maybe changeable?
    #             when launching SUMO simulation with SUMOPy via script?
    #       Option in SUMOPy gui somewhere?
    #  + '    Without changing the SUMOPy source code, you cannot disable the use of taxis/SAVs \n' \
    #  + '    as a backup mode for passenger vehicle stages of a virtual person trip.\n' )
    #
    # TODO: Accurate? Further research required:
    # The following statements need further investigation to validate them
    # (try setting the share in mode to 1.0 and 0.0 while generating plans)
    # changing the "share in mode parameter" has not effect/error warning
    # if you simply change it, and then select current plans
    # in SUMOPy or launch a SUMO simulation from SUMOPy.
    # Maybe regeneration of plans for virtual population necessary for "mode in share" param
    # to affect taxi/SAV backup-mode spawn probability???
    # Where again in SUMOPy was this defined exactly?
    #  + '     Per default, there is a 50/50 chance if the backup mode of transport
    #  + '     is a private Taxi or an SAV.
    #  + '     However, you can change the probability of which backup mode is used.
    #  + '     If, for example, you want the backup mode to be SAV 100% of the time:
    #  + '     In the SUMOPy gui, go to scenario.demand.vTypes
    #  + '     The third column of the vType parameters is the "Share in mode" parameter.
    #  + '     Per default, both private taxis (taxi1) and SAVs have a taxi mode share of 0.5
    #  + '     Change the SAV share in mode to 1.0 and the taxi1 share in mode to 0.0
    #  + '     if you want to ensure that only SAVs are used as backup
    #  + '     the sum of the share in mode parameter for taxi1 and SAV must be 1.0
    print(' ***************************************************************************************************\n')
    print(' c) The EXTERNAL DEMAND, however, is INCLUDED IN the analysis of the TOTAL TRAVELLED KM PER MODE.\n'
          + '     \n'
          + '    In the tripdata.xml,\n'
          + '    data of members of the virtual population (internal demand) is stored in personinfo nodes.\n'
          + '    Vehicle-related data is stored in tripinfo nodes.\n'
          + '    These tripinfo nodes exist for all vehicles,\n'
          + '    regardless if they represent internal or external demand.\n'
          + '    Therefore, if you want to analyze vehicle data which is independent of the persons\n'
          + '    using the vehicle, you need to analyze the tripinfo nodes.\n'
          + '    This is the case for all data related to traffic from the external demand\n'
          + '    (no virtual persons are riding in vehicles from the external demand)\n'
          + '    This also applies for all non-private vehicles, e.g., public transportation (PT), \n'
          + '    private taxis and shared-autonomous vehicles (SAVs).\n\n'
          + ' ***************************************************************************************************\n')
    print(' d) Info regarding (UN-)FINISHED TRIPs and STAGES:\n'
          + '     \n'
          + '    I)   This script has only been tested on 1-way-trips without return-trips.\n'
          + '         (e.g., morning rush hour simulation with every virt. person heading from home to work)\n'
          + '         It should be able to calculate most values accurately for return trips as well.\n'
          + '         (such as average speed, travel time or travelled distance)\n'
          + '         BUT: since the modal split is based on the mode of the first ride stage \n'
          + '         (or lack thereof) of a personinfo node,\n'
          + '         the modal split may not be accurate\n'
          + '         if the mode(s) selected for the outbound trip stages\n'
          + '         differ from the mode(s) of the return trip stages.\n'
          + '         (trips and stages explained below)\n'
          + '     \n'
          + '    II)  In general, 1 personinfo node = 1 trip by a virtual person\n'
          + '         A trip is always started if the start time of the trip lies\n'
          + '         between the start time end time of the simulation.\n'
          + '         Not-yet-started trips create no personinfo node and\n'
          + '         do not appear in the tripdata.xml file.\n'
          + '\n'
          + '    III) 1 trip may consist of several stages: walk, stop or ride.\n'
          + '         Each stage type may appear several times, once or not at all.\n'
          + '\n'
          + '    IV)  If any of the walk or ride stages include negative data (for times and distance),\n'
          + '         this means the stage, and therefore the whole trip, is unfinished.\n'
          + '         If all walk and ride stages have only positive data, that means the trip is finished\n'
          + '         and the destination has been reached before the end of the simulation.\n'
          + '         Stop stages represent planned stationary activities, e.g. being at work.\n'
          + '         Given that the standstill time during a stop stage is desired \n'
          + '         in order to complete the activity,\n'
          + '         stop stages do not influence any traffic KPIs calculated by this script.\n'
          + '         The aforementioned logic (trips only count as finished if all non-stop stages finished)\n'
          + '         means that if you have virtual persons with a ride or walk stages after a stop stage,\n'
          + '         if the stop stage has not reached its planned end-point before the end of the simulation.\n'
          + '         If the simulation is only conducted from, e.g., 6 to 8 a.m.,\n'
          + '         but the stop stage lasts until 10 a.m., and there are walk or ride stages afterwards,\n'
          + '         these walk or ride stages can never be finished, no matter how well traffic flows.\n'
          + '         The whole trip will be unfinished and no trip data can be analyzed.\n'
          + '         Therefore, one of the following options must be fulfilled\n'
          + '         in order for this script to analyze the trip data of the virtual population:\n'
          + '         - either have no stop stages (e.g. for 1-way rush hour simulations)\n'
          + '         - make sure the stop stage is the last stage\n'
          + '         - or be sure that all stop stages end well before the end of the simulation,\n'
          + '           so the virutal population has a chance to complete\n'
          + '           the other stages under the expected traffic\n'
          + '\n'
          + '    V)   Only already-started trips exist in the tripdata.xml file\n'
          + '         all trips by the virtual population (stored in personinfo nodes)\n'
          + '         in the tripdata.xml file\n'
          + '         are taken into account for calculating the modal split,\n'
          + '         both finished and unfinished trips.\n'
          + '\n'
          + '    VI) Unfinished trips are only taken into account for 2 purposes:\n'
          + '         - calculating the modal split\n'
          + '         - calculating the total kilometers driven per mode\n'
          + '         The unfinished trips must be taken into account for the total kilometers driven,\n'
          + '         since taxi and SAV are never finished.\n'
          + '         Since vehicles are removed from a SUMO simulation once their trip has finished,\n'
          + '         a taxi/SAV which has finished its trip would not be able to exist without passengers.\n'
          + '         Thanks to their never-finishing trips, taxis and SAVs can stay idle \n'
          + '         and move around the simulation without passengers by being assigned to them.\n'
          + '         The negative values inherent to unfinished trips are irrevelant to total km driven,\n'
          + '         since for vehicle data (stored in tripinfo nodes),\n'
          + '         the driven distance (routeLength) is always positive\n'
          + '         (others values may be negative).\n'
          + ' \n'
          + '    VII) Since unfinished virtual person trips have stages with negative trip data\n'
          + '         (e.g., negative arrival times),\n'
          + '         they are ignored for the calculation of all other KPIs\n'
          + '         in this tripdata analysis script.\n'
          + '         Given that the route length of walk stages lies within personinfo nodes,\n'
          + '         which only have negative data for unfinished stages,\n'
          + '         it is not possible to calculate the total km walked\n'
          + '         during all finished and unfinished walk stages.\n'
          + '         Therefore, only the total km driven by vehicles is calculated (see VI)\n'
          + '         Unfinished person trips are only relevant for the modal split.\n\n'
          + ' ***************************************************************************************************\n')
    print(' e) Suggestions for IMPROVING SIMULATIONS based on the KPIs determined by this script:\n'
          + '     \n'
          + '    In this script, the tripinfo nodes are analyzed to determine\n'
          + '    the total kilometers driven per mode of transport.\n'
          + '    This can be a useful KPI,\n'
          + '    especially when evaluating the efficiency of SAVs (shared autonomous vehicles).\n'
          + '    With SAVs, there almost certainly will be trips that SAVs must take with no passengers,\n'
          + '    e.g. while idling or while driving to a passenger pickup point while empty vehicle\n'
          + '    While having a very large fleet of SAVs might improve the door-door travel time KPI,\n'
          + '    adding too many SAVs may the increase total kilometers driven\n'
          + '    without significantly reducing travel times.\n'
          + '    To reduce operating costs and city traffic, the optimum number of SAVs\n'
          + '    may be found by determining the optimum combination of minimum door-door travel times \n'
          + '    and minimal kilometers driven in the SAV mode.\n'
          + '    Introducing an SAV service may reduce the total kilometers driven \n'
          + '    by private passenger vehicles (from the internal demand),\n'
          + '    but it may also reduce the mode share of walking or PT users.\n\n'
          + '    On the other hand,\n'
          + '    using only an SAV service instead of a private Taxi service could result\n'
          + '    in slower average travel times.\n'
          + '    However, since the capacity of private taxis (in SUMOPy)\n'
          + '    is set to only 1 passenger, wile the default for SUMOPy SAVs is 8 passengers, \n '
          + '    a private taxi fleet should have a much lower transportation\n'
          + '    than an SAV fleet with the same number of vehicles.\n'
          + '    The percentage of finished private taxi stages (calcuted in this script)\n'
          + '    may be much lower than the percentage of finish SAV stages.\n '
          + '    Therefore, while the average travel time KPIs are important, they are everything.\n '
          + '    A high percentage of SAV passengers arriving within a given timeframe\n'
          + '    with moderate travel times may be more desireable\n'
          + '    than a low percentage of private taxi passengers arriving quickly,\n'
          + '    while the rest do not arrive at all before the end of the simulation.\n'
          + '    These were just some thought experiments to show how the various KPIs in this script\n'
          + '    could be used and interpreted to improve simulations.\n'
          + '        \n'
          + '        \n')


def print_launch_options():
    print('')
    print('\n There are 2 ways this script can be run:')
    print(' - interactive mode (script asks user step-by-step what options they want')
    print(' - non-interactive mode (run script with all options selected via launch arguments')
    print(' The entire range of functions is available in both modes.')
    print('')
    print('\n To launch interactive mode: 2 launch arguments required:')
    print(' --------------------------------------------------------')
    print('\n Please provide the name of the file containing executed trip data')
    print(' as the 1st agument. Common names: tripdata.xml, tripinfo_output.xml \n')
    print(' Please provide the name of the file containing planned trip data')
    print(' as the 2nd agument. Common names: rou.xml, trips.xml, demand.xml\n')
    print(' No binding SUMO conventions exist, file names and file endings fully customizable.')
    print(' If in doubt, check xml file contents to be sure. Compare with the example files)')
    print(' of tripdata.xml and rou.xml included in this /scripts folder.\n')
    print(' Example launch command (tested with Python 3.7):')
    print(' python tripdata_analysis_without_sumopy_py3.py 230627_with_SAV_TAXI_PT.out.tripdata.xml 230627_with_SAV_TAXI_PT.rou.xml')
    print('\n\n To launch non-interactive mode: 1 to 3 additional launch arguments required:')
    print(' ---------------------------------------------------------')
    print('\n If you want to run this file as a non-interactive script,')
    print(' you must provide additional arguments at at launch.\n')
    print(' If you want to run the script with all print-outs, graphs, and exports, add:')
    print('         ni                ')
    print(' (ni = non-interactive) as an additional argument.\n')
    print(' If you want to run the script, but want to omit some print-outs, graphs')
    print(' or exports, instead of  ni , add 1 to 4 of the following arguments:')
    print('         nd                      (nd = no print-out of script description)')
    print('         np                      (np = no print-outs to console at all)')
    print('         nc                      (nc = no .csv file creation)')
    print('         ngs                     (ngs = no graphs shown, e.g. for systems with no GUI)')
    print('         nge                     (nge = no graphs exported)')
    print(' Note: you cannot combine these 4 arguments:  np nc ngs nge')
    print(' Doing so would have the script run uselessly without producing any kind of output.\n')
    print(' Example launch command to only create the tripdata_analyzed.csv file')
    print(' with no graphs and no console print-outs (tested with Python 3.7):')
    print(' python tripdata_analysis_without_sumopy_py3.py 230627_with_SAV_TAXI_PT.out.tripdata.xml 230627_with_SAV_TAXI_PT.rou.xml ngs np\n')
    print(' Tip: If you want save the printed console text to a .txt file, type')
    print('           > logfilename.txt')
    print('      after all the launch arguments.')
    print('      Since all console prints are saved to the .txt file,')
    print('      without printing anything to the console itself,')
    print('      this option is only recommended for non-interactive mode.')
    print('      (interactive mode would request user inputs without showing prompts)\n')


def print_data_analysis_steps():
    print('\n Data analysis steps:')
    print(' *********************\n')
    print(' Required:   1 Vehicle data analysis')
    print('             2 Virtual population data analysis')
    print('             3 Calculation of KPIs')
    print('             4 Displaying results on console\n')
    print(' Optional:   5 Exporting to tripdata_analyzed.csv')
    print('             6 Plotting graphs\n')
    print(' (KPI = key performance indicator, e.g., speed, travel time, etc.)\n')


def script_description_prompt(part1=False):
    var = input('\n Would you like to learn more about what this script calculates\n'
                + ' and how this script works?\n\n'
                + ' No:  just press enter...\n'
                + ' Yes: type y , then press enter...')
    while True:
        if var == '':
            break
        elif var == 'y':
            if part1 == True:
                print_script_description_part1()
            print_script_description_part2()
            break
        else:
            var = input('\n Invalid option entered.\n \n'
                        + ' Yes: type y , then press enter...\n'
                        + ' No:  just press enter...')


def launch_options_info_prompt():
    var = input('\n Would you like to learn more about the various launch options\n'
                + ' of how to run this script in non-interactive mode?\n\n'
                + ' No:  just press enter...\n'
                + ' Yes: type y , then press enter...')
    while True:
        if var == '':
            break
        elif var == 'y':
            print_launch_options()
            break
        else:
            var = input('\n Invalid option entered.\n \n'
                        + ' Yes: type y , then press enter...\n'
                        + ' No:  just press enter...')


def parse_xml_files():
    # Get the current working directory
    current_dir = os.getcwd()
    # Get the file name from the command line argument
    file_name_tripdata = sys.argv[1]
    file_name_rou = sys.argv[2]
    # Construct the file path relative to the current directory
    file_path_tripdata = os.path.join(current_dir, file_name_tripdata)
    file_path_rou = os.path.join(current_dir, file_name_rou)
    # Parse the XML file
    tree_tripdata = ET.parse(file_path_tripdata)
    tree_rou = ET.parse(file_path_rou)
    root_tripdata = tree_tripdata.getroot()
    root_rou = tree_rou.getroot()
    return current_dir, file_name_tripdata, file_name_rou, file_path_tripdata, \
        file_path_rou, tree_tripdata, tree_rou, root_tripdata, root_rou


###################################################################################################
# START OF MAIN SCRIPT

current_dir = None
file_name_tripdata = None
file_name_rou = None
file_path_tripdata = None
file_path_rou = None
tree_tripdata = None
tree_rou = None
root_tripdata = None
root_rou = None

# roots for parsing data
root_tripdata = None
root_rou = None

# launch-only options / arguments:
with_prints = True
with_description = True
# launch and interactive options / arguments:
with_csv = True
with_graphs_shown = True
with_graphs_exported = True

if len(sys.argv) < 3:
    print('\n\n            ERROR: not enough launch arguments')
    print('       ********************************************')
    launch_options_info_prompt()
    script_description_prompt(part1=True)
    print('\n Quitting analyis.\n')
    sys.exit(1)

elif len(sys.argv) > 7:
    print('\n\n            ERROR: too many launch arguments')
    print('       ******************************************')
    print('')
    launch_options_info_prompt()
    script_description_prompt(part1=True)
    print('\n Quitting analyis.\n')
    sys.exit(2)

else:

    # interactive mode
    if len(sys.argv) == 3:

        print_script_description_part1()
        print_data_analysis_steps()

        script_description_prompt()
        launch_options_info_prompt()

        # parse_xml_files() is executed next.
        #
        # Why not earlier?
        # --> To give the user a chance to read the script description and launch options
        #     before the script quits due to a wrong address / file name
        #
        # Why not later?
        # --> So the user is not confused into believing everything is ok.
        #     Continuing the interactive script any longer with wrong file names
        #     would be a waste of the users time and lead to frustration.

        current_dir, file_name_tripdata, file_name_rou, file_path_tripdata, \
            file_path_rou, tree_tripdata, tree_rou, root_tripdata, root_rou = parse_xml_files()

        var = input('\n\n Export KPI results to .csv ?\n'
                    + ' No csv:    type nc , then press enter...\n'
                    + ' With csv:  just press enter...')
        while True:
            if var == '':
                print(' Selected: with csv')
                with_csv = True
                break
            elif var == 'nc':
                print(' Selected: no csv')
                with_csv = False
                break
            else:
                var = input('\n Invalid option entered.\n \n'
                            + ' No csv:    type nc , then press enter...\n'
                            + ' With csv:  just press enter...')

        var = input('\n\n Show KPIs in graphs?     (indipendent of graph to png export)\n'
                    + ' No graphs shown:    type ngs , then press enter...\n'
                    + ' With graphs shown:  just press enter...')
        while True:
            if var == '':
                print(' Selected: with graphs shown')
                with_graphs_shown = True
                break
            elif var == 'ngs':
                print(' Selected: no graphs shown')
                with_graphs_shown = False
                break
            else:
                var = input('\n Invalid option entered.\n \n'
                            + ' No graphs shown:    type ngs , then press enter...\n'
                            + ' With graphs shown:  just press enter...')

        var = input('\n\n Export KPIs graphs to .png files?    (indipendent of showing graphs)\n'
                    + ' No graphs exported to png:    type nge , then press enter...\n'
                    + ' With graphs exported to png:  just press enter...')
        while True:
            if var == '':
                print(' Selected: with graphs exported to png')
                with_graphs_exported = True
                break
            elif var == 'nge':
                print(' Selected: no graphs exported to png')
                with_graphs_exported = False
                break
            else:
                var = input('\n Invalid option entered.\n \n'
                            + ' No graphs exported to png:    type nge , then press enter...\n'
                            + ' With graphs exported to png:  just press enter...')

    # non-interactive mode
    elif len(sys.argv) >= 4 and len(sys.argv) <= 7:

        # parse xml files as the first step as possible in non-interactive mode.
        #
        # Why?
        #
        # A user launching the script correctly in this mode likely has worked
        # enough with the script that they do not wish to read any script description.
        # If they do, they would probably launch in interactive mode.
        # If they make an error when launching in non-interactive mode, as a safety measure,
        # the script automatically asks them if they want to learn more
        # launch arguments and the script itself.

        current_dir, file_name_tripdata, file_name_rou, file_path_tripdata, \
            file_path_rou, tree_tripdata, tree_rou, root_tripdata, root_rou = parse_xml_files()

        n = 3
        while n < len(sys.argv):
            if sys.argv[n] == 'ni':
                pass
            elif sys.argv[n] == 'nd':
                with_description = False
            elif sys.argv[n] == 'np':
                if with_csv == False and with_graphs_shown == False and with_graphs_exported == False:
                    print(' \n Error! You cannot combine these 4 arguments:  np nc ngs nge')
                    print(' Doing so would have the script run uselessly without producing any kind of output.\n')
                    launch_options_info_prompt()
                    script_description_prompt()
                    print('\n Quitting analyis.\n')
                    sys.exit(3)
                else:
                    with_prints = False
            elif sys.argv[n] == 'nc':
                if with_prints == False and with_graphs_shown == False and with_graphs_exported == False:
                    print(' \n Error! You cannot combine these 4 arguments:  np nc ngs nge')
                    print(' Doing so would have the script run uselessly without producing any kind of output.\n')
                    launch_options_info_prompt()
                    script_description_prompt()
                    print('\n Quitting analyis.\n')
                    sys.exit(4)
                else:
                    with_csv = False
            elif sys.argv[n] == 'ngs':
                if with_prints == False and with_csv == False and with_graphs_exported == False:
                    print(' \n Error! You cannot combine these 4 arguments:  np nc ngs nge')
                    print(' Doing so would have the script run uselessly without producing any kind of output.\n')
                    launch_options_info_prompt()
                    script_description_prompt()
                    print('\n Quitting analyis.\n')
                    sys.exit(5)
                else:
                    with_graphs_shown = False
            elif sys.argv[n] == 'nge':
                if with_prints == False and with_csv == False and with_graphs_shown == False:
                    print(' \n Error! You cannot combine these 4 arguments:  np nc ngs nge')
                    print(' Doing so would have the script run uselessly without producing any kind of output.\n')
                    launch_options_info_prompt()
                    script_description_prompt()
                    print('\n Quitting analyis.\n')
                    sys.exit(6)
                else:
                    with_graphs_exported = False
            else:
                print(' Error! Invalid launch argument: ' + str(sys.argv[n]))
                launch_options_info_prompt()
                script_description_prompt()
                print('\n Quitting analyis.\n')
                sys.exit(7)
            n += 1

        if with_description == True and with_prints == True:
            print_script_description_part1()
            print_script_description_part2()
            print_launch_options()
            print_data_analysis_steps()


if with_prints:
    print('\n\n Preparing data analysis...')

# Store the mode shares in a dictionary
mode_shares = {}
mode_shares_percent = {}
mode_share_trips_NULL = None

# Find personinfo nodes with vehicle matching tripinfo IDs
personinfo_data_sav = []
personinfo_data_all = []
num_persontrips_all = 0
num_persontrips_SAV = 0
num_persontrips_walk = 0
total_number_of_finished_trips = 0
share_persontrips_finished = None
share_num_walk_stages_finished = None
shares_vehicle_stages_finished = {}  # Dictionary with the share of finished stages for each vehicle mode

# Dictionary with the (main) taxi/SAV/PT-line of each line-using virtual person (only 1 line saved per trip)
person_line_dict = {}
line_veh_dict = {}  # Dictionary with the vehicle type each line

primary_mode_counts = {}  # Dictionary to store vType counts
num_vType_stages_all = {}  # Dictionary to store total number of stages per vType across all virtual person trips
num_vType_stages_finished = {}  # Dictionary to store finished stage counts of each vType
num_ride_stage_veh_NULL = 0
num_trips_primary_mode_NULL = 0
vType_route_lengths = {}  # Dictionary to store routeLengths for each vType
vType_route_lengths_sum = 0  # Total routeLength for all vTypes
num_walk_stages_finished = 0  # Total count of finished walk items
num_walk_stages = 0  # Total count of walk items

share_finished_trips_per_mode = {}

walk_route_length_sum = 0  # Total route length of all FINISHED walk stages (unfinished walk stages: length = -1.0)

for person in root_rou.findall('.//person'):
    person_id = person.get('id')
    rides = person.findall('.//ride') or []
    # only checking the first ride for modal split analysis
    # (script assumes same vtype for all rides of a trip)
    if len(rides) > 0:
        line = rides[0].get('lines')
        if line != None:
            person_line_dict[person_id] = line

for flow in root_rou.findall('.//flow'):
    line = flow.get('line')
    if line != None:
        vtype = flow.get('type')
        line_veh_dict[line] = vtype

for vehicle in root_rou.findall('.//vehicle'):
    line = vehicle.get('line')
    if line != None:
        vtype = vehicle.get('type')
        line_veh_dict[line] = vtype

if with_prints:
    print('')
    print('')
    print(' Step 1: Starting analysis of vehicle data')
vehicle_count = len(root_tripdata.findall('.//tripinfo'))

veh_count_10_perc_limit = 10000
veh_count_1_perc_limit = 100000

if vehicle_count >= 10:
    vehicle_count_10_percent = round(vehicle_count/10)
    if vehicle_count >= 100:
        vehicle_count_1_percent = round(vehicle_count/100)

vehicle_counter = 0
vehicle_counter_old = 0
inverse_loading_print = False

if with_prints:
    if vehicle_count >= veh_count_1_perc_limit:
        print('            ', end='')

# To calculate the total travelled distance of all vehicles:
# Iterate over tripinfo nodes
for tripinfo in root_tripdata.findall('.//tripinfo'):
    if with_prints:
        if vehicle_count >= veh_count_10_perc_limit:
            if vehicle_counter == 1*vehicle_count_10_percent:
                print('  10 % of vehicle data analyzed')
                if vehicle_count >= veh_count_1_perc_limit:
                    print('            ', end='')
                    vehicle_counter_old = vehicle_counter
            elif vehicle_counter == 2*vehicle_count_10_percent:
                print('  20 % of vehicle data analyzed')
                if vehicle_count >= veh_count_1_perc_limit:
                    print('            ', end='')
                vehicle_counter_old = vehicle_counter
            elif vehicle_counter == 3*vehicle_count_10_percent:
                print('  30 % of vehicle data analyzed')
                if vehicle_count >= veh_count_1_perc_limit:
                    print('            ', end='')
                vehicle_counter_old = vehicle_counter
            elif vehicle_counter == 4*vehicle_count_10_percent:
                print('  40 % of vehicle data analyzed')
                if vehicle_count >= veh_count_1_perc_limit:
                    print('            ', end='')
                vehicle_counter_old = vehicle_counter
            elif vehicle_counter == 5*vehicle_count_10_percent:
                print('  50 % of vehicle data analyzed')
                if vehicle_count >= veh_count_1_perc_limit:
                    print('            ', end='')
                vehicle_counter_old = vehicle_counter
            elif vehicle_counter == 6*vehicle_count_10_percent:
                print('  60 % of vehicle data analyzed')
                if vehicle_count >= veh_count_1_perc_limit:
                    print('            ', end='')
                vehicle_counter_old = vehicle_counter
            elif vehicle_counter == 7*vehicle_count_10_percent:
                print('  70 % of vehicle data analyzed')
                if vehicle_count >= veh_count_1_perc_limit:
                    print('            ', end='')
                vehicle_counter_old = vehicle_counter
            elif vehicle_counter == 8*vehicle_count_10_percent:
                print('  80 % of vehicle data analyzed')
                if vehicle_count >= veh_count_1_perc_limit:
                    print('            ', end='')
                vehicle_counter_old = vehicle_counter
            elif vehicle_counter == 9*vehicle_count_10_percent:
                print('  90 % of vehicle data analyzed')
                if vehicle_count >= veh_count_1_perc_limit:
                    print('            ', end='')
                vehicle_counter_old = vehicle_counter
            elif vehicle_count >= veh_count_1_perc_limit:
                if inverse_loading_print == False:
                    if vehicle_counter-vehicle_counter_old == 1*vehicle_count_1_percent:
                        print('')
                    elif vehicle_counter-vehicle_counter_old == 2*vehicle_count_1_percent:
                        print('   l')
                    elif vehicle_counter-vehicle_counter_old == 3*vehicle_count_1_percent:
                        print('       o')
                    elif vehicle_counter-vehicle_counter_old == 4*vehicle_count_1_percent:
                        print('           a')
                    elif vehicle_counter-vehicle_counter_old == 5*vehicle_count_1_percent:
                        print('               d')
                    elif vehicle_counter-vehicle_counter_old == 6*vehicle_count_1_percent:
                        print('                   i')
                    elif vehicle_counter-vehicle_counter_old == 7*vehicle_count_1_percent:
                        print('                       n')
                    elif vehicle_counter-vehicle_counter_old == 8*vehicle_count_1_percent:
                        print('                           g')
                    elif vehicle_counter-vehicle_counter_old == 9*vehicle_count_1_percent:
                        print('')
                        inverse_loading_print = True
                else:
                    if vehicle_counter-vehicle_counter_old == 1*vehicle_count_1_percent:
                        print('')
                    elif vehicle_counter-vehicle_counter_old == 2*vehicle_count_1_percent:
                        print('                           l')
                    elif vehicle_counter-vehicle_counter_old == 3*vehicle_count_1_percent:
                        print('                       o')
                    elif vehicle_counter-vehicle_counter_old == 4*vehicle_count_1_percent:
                        print('                   a')
                    elif vehicle_counter-vehicle_counter_old == 5*vehicle_count_1_percent:
                        print('               d')
                    elif vehicle_counter-vehicle_counter_old == 6*vehicle_count_1_percent:
                        print('           i')
                    elif vehicle_counter-vehicle_counter_old == 7*vehicle_count_1_percent:
                        print('       n')
                    elif vehicle_counter-vehicle_counter_old == 8*vehicle_count_1_percent:
                        print('   g')
                    elif vehicle_counter-vehicle_counter_old == 9*vehicle_count_1_percent:
                        print('')
                        inverse_loading_print = False

    vType = tripinfo.get('vType')
    route_length = float(tripinfo.get('routeLength'))
    # Add routeLength to the corresponding vType
    if vType in vType_route_lengths:
        vType_route_lengths[vType] += route_length
    else:
        vType_route_lengths[vType] = route_length
    # Increment total routeLength
    vType_route_lengths_sum += route_length
    vehicle_counter += 1

if with_prints:
    print(' 100 % of vehicle data analyzed')
    print('')
    print('')
    print('')
    print(' Step 2: Starting analysis of virtual population data')
person_count = len(root_tripdata.findall('.//personinfo'))

person_count_10_perc_limit = 1000
person_count_1_perc_limit = 10000

if person_count >= person_count_10_perc_limit:
    person_count_10_percent = round(person_count/10)
    if person_count >= person_count_1_perc_limit:
        person_count_1_percent = round(person_count/100)

num_persontrips_all = 0
num_persontrips_all_old = 0
inverse_loading_print = False

if with_prints:
    if person_count >= person_count_1_perc_limit:
        print('            ', end='')

# Collect data from each member of the virtual population (internal demand)
for personinfo in root_tripdata.findall('.//personinfo'):
    if with_prints:
        if person_count >= person_count_10_perc_limit:
            if num_persontrips_all == 1*person_count_10_percent:
                print('  10 % of virtual population data analyzed')
                if person_count >= person_count_1_perc_limit:
                    print('            ', end='')
                    num_persontrips_all_old = num_persontrips_all
            elif num_persontrips_all == 2*person_count_10_percent:
                print('  20 % of virtual population data analyzed')
                if person_count >= person_count_1_perc_limit:
                    print('            ', end='')
                    num_persontrips_all_old = num_persontrips_all
            elif num_persontrips_all == 3*person_count_10_percent:
                print('  30 % of virtual population data analyzed')
                if person_count >= person_count_1_perc_limit:
                    print('            ', end='')
                    num_persontrips_all_old = num_persontrips_all
            elif num_persontrips_all == 4*person_count_10_percent:
                print('  40 % of virtual population data analyzed')
                if person_count >= person_count_1_perc_limit:
                    print('            ', end='')
                    num_persontrips_all_old = num_persontrips_all
            elif num_persontrips_all == 5*person_count_10_percent:
                print('  50 % of virtual population data analyzed')
                if person_count >= person_count_1_perc_limit:
                    print('            ', end='')
                    num_persontrips_all_old = num_persontrips_all
            elif num_persontrips_all == 6*person_count_10_percent:
                print('  60 % of virtual population data analyzed')
                if person_count >= person_count_1_perc_limit:
                    print('            ', end='')
                    num_persontrips_all_old = num_persontrips_all
            elif num_persontrips_all == 7*person_count_10_percent:
                print('  70 % of virtual population data analyzed')
                if person_count >= person_count_1_perc_limit:
                    print('            ', end='')
                    num_persontrips_all_old = num_persontrips_all
            elif num_persontrips_all == 8*person_count_10_percent:
                print('  80 % of virtual population data analyzed')
                if person_count >= person_count_1_perc_limit:
                    print('            ', end='')
                    num_persontrips_all_old = num_persontrips_all
            elif num_persontrips_all == 9*person_count_10_percent:
                print('  90 % of virtual population data analyzed')
                if person_count >= person_count_1_perc_limit:
                    print('            ', end='')
                    num_persontrips_all_old = num_persontrips_all
            elif person_count >= person_count_1_perc_limit:
                if inverse_loading_print == False:
                    if num_persontrips_all-num_persontrips_all_old == 1*person_count_1_percent:
                        print('')
                    elif num_persontrips_all-num_persontrips_all_old == 2*person_count_1_percent:
                        print('        l')
                    elif num_persontrips_all-num_persontrips_all_old == 3*person_count_1_percent:
                        print('            o')
                    elif num_persontrips_all-num_persontrips_all_old == 4*person_count_1_percent:
                        print('                a')
                    elif num_persontrips_all-num_persontrips_all_old == 5*person_count_1_percent:
                        print('                    d')
                    elif num_persontrips_all-num_persontrips_all_old == 6*person_count_1_percent:
                        print('                        i')
                    elif num_persontrips_all-num_persontrips_all_old == 7*person_count_1_percent:
                        print('                            n')
                    elif num_persontrips_all-num_persontrips_all_old == 8*person_count_1_percent:
                        print('                                g')
                    elif num_persontrips_all-num_persontrips_all_old == 9*person_count_1_percent:
                        print('')
                        inverse_loading_print = True
                else:
                    if num_persontrips_all-num_persontrips_all_old == 1*person_count_1_percent:
                        print('')
                    elif num_persontrips_all-num_persontrips_all_old == 2*person_count_1_percent:
                        print('                                l')
                    elif num_persontrips_all-num_persontrips_all_old == 3*person_count_1_percent:
                        print('                            o')
                    elif num_persontrips_all-num_persontrips_all_old == 4*person_count_1_percent:
                        print('                        a')
                    elif num_persontrips_all-num_persontrips_all_old == 5*person_count_1_percent:
                        print('                    d')
                    elif num_persontrips_all-num_persontrips_all_old == 6*person_count_1_percent:
                        print('                i')
                    elif num_persontrips_all-num_persontrips_all_old == 7*person_count_1_percent:
                        print('            n')
                    elif num_persontrips_all-num_persontrips_all_old == 8*person_count_1_percent:
                        print('        g')
                    elif num_persontrips_all-num_persontrips_all_old == 9*person_count_1_percent:
                        print('')
                        inverse_loading_print = False

    vehicle = personinfo.find('vehicle')
    personinfo_id = personinfo.get('id')

    # Get the ride or walk elements within personinfo
    rides = personinfo.findall('.//ride') or []
    walks = personinfo.findall('.//walk') or []

    travel_time_with_waiting = 0
    travel_time_without_waiting = 0
    ride_time_with_waiting = 0
    ride_time_without_waiting = 0
    route_Length_person = 0
    route_length_person_rides = 0

    is_walk = True
    is_trip_SAV = False
    trip_unfinished = False
    has_found_primary_vehicle_mode = False  # flag for entering an if statement in for loop only once

    primary_mode = 'walk'

    # Extract information from each walk
    for walk in walks:
        num_walk_stages += 1
        depart = float(walk.get('depart'))
        arrival = float(walk.get('arrival'))
        route_Length = float(walk.get('routeLength'))

        # Check for negative values
        if depart < 0 or arrival < 0 or route_Length < 0:
            trip_unfinished = True
        else:
            if 'walk' in primary_mode_counts:
                primary_mode_counts['walk'] += 1
            else:
                primary_mode_counts['walk'] = 1
            num_walk_stages_finished += 1
            travel_time_without_waiting += arrival-depart
            travel_time_with_waiting = travel_time_without_waiting  # walk stages have no waiting time
            route_Length_person += route_Length
            walk_route_length_sum += route_Length

    vehicles = []
    # Extract information from each ride
    for ride in rides:
        vType = None
        # Determine vType of ride stage
        vehicle_id = ride.get('vehicle')
        if vehicle_id != 'NULL':
            if vehicles.count(vehicle_id) < 1:
                vehicles.append(vehicle_id)
            # Find tripinfo node with matching ID
            tripinfo = root_tripdata.find('.//tripinfo[@id="' + vehicle_id + '"]')
            vType = tripinfo.get('vType')
        else:
            # sometimes a person can be waiting for a vehicle, and vehicle_id != NULL
            # But if vehicle_id == NULL, the person is definately waiting for a vehicle
            line = person_line_dict[personinfo_id]

            if list(line_veh_dict.keys()).count(line) > 0:
                vType = line_veh_dict[line]
            else:
                vType = 'unknown_vtype'
                num_ride_stage_veh_NULL += 1

        # For mode share: assign vType of the first ride stage of a person as the mode of the whole trip
        # (trip is walk if no ride stage exists)
        if has_found_primary_vehicle_mode == False:
            is_walk = False
            if vType in primary_mode_counts:
                primary_mode_counts[vType] += 1
            else:
                primary_mode_counts[vType] = 1
            has_found_primary_vehicle_mode = True
            primary_mode = vType

        # For data calculation wrt individual stages (e.g. completed stages)
        if vType in num_vType_stages_all:
            num_vType_stages_all[vType] += 1
        else:
            num_vType_stages_all[vType] = 1

        # get trip data
        waiting_time = float(ride.get('waitingTime'))
        depart = float(ride.get('depart'))
        arrival = float(ride.get('arrival'))
        route_Length = float(ride.get('routeLength'))

        # Calculate average data of trip (only trip is finished, i.e., if all stages are finished)
        # (unfinished stage has negative data)
        # (only if no stage has a negative value, will trip_unfinished flag still be True)
        if waiting_time < 0 or depart < 0 or arrival < 0 or route_Length < 0:
            trip_unfinished = True
        else:
            travel_time_with_waiting += waiting_time + (arrival-depart)
            travel_time_without_waiting += arrival-depart
            ride_time_with_waiting += waiting_time + (arrival-depart)
            ride_time_without_waiting += arrival-depart
            route_Length_person += route_Length
            route_length_person_rides += route_Length

            if vType in num_vType_stages_finished:
                num_vType_stages_finished[vType] += 1
            else:
                num_vType_stages_finished[vType] = 1

    if is_walk == True:
        num_persontrips_walk += 1

    # Append the personinfo data
    if trip_unfinished is False:
        total_number_of_finished_trips += 1
        personinfo_data_all.append((personinfo_id, travel_time_with_waiting, travel_time_without_waiting,
                                    ride_time_with_waiting, ride_time_without_waiting, route_Length_person,
                                    route_length_person_rides, trip_unfinished, primary_mode))
    else:
        personinfo_data_all.append((personinfo_id, None, None,
                                    None, None, None,
                                    None, trip_unfinished, primary_mode))
    num_persontrips_all += 1

if with_prints:
    print(' 100 % of virtual population data analyzed')

if num_persontrips_all < 1:
    print('\n\n ERROR')
    print(' No person info nodes found in file provided as first argument!')
    print(' Either the wrong file was given,')
    print(' or zero members of the virtual population started a trip during the simulation.')
    print('\n Quitting analyis.\n')
    launch_options_info_prompt()
    script_description_prompt()
    sys.exit(8)

if with_prints:
    print('')
    print('')
    print('')
    print(' Step 3: Calculation of KPIs')

# Calculate shares of finished trips and stages
num_persontrips_vehicle = num_persontrips_all - num_persontrips_walk
share_persontrips_finished = total_number_of_finished_trips/num_persontrips_all
share_num_walk_stages_finished = num_walk_stages_finished/num_walk_stages

# calcutlate the modal share of each mode of all trips and all finished trips (based on primary mode of each trip)
for vType in num_vType_stages_finished:
    shares_vehicle_stages_finished[vType] = num_vType_stages_finished[vType]/num_vType_stages_all[vType]
sum_of_vtype_stages = 0
sum_of_vtype_stages_finished = 0
for vType in shares_vehicle_stages_finished:
    sum_of_vtype_stages_finished += num_vType_stages_finished[vType]
    sum_of_vtype_stages += num_vType_stages_all[vType]


# Calculate average travel time for personinfo_data_all

trips_mode_all = {}
trips_mode_finished = {}
trips_mode_unfinished = {}
total_number_of_finished_ride_trips = 0


average_ride_time_with_waiting_all = -1
average_ride_time_without_waiting_all = -1
average_route_length_person_rides = -1
average_travel_time_with_waiting_all = -1
average_travel_time_without_waiting_all = -1
average_route_length_person_all = -1

average_speed_ms_with_waiting_rides = -1
average_speed_ms_without_waiting_rides = -1
average_speed_kmh_with_waiting_rides = -1
average_speed_kmh_without_waiting_rides = -1
average_speed_ms_with_waiting_all = -1
average_speed_ms_without_waiting_all = -1
average_speed_kmh_with_waiting_all = -1
average_speed_kmh_without_waiting_all = -1


average_ride_time_with_waiting_per_mode = {}
average_ride_time_without_waiting_per_mode = {}
average_route_length_person_rides_per_mode = {}
average_travel_time_with_waiting_per_mode = {}
average_travel_time_without_waiting_per_mode = {}
average_route_length_person_per_mode = {}

average_speed_ms_with_waiting_rides_per_mode = {}
average_speed_ms_without_waiting_rides_per_mode = {}
average_speed_kmh_with_waiting_rides_per_mode = {}
average_speed_kmh_without_waiting_rides_per_mode = {}
average_speed_ms_with_waiting_all_per_mode = {}
average_speed_ms_without_waiting_all_per_mode = {}
average_speed_kmh_with_waiting_all_per_mode = {}
average_speed_kmh_without_waiting_all_per_mode = {}


total_ride_time_with_waiting_all = 0
total_ride_time_without_waiting_all = 0
total_route_length_person_rides = 0
total_travel_time_with_waiting_all = 0
total_travel_time_without_waiting_all = 0
total_route_length_person_all = 0

total_ride_time_with_waiting_per_mode = {}
total_ride_time_without_waiting_per_mode = {}
total_route_length_person_rides_per_mode = {}
total_travel_time_with_waiting_per_mode = {}
total_travel_time_without_waiting_per_mode = {}
total_route_Length_person_per_mode = {}


for mode in primary_mode_counts:
    trips_mode_all[mode] = 0
    trips_mode_finished[mode] = 0
    trips_mode_unfinished[mode] = 0

    total_ride_time_with_waiting_per_mode[mode] = 0
    total_ride_time_without_waiting_per_mode[mode] = 0
    total_route_length_person_rides_per_mode[mode] = 0
    total_travel_time_with_waiting_per_mode[mode] = 0
    total_travel_time_without_waiting_per_mode[mode] = 0
    total_route_Length_person_per_mode[mode] = 0


for info in personinfo_data_all:
    personinfo_id, travel_time_with_waiting, travel_time_without_waiting, ride_time_with_waiting, \
        ride_time_without_waiting, route_Length_person, route_length_person_rides, trip_unfinished, primary_mode = info

    if trip_unfinished:
        trips_mode_unfinished[primary_mode] += 1
    else:
        if primary_mode != 'walk':
            total_number_of_finished_ride_trips += 1
            total_ride_time_with_waiting_all += ride_time_with_waiting
            total_ride_time_without_waiting_all += ride_time_without_waiting
            total_route_length_person_rides += route_length_person_rides
            total_ride_time_with_waiting_per_mode[primary_mode] += ride_time_with_waiting
            total_ride_time_without_waiting_per_mode[primary_mode] += ride_time_without_waiting
            total_route_length_person_rides_per_mode[primary_mode] += route_length_person_rides
        total_travel_time_with_waiting_all += travel_time_with_waiting
        total_travel_time_without_waiting_all += travel_time_without_waiting
        total_route_length_person_all += route_Length_person
        total_travel_time_with_waiting_per_mode[primary_mode] += travel_time_with_waiting
        total_travel_time_without_waiting_per_mode[primary_mode] += travel_time_without_waiting
        total_route_Length_person_per_mode[primary_mode] += route_Length_person

    trips_mode_all[primary_mode] += 1

# Calculate (average) data per mode:
for mode in primary_mode_counts:

    trips_mode_finished[mode] = trips_mode_all[mode] - trips_mode_unfinished[mode]
    if trips_mode_all[mode] < 1:
        share_finished_trips_per_mode[mode] = 0
    else:
        share_finished_trips_per_mode[mode] = (trips_mode_all[mode]-trips_mode_unfinished[mode])/trips_mode_all[mode]

    if trips_mode_finished[mode] > 0:

        if mode != 'walk':

            average_ride_time_with_waiting_per_mode[mode] = total_ride_time_with_waiting_per_mode[mode] / \
                trips_mode_finished[mode]
            average_ride_time_without_waiting_per_mode[mode] = total_ride_time_without_waiting_per_mode[mode] / \
                trips_mode_finished[mode]
            average_route_length_person_rides_per_mode[mode] = total_route_length_person_rides_per_mode[mode] / \
                trips_mode_finished[mode]

            average_speed_ms_with_waiting_rides_per_mode[mode] = average_route_length_person_rides_per_mode[mode] / \
                average_ride_time_with_waiting_per_mode[mode]
            average_speed_ms_without_waiting_rides_per_mode[mode] = average_route_length_person_rides_per_mode[mode] / \
                average_ride_time_without_waiting_per_mode[mode]
            average_speed_kmh_with_waiting_rides_per_mode[mode] = 3.6*average_speed_ms_with_waiting_rides_per_mode[mode]
            average_speed_kmh_without_waiting_rides_per_mode[mode] = 3.6 * \
                average_speed_ms_without_waiting_rides_per_mode[mode]

        average_travel_time_with_waiting_per_mode[mode] = total_travel_time_with_waiting_per_mode[mode] / \
            trips_mode_finished[mode]
        average_travel_time_without_waiting_per_mode[mode] = total_travel_time_without_waiting_per_mode[mode] / \
            trips_mode_finished[mode]
        average_route_length_person_per_mode[mode] = total_route_Length_person_per_mode[mode]/trips_mode_finished[mode]

        average_speed_ms_with_waiting_all_per_mode[mode] = average_route_length_person_per_mode[mode] / \
            average_travel_time_with_waiting_per_mode[mode]
        average_speed_ms_without_waiting_all_per_mode[mode] = average_route_length_person_per_mode[mode] / \
            average_travel_time_without_waiting_per_mode[mode]
        average_speed_kmh_with_waiting_all_per_mode[mode] = 3.6*average_speed_ms_with_waiting_all_per_mode[mode]
        average_speed_kmh_without_waiting_all_per_mode[mode] = 3.6*average_speed_ms_without_waiting_all_per_mode[mode]

    else:

        if mode != 'walk':

            average_ride_time_with_waiting_per_mode[mode] = -1
            average_ride_time_without_waiting_per_mode[mode] = -1
            average_route_length_person_rides_per_mode[mode] = -1

            average_speed_ms_with_waiting_rides_per_mode[mode] = -1
            average_speed_ms_without_waiting_rides_per_mode[mode] = -1
            average_speed_kmh_with_waiting_rides_per_mode[mode] = -1
            average_speed_kmh_without_waiting_rides_per_mode[mode] = -1

        average_travel_time_with_waiting_per_mode[mode] = -1
        average_travel_time_without_waiting_per_mode[mode] = -1
        average_route_length_person_per_mode[mode] = -1

        average_speed_ms_with_waiting_all_per_mode[mode] = -1
        average_speed_ms_without_waiting_all_per_mode[mode] = -1
        average_speed_kmh_with_waiting_all_per_mode[mode] = -1
        average_speed_kmh_without_waiting_all_per_mode[mode] = -1


# Calculate average data for the total population / for all persons:

if total_number_of_finished_trips > 0:

    if total_number_of_finished_ride_trips > 0:

        average_ride_time_with_waiting_all = total_ride_time_with_waiting_all / total_number_of_finished_ride_trips
        average_ride_time_without_waiting_all = total_ride_time_without_waiting_all / total_number_of_finished_ride_trips
        average_route_length_person_rides = total_route_length_person_rides / total_number_of_finished_ride_trips

        average_speed_ms_with_waiting_rides = average_route_length_person_rides/average_ride_time_with_waiting_all
        average_speed_ms_without_waiting_rides = average_route_length_person_rides/average_ride_time_without_waiting_all
        average_speed_kmh_with_waiting_rides = 3.6*average_speed_ms_with_waiting_rides
        average_speed_kmh_without_waiting_rides = 3.6*average_speed_ms_without_waiting_rides

    average_travel_time_with_waiting_all = total_travel_time_with_waiting_all / total_number_of_finished_trips
    average_travel_time_without_waiting_all = total_travel_time_without_waiting_all / total_number_of_finished_trips
    average_route_length_person_all = total_route_length_person_all / total_number_of_finished_trips

    average_speed_ms_with_waiting_all = average_route_length_person_all/average_travel_time_with_waiting_all
    average_speed_ms_without_waiting_all = average_route_length_person_all/average_travel_time_without_waiting_all
    average_speed_kmh_with_waiting_all = 3.6*average_speed_ms_with_waiting_all
    average_speed_kmh_without_waiting_all = 3.6*average_speed_ms_without_waiting_all


# Calculate the mode share of each vType
for vtype, count in primary_mode_counts.items():
    mode_share = float(count) / num_persontrips_all
    mode_shares[vtype] = mode_share
    mode_shares_percent[vtype] = 100 * mode_share

# Calculate the walking mode share
walking_mode_share = float(num_persontrips_walk) / num_persontrips_all
mode_shares["walk"] = walking_mode_share
mode_shares_percent["walk"] = 100 * walking_mode_share

# Sort the mode shares (by size)
sorted_mode_shares = {k: v for k, v in sorted(mode_shares_percent.items(), key=lambda item: item[0])}

if with_prints:
    print('')
    print('')
    print('')
    print(' Step 4: Displaying KPI results on console')
    print('')
    print('')

# Print the extracted information

###################################################################################################################################

# tripinfo-based section
# (internal AND external demand)

if with_prints:
    # Print the routeLength sums for each vType
    print(' Section 1: internal and external demand, finished and unfinished trips')
    print(' *************************************************************************************************')
    print(' *************************************************************************************************')
    print('')
    print(' Total distance travelled by vehicles')
    print(' *************************************************************************************************')
    print(' (sum of all route lengths in all tripinfo nodes)')
    print(' (internal + external demand combined, finished and unfinished vehicle trips)')
    print('')
    print(' SUM of ALL DISTANCES travelled BY ALL VEHICLES:                    {:20.3f} km'.format(
        vType_route_lengths_sum/1000))
    print(' Per vehicle type:')
    for vType, route_length_sum in vType_route_lengths.items():
        print('    {:<15}                                                 {:20.3f} km'.format(vType, route_length_sum/1000))
    print('')
    print('')
    print('')
    print('')

# tripinfo-based section
# (internal AND external demand)

###################################################################################################################################

# personinfo-based section
# (internal demand caused by virtualpopulation)
    print(' Section 2: only internal demand (generated by virutal population), finished and unfinished trips')
    print(' *************************************************************************************************')
    print(' *************************************************************************************************')
    print('')
    print(' Finished and unfinished trips of virtual population')
    print(' *************************************************************************************************')
    print(' (if any value == -1: no finished trips for this mode)')
    print('                                              ')
    print(' NUMBER of STARTED TRIPS of virtual population:                            {:9.0f} trips'.format(
        num_persontrips_all))
    print(' NUMBER of FINISHED TRIPS of virtual population:                           {:9.0f} trips'.format(
        total_number_of_finished_trips))
    print('')
    print(' Total PERCENTAGE of FINISHED TRIPS of virtual population:                     {:9.3f} %'.format(
        share_persontrips_finished*100))
    print(' Per primary mode of travel:')
    for mode in share_finished_trips_per_mode:
        print('    {:<15}                                                            {:9.3f} %'.format(
            mode, share_finished_trips_per_mode[mode]*100))
    print('')
    print('')
    print(' MODAL SPLIT of all started trips of virtual population')
    print(' *************************************************************************************************')
    print(' (including finished and unfinished person trips based on vtype of first ride stage)')
    print(' (mode = walk if no ride stages in trip)')
    print(' (see section b) at the start of the script for calculation method)')
    print('')
    for vType, mode_share in sorted_mode_shares.items():
        print('    {:<15}                                                            {:9.3f} %'.format(vType, mode_share))
    print('')
    print('')
    print('')
    print('')
    print(' Section 3: only internal demand (generated by virutal population), only finished trips')
    print(' *************************************************************************************************')
    print(' *************************************************************************************************')
    print('')
    if total_number_of_finished_trips < 1:
        print('\n *************************************************************************************************')
        print(' *   WARNING!                                                                                    *')
        print(' *   No finished trips in data. Therefore, no average speed, travel time, etc. available!        *')
        print(' *   Uncalculable KPIs have value = -1 in csv export file.                                       *')
        print(' *************************************************************************************************')
    else:
        if total_number_of_finished_ride_trips < 1:
            print('\n *************************************************************************************************')
            print(' *   WARNING!                                                                                    *')
            print(' *   No finished trips with ride stages in data.                                                 *')
            print(' *   Therefore, no average speed, travel time, etc. for vehicle users available!                 *')
            print(' *   Uncalculable KPIs have value = -1 in csv export file.                                       *')
            print(' *************************************************************************************************\n\n')

        print(' Average trip data of internal demand (based on finished trips)')
        print(' *************************************************************************************************')
        print(' (if any value == -1: no finished trips for this mode <--> no data for this KPI for this mode)')
        print('')
        print(' Average DOOR-DOOR travel TIME WITH WALK stages INCLUDING WAITING times:      {:9.2f} seconds'.format(
            average_travel_time_with_waiting_all))
        print(' Per primary mode of travel:')
        for mode in average_travel_time_with_waiting_per_mode:
            print('    {:<15}                                                           {:9.2f} seconds'.format(
                mode, average_travel_time_with_waiting_per_mode[mode]))
        print('')
        print('')
        if total_number_of_finished_ride_trips > 0:
            print(' Average RIDE TIME WITHOUT WALK stages INCLUDING WAITING times:               {:9.2f} seconds'.format(
                average_ride_time_with_waiting_all))
            print(' Per primary mode of travel:')
            for mode in average_ride_time_with_waiting_per_mode:
                print('    {:<15}                                                           {:9.2f} seconds'.format(
                    mode, average_ride_time_with_waiting_per_mode[mode]))
            print('')
            print('')
        print(' Average DOOR-DOOR travel TIME WITH WALK stages EXCLUDING WAITING times:      {:9.2f} seconds'.format(
            average_travel_time_without_waiting_all))
        print(' Per primary mode of travel:')
        for mode in average_travel_time_without_waiting_per_mode:
            print('    {:<15}                                                           {:9.2f} seconds'.format(
                mode, average_travel_time_without_waiting_per_mode[mode]))
        print('')
        print('')
        if total_number_of_finished_ride_trips > 0:
            print(' Average RIDE TIME WITHOUT WALK stages EXCLUDING WAITING times:               {:9.2f} seconds'.format(
                average_ride_time_without_waiting_all))
            print(' Per primary mode of travel:')
            for mode in average_ride_time_without_waiting_per_mode:
                print('    {:<15}                                                           {:9.2f} seconds'.format(
                    mode, average_ride_time_without_waiting_per_mode[mode]))
            print('')
            print('')
        print(' Average DOOR-DOOR travel DISTANCE:                                           {:9.2f} meters'.format(
            average_route_length_person_all))
        print(' Per primary mode of travel:')
        for mode in average_route_length_person_per_mode:
            print('    {:<15}                                                           {:9.2f} meters'.format(
                mode, average_route_length_person_per_mode[mode]))
        print('')
        print('')
        if total_number_of_finished_ride_trips > 0:
            print(' Average RIDE DISTANCE WITHOUT WALK stages:                                   {:9.2f} meters'.format(
                average_route_length_person_rides))
            print(' Per primary mode of travel:')
            for mode in average_route_length_person_rides_per_mode:
                print('    {:<15}                                                           {:9.2f} meters'.format(
                    mode, average_route_length_person_rides_per_mode[mode]))
            print('')
            print('')
        print(' Average DOOR-DOOR travel SPEED m/s WITH WALK stages INCLUDING WAITING times: {:9.2f} m/s'.format(
            average_speed_ms_with_waiting_all))
        print(' Per primary mode of travel:')
        for mode in average_speed_ms_with_waiting_all_per_mode:
            print('    {:<15}                                                           {:9.2f} m/s'.format(mode,
                  average_speed_ms_with_waiting_all_per_mode[mode]))
        print('')
        print('')
        if total_number_of_finished_ride_trips > 0:
            print(' Average RIDE SPEED m/s WITHOUT WALK stages INCLUDING WAITING times:          {:9.2f} m/s'.format(
                average_speed_ms_with_waiting_rides))
            print(' Per primary mode of travel:')
            for mode in average_speed_ms_with_waiting_rides_per_mode:
                print('    {:<15}                                                           {:9.2f} m/s'.format(mode,
                      average_speed_ms_with_waiting_rides_per_mode[mode]))
            print('')
            print('')
        print(' Average DOOR-DOOR travel SPEED m/s WITH WALK stages EXCLUDING WAITING times: {:9.2f} m/s'.format(
            average_speed_ms_without_waiting_all))
        print(' Per primary mode of travel:')
        for mode in average_speed_ms_without_waiting_all_per_mode:
            print('    {:<15}                                                           {:9.2f} m/s'.format(mode,
                  average_speed_ms_without_waiting_all_per_mode[mode]))
        print('')
        print('')
        if total_number_of_finished_ride_trips > 0:
            print(' Average RIDE SPEED m/s WITHOUT WALK stages EXCLUDING WAITING times:          {:9.2f} m/s'.format(
                average_speed_ms_without_waiting_rides))
            print(' Per primary mode of travel:')
            for mode in average_speed_ms_without_waiting_rides_per_mode:
                print('    {:<15}                                                           {:9.2f} m/s'.format(mode,
                      average_speed_ms_without_waiting_rides_per_mode[mode]))
            print('')
            print('')
        print(' Average DOOR-DOOR travel SPEED km/h WITH WALK stages INCLUDING WAITING times:{:9.2f} km/h'.format(
            average_speed_kmh_with_waiting_all))
        print(' Per primary mode of travel:')
        for mode in average_speed_kmh_with_waiting_all_per_mode:
            print('    {:<15}                                                           {:9.2f} km/h'.format(mode,
                  average_speed_kmh_with_waiting_all_per_mode[mode]))
        print('')
        print('')
        if total_number_of_finished_ride_trips > 0:
            print(' Average RIDE SPEED km/h WITHOUT WALK stages INCLUDING WAITING times:         {:9.2f} km/h'.format(
                average_speed_kmh_with_waiting_rides))
            print(' Per primary mode of travel:')
            for mode in average_speed_kmh_with_waiting_rides_per_mode:
                print('    {:<15}                                                           {:9.2f} km/h'.format(mode,
                      average_speed_kmh_with_waiting_rides_per_mode[mode]))
            print('')
            print('')
        print(' Average DOOR-DOOR travel SPEED km/h WITH WALK stages EXCLUDING WAITING times:{:9.2f} km/h'.format(
            average_speed_kmh_without_waiting_all))
        print(' Per primary mode of travel:')
        for mode in average_speed_kmh_without_waiting_all_per_mode:
            print('    {:<15}                                                           {:9.2f} km/h'.format(mode,
                  average_speed_kmh_without_waiting_all_per_mode[mode]))
        print('')
        print('')
        if total_number_of_finished_ride_trips > 0:
            print(' Average RIDE SPEED km/h WITHOUT WALK stages EXCLUDING WAITING times:         {:9.2f} km/h'.format(
                average_speed_kmh_without_waiting_rides))
            print(' Per primary mode of travel:')
            for mode in average_speed_kmh_without_waiting_rides_per_mode:
                print('    {:<15}                                                           {:9.2f} km/h'.format(mode,
                      average_speed_kmh_without_waiting_rides_per_mode[mode]))
    print('')

# personinfo-based section
# (internal demand caused by virtualpopulation)

###################################################################################################################################

# CSV CREATION and EXPORT

if with_prints:
    print('')
    print('')
    print('')

if with_csv is False:
    if with_prints:
        print(' Step 5: Exporting to .csv deactivated by user')
else:
    if with_prints:
        print(' Step 5: Exporting KPI results to tripdata_analyzed.csv')

    # Create a list of dictionaries containing the variable names and values
    data = [
        {'Variable': 'num_persontrips_all', 'Value': num_persontrips_all},
        {'Variable': 'total_number_of_finished_trips', 'Value': total_number_of_finished_trips},
        {'Variable': 'share_persontrips_finished', 'Value': share_persontrips_finished},
        {'Variable': 'share_finished_trips_per_mode', 'Value': share_finished_trips_per_mode},
        {'Variable': 'mode_shares', 'Value': mode_shares},
        {'Variable': 'vType_route_lengths_sum_in_meters', 'Value': vType_route_lengths_sum},
        {'Variable': 'vType_route_lengths_in_meters', 'Value': vType_route_lengths},
        {'Variable': 'average_travel_time_with_waiting_all_door_door', 'Value': average_travel_time_with_waiting_all},
        {'Variable': 'average_travel_time_with_waiting_per_mode_door_door',
            'Value': average_travel_time_with_waiting_per_mode},
        {'Variable': 'average_travel_time_without_waiting_all_door_door', 'Value': average_travel_time_without_waiting_all},
        {'Variable': 'average_travel_time_without_waiting_per_mode_door_door',
            'Value': average_travel_time_without_waiting_per_mode},
        {'Variable': 'average_ride_time_with_waiting_all', 'Value': average_ride_time_with_waiting_all},
        {'Variable': 'average_ride_time_with_waiting_per_mode', 'Value': average_ride_time_with_waiting_per_mode},
        {'Variable': 'average_ride_time_without_waiting_all', 'Value': average_ride_time_without_waiting_all},
        {'Variable': 'average_ride_time_without_waiting_per_mode', 'Value': average_ride_time_without_waiting_per_mode},
        {'Variable': 'average_route_length_person_all_in_meters_door_door', 'Value': average_route_length_person_all},
        {'Variable': 'average_route_length_person_per_mode_in_meters_door_door', 'Value': average_route_length_person_per_mode},
        {'Variable': 'average_route_length_person_rides_in_meters', 'Value': average_route_length_person_rides},
        {'Variable': 'average_route_length_person_rides_per_mode_in_meters',
            'Value': average_route_length_person_rides_per_mode},
        {'Variable': 'average_speed_kmh_with_waiting_all_door_door', 'Value': average_speed_kmh_with_waiting_all},
        {'Variable': 'average_speed_kmh_with_waiting_all_per_mode_door_door',
            'Value': average_speed_kmh_with_waiting_all_per_mode},
        {'Variable': 'average_speed_kmh_without_waiting_all_door_door', 'Value': average_speed_kmh_without_waiting_all},
        {'Variable': 'average_speed_kmh_without_waiting_all_per_mode_door_door',
            'Value': average_speed_kmh_without_waiting_all_per_mode},
        {'Variable': 'average_speed_kmh_with_waiting_rides', 'Value': average_speed_kmh_with_waiting_rides},
        {'Variable': 'average_speed_kmh_with_waiting_rides_per_mode',
            'Value': average_speed_kmh_with_waiting_rides_per_mode},
        {'Variable': 'average_speed_kmh_without_waiting_rides', 'Value': average_speed_kmh_without_waiting_rides},
        {'Variable': 'average_speed_kmh_without_waiting_rides_per_mode',
            'Value': average_speed_kmh_without_waiting_rides_per_mode},
        {'Variable': 'average_speed_ms_with_waiting_all_door_door', 'Value': average_speed_ms_with_waiting_all},
        {'Variable': 'average_speed_ms_with_waiting_all_per_mode_door_door',
            'Value': average_speed_ms_with_waiting_all_per_mode},
        {'Variable': 'average_speed_ms_with_waiting_rides_door_door', 'Value': average_speed_ms_with_waiting_rides},
        {'Variable': 'average_speed_ms_with_waiting_rides_per_mode_door_door',
            'Value': average_speed_ms_with_waiting_rides_per_mode},
        {'Variable': 'average_speed_ms_without_waiting_all', 'Value': average_speed_ms_without_waiting_all},
        {'Variable': 'average_speed_ms_without_waiting_all_per_mode',
            'Value': average_speed_ms_without_waiting_all_per_mode},
        {'Variable': 'average_speed_ms_without_waiting_rides', 'Value': average_speed_ms_without_waiting_rides},
        {'Variable': 'average_speed_ms_without_waiting_rides_per_mode',
            'Value': average_speed_ms_without_waiting_rides_per_mode}
    ]

    # Define the filename for the CSV file
    filename = 'tripdata_analyzed.csv'

    # Open the file in write mode
    with open(filename, 'w', newline='') as csvfile:
        # Create a CSV writer node
        writer = csv.writer(csvfile)

        # Write the header row
        writer.writerow(['Variable', 'Value'])

        # Write the data rows
        for item in data:
            writer.writerow([item['Variable'], item['Value']])


# CSV CREATION and EXPORT

###################################################################################################################################

# graph plotting

if with_prints:
    print('')
    print('')
    print('')
if with_graphs_shown is False and with_graphs_exported is False:
    if with_prints:
        print(' Step 6: Plotting and exporting graphs deactivated by user')
else:
    if with_prints:
        if with_graphs_shown is False:
            print(' Step 6: Plotting and exporting graphs of KPI results to .png files')
        elif with_graphs_exported is False:
            print(' Step 6: Plotting and displaying graphs of KPI results')
        else:
            print(' Step 6: Plotting and exporting graphs of KPI results to .png files, then displaying graphs')

    if total_number_of_finished_trips > 0:

        if total_number_of_finished_ride_trips > 0:

            # Plot the next set of variables
            x3 = []
            x3.append('total\n average')
            x3.append('')
            for mode in average_ride_time_with_waiting_per_mode:
                x3.append(mode)
            y3 = []
            y3.append(average_ride_time_with_waiting_all)
            y3.append(0)
            for mode in average_ride_time_with_waiting_per_mode:
                y3.append(average_ride_time_with_waiting_per_mode[mode])
            plt.figure(3)
            plt.bar(range(len(x3)), y3, align='center')
            plt.xticks(range(len(x3)), x3)
            plt.ylabel('time in seconds')
            plt.title('RIDE TIME WITHOUT WALK stages INCLUDING WAITING times\n'
                      + '(only internal demand, only finished trips)')
            plt.tight_layout()
            if with_graphs_exported:
                plt.savefig('average_ride_time_with_waiting.png', dpi=300)

            # Plot the next set of variables
            x4 = []
            x4.append('total\n average')
            x4.append('')
            for mode in average_ride_time_without_waiting_per_mode:
                x4.append(mode)
            y4 = []
            y4.append(average_ride_time_without_waiting_all)
            y4.append(0)
            for mode in average_ride_time_without_waiting_per_mode:
                y4.append(average_ride_time_without_waiting_per_mode[mode])
            plt.figure(4)
            plt.bar(range(len(x4)), y4, align='center')
            plt.xticks(range(len(x4)), x4)
            plt.xlabel('')
            plt.ylabel('time in seconds')
            plt.title('RIDE TIME WITHOUT WALK stages EXCLUDING WAITING times\n'
                      + '(only internal demand, only finished trips)')
            plt.tight_layout()
            if with_graphs_exported:
                plt.savefig('average_ride_time_without_waiting.png', dpi=300)

            # Plot the next set of variables
            x5 = []
            x5.append('total\n average')
            x5.append('')
            for mode in average_route_length_person_rides_per_mode:
                x5.append(mode)
            y5 = []
            y5.append(average_route_length_person_rides)
            y5.append(0)
            for mode in average_route_length_person_rides_per_mode:
                y5.append(average_route_length_person_rides_per_mode[mode])
            plt.figure(5)
            plt.bar(range(len(x5)), y5, align='center')
            plt.xticks(range(len(x5)), x5)
            plt.xlabel('')
            plt.ylabel('distance in meters')
            plt.title('RIDE DISTANCE WITHOUT WALK stages\n'
                      + '(only internal demand, only finished trips)')
            plt.tight_layout()
            if with_graphs_exported:
                plt.savefig('average_route_length_person_rides.png', dpi=300)

            # Plot the next set of variables
            x9 = []
            x9.append('total\n average')
            x9.append('')
            for mode in average_speed_ms_with_waiting_rides_per_mode:
                x9.append(mode)
            y9 = []
            y9.append(average_speed_ms_with_waiting_rides)
            y9.append(0)
            for mode in average_speed_ms_with_waiting_rides_per_mode:
                y9.append(average_speed_ms_with_waiting_rides_per_mode[mode])
            plt.figure(9)
            plt.bar(range(len(x9)), y9, align='center')
            plt.xticks(range(len(x9)), x9)
            plt.xlabel('')
            plt.ylabel('speed in m/s')
            plt.title('RIDE SPEED WITHOUT WALK stages INCLUDING WAITING times\n'
                      + '(only internal demand, only finished trips)')
            plt.tight_layout()
            if with_graphs_exported:
                plt.savefig('average_speed_ms_with_waiting_rides.png', dpi=300)

            # Plot the next set of variables
            x10 = []
            x10.append('total\n average')
            x10.append('')
            for mode in average_speed_ms_without_waiting_rides_per_mode:
                x10.append(mode)
            y10 = []
            y10.append(average_speed_ms_without_waiting_rides)
            y10.append(0)
            for mode in average_speed_ms_without_waiting_rides_per_mode:
                y10.append(average_speed_ms_without_waiting_rides_per_mode[mode])
            plt.figure(10)
            plt.bar(range(len(x10)), y10, align='center')
            plt.xticks(range(len(x10)), x10)
            plt.xlabel('')
            plt.ylabel('speed in m/s')
            plt.title('RIDE SPEED WITHOUT WALK stages EXCLUDING WAITING times\n'
                      + '(only internal demand, only finished trips)')
            plt.tight_layout()
            if with_graphs_exported:
                plt.savefig('average_speed_ms_without_waiting_rides.png', dpi=300)

            # Plot the next set of variables
            x11 = []
            x11.append('total\n average')
            x11.append('')
            for mode in average_speed_kmh_with_waiting_rides_per_mode:
                x11.append(mode)
            y11 = []
            y11.append(average_speed_kmh_with_waiting_rides)
            y11.append(0)
            for mode in average_speed_kmh_with_waiting_rides_per_mode:
                y11.append(average_speed_kmh_with_waiting_rides_per_mode[mode])
            plt.figure(11)
            plt.bar(range(len(x11)), y11, align='center')
            plt.xticks(range(len(x11)), x11)
            plt.xlabel('')
            plt.ylabel('speed in km/h')
            plt.title('RIDE SPEED WITHOUT WALK stages INCLUDING WAITING times\n'
                      + '(only internal demand, only finished trips)')
            plt.tight_layout()
            if with_graphs_exported:
                plt.savefig('average_speed_kmh_with_waiting_rides.png', dpi=300)

            # Plot the next set of variables
            x12 = []
            x12.append('total\n average')
            x12.append('')
            for mode in average_speed_kmh_without_waiting_rides_per_mode:
                x12.append(mode)
            y12 = []
            y12.append(average_speed_kmh_without_waiting_rides)
            y12.append(0)
            for mode in average_speed_kmh_without_waiting_rides_per_mode:
                y12.append(average_speed_kmh_without_waiting_rides_per_mode[mode])
            plt.figure(12)
            plt.bar(range(len(x12)), y12, align='center')
            plt.xticks(range(len(x12)), x12)
            plt.xlabel('')
            plt.ylabel('speed in km/h')
            plt.title('RIDE SPEED WITHOUT WALK stages EXCLUDING WAITING times\n'
                      + '(only internal demand, only finished trips)')
            plt.tight_layout()
            if with_graphs_exported:
                plt.savefig('average_speed_kmh_without_waiting_rides.png', dpi=300)

        # Plot the next set of variables
        x6 = []
        x6.append('total\n average')
        x6.append('')
        for mode in average_route_length_person_per_mode:
            x6.append(mode)
        y6 = []
        y6.append(average_route_length_person_all)
        y6.append(0)
        for mode in average_route_length_person_per_mode:
            y6.append(average_route_length_person_per_mode[mode])
        plt.figure(6)
        plt.bar(range(len(x6)), y6, align='center')
        plt.xticks(range(len(x6)), x6)
        plt.xlabel('')
        plt.ylabel('distance in meters')
        plt.title('DOOR-DOOR travel DISTANCE\n'
                  + '(only internal demand, only finished trips)')
        plt.tight_layout()
        if with_graphs_exported:
            plt.savefig('average_route_length_person_door_door.png', dpi=300)

        # Plot the next set of variables
        x7 = []
        x7.append('total\n average')
        x7.append('')
        for mode in average_travel_time_with_waiting_per_mode:
            x7.append(mode)
        y7 = []
        y7.append(average_travel_time_with_waiting_all)
        y7.append(0)
        for mode in average_travel_time_with_waiting_per_mode:
            y7.append(average_travel_time_with_waiting_per_mode[mode])
        plt.figure(7)
        plt.bar(range(len(x7)), y7, align='center')
        plt.xticks(range(len(x7)), x7)
        plt.xlabel('')
        plt.ylabel('time in seconds')
        plt.title('DOOR-DOOR TIME WITH WALK stages INCLUDING WAITING times\n'
                  + '(only internal demand, only finished trips)')
        plt.tight_layout()
        if with_graphs_exported:
            plt.savefig('average_travel_time_with_waiting_all_door_door.png', dpi=300)

        # Plot the next set of variables
        x8 = []
        x8.append('total\n average')
        x8.append('')
        for mode in average_travel_time_without_waiting_per_mode:
            x8.append(mode)
        y8 = []
        y8.append(average_travel_time_without_waiting_all)
        y8.append(0)
        for mode in average_travel_time_without_waiting_per_mode:
            y8.append(average_travel_time_without_waiting_per_mode[mode])
        plt.figure(8)
        plt.bar(range(len(x8)), y8, align='center')
        plt.xticks(range(len(x8)), x8)
        plt.xlabel('')
        plt.ylabel('time in seconds')
        plt.title('DOOR-DOOR TIME WITH WALK stages EXCLUDING WAITING times\n'
                  + '(only internal demand, only finished trips)')
        plt.tight_layout()
        if with_graphs_exported:
            plt.savefig('average_travel_time_without_waiting_all_door_door.png', dpi=300)

        # Plot the next set of variables
        x13 = []
        x13.append('total\n average')
        x13.append('')
        for mode in average_speed_ms_with_waiting_all_per_mode:
            x13.append(mode)
        y13 = []
        y13.append(average_speed_ms_with_waiting_all)
        y13.append(0)
        for mode in average_speed_ms_with_waiting_all_per_mode:
            y13.append(average_speed_ms_with_waiting_all_per_mode[mode])
        plt.figure(13)
        plt.bar(range(len(x13)), y13, align='center')
        plt.xticks(range(len(x13)), x13)
        plt.xlabel('')
        plt.ylabel('speed in m/s')
        plt.title('DOOR-DOOR SPEED WITH WALK stages INCLUDING WAITING times\n'
                  + '(only internal demand, only finished trips)')
        plt.tight_layout()
        if with_graphs_exported:
            plt.savefig('average_speed_ms_with_waiting_all_door_door.png', dpi=300)

        # Plot the next set of variables
        x14 = []
        x14.append('total\n average')
        x14.append('')
        for mode in average_speed_ms_without_waiting_all_per_mode:
            x14.append(mode)
        y14 = []
        y14.append(average_speed_ms_without_waiting_all)
        y14.append(0)
        for mode in average_speed_ms_without_waiting_all_per_mode:
            y14.append(average_speed_ms_without_waiting_all_per_mode[mode])
        plt.figure(14)
        plt.bar(range(len(x14)), y14, align='center')
        plt.xticks(range(len(x14)), x14)
        plt.xlabel('')
        plt.ylabel('speed in m/s')
        plt.title('DOOR-DOOR SPEED WITH WALK stages EXCLUDING WAITING times\n'
                  + '(only internal demand, only finished trips)')
        plt.tight_layout()
        if with_graphs_exported:
            plt.savefig('average_speed_ms_without_waiting_all_door_door.png', dpi=300)

        # Plot the next set of variables
        x15 = []
        x15.append('total\n average')
        x15.append('')
        for mode in average_speed_kmh_with_waiting_all_per_mode:
            x15.append(mode)
        y15 = []
        y15.append(average_speed_kmh_with_waiting_all)
        y15.append(0)
        for mode in average_speed_kmh_with_waiting_all_per_mode:
            y15.append(average_speed_kmh_with_waiting_all_per_mode[mode])
        plt.figure(15)
        plt.bar(range(len(x15)), y15, align='center')
        plt.xticks(range(len(x15)), x15)
        plt.xlabel('')
        plt.ylabel('speed in km/h')
        plt.title('DOOR-DOOR SPEED WITH WALK stages INCLUDING WAITING times\n'
                  + '(only internal demand, only finished trips)')
        plt.tight_layout()
        if with_graphs_exported:
            plt.savefig('average_speed_kmh_with_waiting_all_door_door.png', dpi=300)

        # Plot the next set of variables
        x16 = []
        x16.append('total\n average')
        x16.append('')
        for mode in average_speed_kmh_without_waiting_all_per_mode:
            x16.append(mode)
        y16 = []
        y16.append(average_speed_kmh_without_waiting_all)
        y16.append(0)
        for mode in average_speed_kmh_without_waiting_all_per_mode:
            y16.append(average_speed_kmh_without_waiting_all_per_mode[mode])
        plt.figure(16)
        plt.bar(range(len(x16)), y16, align='center')
        plt.xticks(range(len(x16)), x16)
        plt.xlabel('')
        plt.ylabel('speed in km/h')
        plt.title('DOOR-DOOR SPEED WITH WALK stages EXCLUDING WAITING times\n'
                  + '(only internal demand, only finished trips)')
        plt.tight_layout()
        if with_graphs_exported:
            plt.savefig('average_speed_kmh_without_waiting_all_door_door.png', dpi=300)

    # Plot the next set of variables
    # Total km driven per vtype
    x2 = []
    for mode in vType_route_lengths:
        x2.append(mode)
    y2 = []
    for mode in vType_route_lengths:
        y2.append(vType_route_lengths[mode]/1000)
    plt.figure(2)
    plt.bar(range(len(x2)), y2, align='center')
    plt.xticks(range(len(x2)), x2)
    plt.xlabel('')
    plt.ylabel('distance in km')
    plt.title('SUM of ALL DISTANCES travelled PER VEHICLE TYPE\n'
              + 'Total sum of driven km by all vehicles: {:.3f} km\n'.format(vType_route_lengths_sum/1000)
              + '(internal and external demand, finished and unfinished trips)')
    plt.tight_layout()
    if with_graphs_exported:
        plt.savefig('vType_route_lengths.png', dpi=300)

    # Plot the next set of variables
    # Percentage of Finished Trips
    x1 = []
    x1.append('total\n average')
    x1.append('')
    for mode in share_finished_trips_per_mode:
        x1.append(mode)
    y1 = []
    y1.append(share_persontrips_finished*100)
    y1.append(0)
    for mode in share_finished_trips_per_mode:
        y1.append(share_finished_trips_per_mode[mode]*100)
    plt.figure(1)
    plt.bar(range(len(x1)), y1, align='center')
    plt.xticks(range(len(x1)), x1)
    plt.xlabel('')
    plt.ylabel('finished trips in %')
    plt.title('PERCENTAGE of FINISHED TRIPS\n'
              + 'Total number of trips ...finished = {:}'.format(total_number_of_finished_trips)
              + ' / ...started = {:}\n'.format(num_persontrips_all)
              + '(only internal demand)')
    plt.tight_layout()
    if with_graphs_exported:
        plt.savefig('share_finished_trips_per_mode.png', dpi=300)

    # Plot the next set of variables
    # Modal Split
    labels = mode_shares.keys()
    shares = mode_shares.values()
    # Plot the mode shares as a pie chart
    plt.figure(0)
    plt.pie(shares, labels=labels, autopct='%1.1f%%')
    # plt.pie(shares, labels=labels)
    plt.title('MODAL SPLIT\n'
              + '(only internal demand, finished and unfinished trips)')

    # Set aspect ratio to be equal so that pie is drawn as a circle
    plt.axis('equal')
    plt.tight_layout()
    if with_graphs_exported:
        plt.savefig('modal_split.png', dpi=300)

if with_prints:
    print('')
    print('')
    print('')
    print(' Data analysis complete.')

# Display all figures
if with_graphs_shown is True:
    plt.show()
