/****************************************************************************/
// Eclipse SUMO, Simulation of Urban MObility; see https://eclipse.dev/sumo
// Copyright (C) 2001-2026 German Aerospace Center (DLR) and others.
// This program and the accompanying materials are made available under the
// terms of the Eclipse Public License 2.0 which is available at
// https://www.eclipse.org/legal/epl-2.0/
// This Source Code may also be made available under the following Secondary
// Licenses when the conditions for such availability set forth in the Eclipse
// Public License 2.0 are satisfied: GNU General Public License, version 2
// or later which is available at
// https://www.gnu.org/licenses/old-licenses/gpl-2.0-standalone.html
// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later
/****************************************************************************/
/// @file    SUMOTime.cpp
/// @author  Daniel Krajzewicz
/// @author  Jakob Erdmann
/// @author  Michael Behrisch
/// @author  Mirko Barthauer
/// @date    Fri, 29.04.2005
///
// Variables, methods, and tools for internal time representation
/****************************************************************************/
#include <config.h>

#include <sstream>
#include <iostream>
#include <iomanip>
#include "SUMOTime.h"
#include "StringTokenizer.h"
#include "StringUtils.h"
#include "StdDefs.h"
#include "MsgHandler.h"


// ===========================================================================
// type definitions
// ===========================================================================
SUMOTime DELTA_T = 1000;


// ===========================================================================
// method definitions
// ===========================================================================

SUMOTime
string2time(const std::string& r) {
    if (r.find(":") == std::string::npos) {
        const double time = StringUtils::toDouble(r);
        if (time > STEPS2TIME(SUMOTime_MAX)) {
            throw TimeFormatException("Input string '" + r + "' exceeds the time value range.");
        }
        return TIME2STEPS(time);
    } else {
        // try to parse dd:hh:mm:ss.s
        std::vector<std::string> hrt = StringTokenizer(r, ":").getVector();
        if (hrt.size() == 3) {
            //std::cout << "parsed '" << r << "' as " << (3600 * string2time(hrt[0]) + 60 * string2time(hrt[1]) + string2time(hrt[2])) << "\n";
            return 3600 * string2time(hrt[0]) + 60 * string2time(hrt[1]) + string2time(hrt[2]);
        } else if (hrt.size() == 4) {
            //std::cout << "parsed '" << r << "' as " << (24 * 3600 * string2time(hrt[0]) + 3600 * string2time(hrt[1]) + 60 * string2time(hrt[2]) + string2time(hrt[3])) << "\n";
            return 24 * 3600 * string2time(hrt[0]) + 3600 * string2time(hrt[1]) + 60 * string2time(hrt[2]) + string2time(hrt[3]);
        }
        throw TimeFormatException("Input string '" + r + "' is not a valid time format (jj:HH:MM:SS.S).");
    }
}


bool
isTime(const std::string& r) {
    if (r.find(":") == std::string::npos) {
        if (StringUtils::isDouble(r)) {
            return (StringUtils::toDouble(r) <= STEPS2TIME(SUMOTime_MAX));
        } else {
            return false;
        }
    } else {
        // try to parse dd:hh:mm:ss.s
        const std::vector<std::string> hrt = StringTokenizer(r, ":").getVector();
        if (hrt.size() == 3) {
            return StringUtils::isInt(hrt[0]) && StringUtils::isInt(hrt[1]) && StringUtils::isInt(hrt[2]);
        } else if (hrt.size() == 4) {
            return StringUtils::isInt(hrt[0]) && StringUtils::isInt(hrt[1]) && StringUtils::isInt(hrt[2]) && StringUtils::isDouble(hrt[3]);
        } else {
            return false;
        }
    }
}


std::string
time2string(SUMOTime t, bool humanReadable) {
    std::ostringstream oss;
    if (t < 0) {
        oss << "-";
    }
    // needed for signed zero errors, see #5926
    // llabs(SUMOTime_MIN) would create overflow and must be handleed separately
    t = t == SUMOTime_MIN ? SUMOTime_MAX : (SUMOTime)llabs(t);
    SUMOTime scale = (SUMOTime)pow(10, MAX2(0, 3 - gPrecision));
    if (scale > 1) {
        if (t != SUMOTime_MAX) {
            t = (t + scale / 2) / scale;
        } else {
            scale = 1;
        }
    }
    const SUMOTime second = TIME2STEPS(1) / scale;
    if (humanReadable) {
        const SUMOTime minute = 60 * second;
        const SUMOTime hour = 60 * minute;
        const SUMOTime day = 24 * hour;
        // 123456 -> "00:00:12.34"
        if (t > day) {
            oss << t / day << ":";
            t %= day;
        }
        oss << std::setfill('0') << std::setw(2);
        oss << t / hour << ":";
        t %= hour;
        oss << std::setw(2) << t / minute << ":";
        t %= minute;
        oss << std::setw(2) << t / second;
        t %= second;
        if (t != 0 || TS < 1.) {
            oss << ".";
            oss << std::setw(MIN2(3, gPrecision));
            oss << t;
        }
    } else {
        oss << t / second << ".";
        oss << std::setfill('0') << std::setw(MIN2(3, gPrecision));
        oss << t % second;
    }
    return oss.str();
}


std::string
time2string(SUMOTime t) {
    return time2string(t, gHumanReadableTime);
}


std::string
elapsedMs2string(long long int t) {
    if (gHumanReadableTime) {
        if (STEPS2TIME(t) > 60) {
            // round to seconds
            return time2string((t / 1000) * 1000);
        } else {
            return toString((double)t / 1000.0) + "s";
        }
    } else {
        return time2string(t) + "s";
    }
}

bool checkStepLengthMultiple(const SUMOTime t, const std::string& error, SUMOTime deltaT, SUMOTime begin) {
    if (begin % deltaT == 0) {
        if (t % deltaT != 0) {
            WRITE_WARNING("The given time value " + time2string(t) + " is not a multiple of the step length " + time2string(deltaT) + error + ".")
        }
    } else {
        if ((t - begin) % deltaT != 0) {
            WRITE_WARNING("The given time value " + time2string(t) + " is not reached with step length " + time2string(deltaT)
                          + " and begin time " + time2string(begin) + error + ".")
        }
    }
    // next line used to fix build
    return false;
}

void checkTimeBounds(const double time) {
    if (time > STEPS2TIME(SUMOTime_MAX)) {
        throw TimeFormatException("Input time " + toString(time) + "s exceeds the time value range.");
    }
}


/****************************************************************************/
