/*
    SPDX-FileCopyrightText: 2020 Volker Krause <vkrause@kde.org>

    SPDX-License-Identifier: LGPL-2.0-or-later
*/

#ifndef KPUBLICTRANSPORT_RENTALVEHICLE_H
#define KPUBLICTRANSPORT_RENTALVEHICLE_H

#include "datatypes.h"

namespace KPublicTransport {

class RentalVehicleNetwork;
class RentalVehicleType;
class RentalVehiclePrivate;

/** An individual rental vehicle used on a JourneySection, ie. a vehicle you don't own yourself but have to drive yourself.
 *  This can be:
 *  - free floating or dock-based rental bikes, with or without electric assistance
 *  - car sharing (but not ride sharing)
 *  - electric (kick) scooters
 *  - electric motorcycles (scooters)
 *
 *  @see GBFS: https://github.com/NABSA/gbfs/blob/v2.1-RC/gbfs.md#vehicle_typesjson-added-in-v21-rc
 *  @see MDS: https://github.com/openmobilityfoundation/mobility-data-specification/blob/master/provider/README.md#vehicle-types
 */
class KPUBLICTRANSPORT_EXPORT RentalVehicle
{
    KPUBLICTRANSPORT_GADGET(RentalVehicle)
public:
    /** Type of vehicle. */
    enum VehicleType {
        Unknown = 0,
        Bicycle = 1, ///< human-powered bicylce
        Pedelec = 2, ///< bicycle with electric assistance
        ElectricKickScooter = 4, ///< "e scooter", electrically assisted kick scooters, not to be confused with motorcycle-like scooters
        ElectricMoped = 8, ///< motorcycle-like electric scooters
        Car = 16, ///< electrical- or combustion-powered car
    };
    Q_ENUM(VehicleType)
    Q_DECLARE_FLAGS(VehicleTypes, VehicleType)
    Q_FLAG(VehicleTypes)

    /** Vehicle type.
     *  @deprecated use vehicleType
     */
    Q_PROPERTY(VehicleType type READ type STORED false)

    /** Sharing network operator. */
    KPUBLICTRANSPORT_PROPERTY(KPublicTransport::RentalVehicleNetwork, network, setNetwork)

    /** Vehicle type information. */
    KPUBLICTRANSPORT_PROPERTY(KPublicTransport::RentalVehicleType, vehicleType, setVehicleType)

    /** Remaining range of the vehicle in meters.
     *  Negative if unknown.
     */
    KPUBLICTRANSPORT_PROPERTY(int, remainingRange, setRemainingRange)

    /** Icon representing the vehicle type.
     *  Can be a qrc: URL or an XDG icon name.
     */
    Q_PROPERTY(QString vehicleTypeIconName READ vehicleTypeIconName)

    /** Deep booking link via a web UI. */
    KPUBLICTRANSPORT_PROPERTY(QUrl, webBookingUrl, setWebBookingUrl)
    /** Deep booking link via an app. */
    KPUBLICTRANSPORT_PROPERTY(QUrl, appBookingUrl, setAppBookingUrl)

    /** Label shortly describing this transport for display. */
    Q_PROPERTY(QString label READ label STORED false)

public:
    [[nodiscard]] QString vehicleTypeIconName() const;
    [[nodiscard]] QString label() const;

    [[nodiscard]] [[deprecated("use RentalVehicleType")]] VehicleType type() const;

    /** Icon representing the vehicle type.
     *  Can be a qrc: URL or an XDG icon name.
     */
    [[nodiscard]] [[deprecated("use RentalVehicleType")]] static QString vehicleTypeIconName(KPublicTransport::RentalVehicle::VehicleType type);

    /** Serializes one object to JSON. */
    [[nodiscard]] static QJsonObject toJson(const RentalVehicle &vehicle);
    /** Deserialize an object from JSON. */
    [[nodiscard]] static RentalVehicle fromJson(const QJsonObject &obj);
};

Q_DECLARE_OPERATORS_FOR_FLAGS(RentalVehicle::VehicleTypes)

class RentalVehicleTypePrivate;

/** Information about the type of a rental vehicle.
 *  Follows the GBFS vehicle type spec.
 *
 *  @see https://gbfs.org/documentation/reference/#vehicle_typesjson
 */
class KPUBLICTRANSPORT_EXPORT RentalVehicleType
{
    KPUBLICTRANSPORT_GADGET(RentalVehicleType)
    Q_CLASSINFO("RegisterEnumClassesUnscoped", "false")
public:
    KPUBLICTRANSPORT_PROPERTY(QString, id, setId) // TODO should this be an identifier set, per backend?

    /** User-visible (possibly translated) name of the vehicle. */
    KPUBLICTRANSPORT_PROPERTY(QString, name, setName)

    /** Vehicle form factor. */
    enum class FormFactor {
        Undefined,
        Bicycle,
        CargoBicycle,
        Car,
        Moped,
        ScooterStanding,
        ScooterSeated,
        Other,
    };
    Q_ENUM(FormFactor)
    KPUBLICTRANSPORT_PROPERTY(FormFactor, formFactor, setFormFactor)

    /** Vehicle propulsion type. */
    enum class PropulsionType {
        Undefined,
        Human,
        ElectricAssist,
        Electric,
        Combustion,
        CombustionDiesel,
        Hybrid,
        PlugInHybrid,
        HydrogenFuelCell,
    };
    Q_ENUM(PropulsionType)
    KPUBLICTRANSPORT_PROPERTY(PropulsionType, propulsionType, setPropulsionType)

    /** For vehicles with a non-human propulsion type, the maximum range in meters
     *  when fully charged/fueled.
     *  @c NAN indicates unknown/unset values.
     */
    KPUBLICTRANSPORT_PROPERTY(double, maxRangeMeters, setMaxRangeMeters)

    /** Maximum amount of passenger the vehicle can accomdate (driver included).
     *  @c -1 indicate unknown/unset values.
     */
    KPUBLICTRANSPORT_PROPERTY(int, riderCapacity, setRiderCapcatiry)

    /** CO₂ emission per kilometer, in gram.
     *  @c NAN indicate unknown values.
     */
    KPUBLICTRANSPORT_PROPERTY(double, co2EmissionPerKm, setCo2EmissionPerKm)

    /** Vehicle return constraint. */
    enum class ReturnConstraint {
        Undefined,
        FreeFloating,
        RoundtripStation,
        AnyStation,
        Hybrid,
    };
    Q_ENUM(ReturnConstraint)
    KPUBLICTRANSPORT_PROPERTY(ReturnConstraint, returnConstraint, setReturnConstraint)

    /** Icon representing the vehicle type.
     *  Can be a qrc: URL or an XDG icon name.
     */
    Q_PROPERTY(QString typeIconName READ typeIconName STORED false)
    [[nodiscard]] QString typeIconName() const;
    Q_INVOKABLE [[nodiscard]] static QString typeIconName(KPublicTransport::RentalVehicleType::FormFactor formFactor, KPublicTransport::RentalVehicleType::PropulsionType propulsionType);

    /** Label shortly describing this transport for display. */
    Q_PROPERTY(QString label READ label STORED false)
    [[nodiscard]] QString label() const;

    /** Serializes one object to JSON. */
    [[nodiscard]] static QJsonObject toJson(const RentalVehicleType &vehicleType);
    [[nodiscard]] static QJsonArray toJson(const std::vector<RentalVehicleType> &vehicleTypes);
    /** Deserialize an object from JSON. */
    [[nodiscard]] static RentalVehicleType fromJson(const QJsonObject &obj);
    [[nodiscard]] static std::vector<RentalVehicleType> fromJson(const QJsonArray &array);
};

class RentalVehicleStationPrivate;

/** Additional information for a vehicle renting station, attached to Location objects.
 *  This is typically needed for dock-based bike sharing systems.
 *
 *  @see https://github.com/NABSA/gbfs/blob/master/gbfs.md#station_informationjson
 *  @see https://github.com/NABSA/gbfs/blob/master/gbfs.md#station_statusjson
 */
class KPUBLICTRANSPORT_EXPORT RentalVehicleStation
{
    KPUBLICTRANSPORT_GADGET(RentalVehicleStation)
    /** Number of available (rentable) vehicles at this station. */
    KPUBLICTRANSPORT_PROPERTY(int, availableVehicles, setAvailableVehicles)
    /** Number of dock positions at this station.
     *  If capacity == availableVehicles no vehicles can be returned at this station.
     */
    KPUBLICTRANSPORT_PROPERTY(int, capacity, setCapacity)

    /** Sharing network operator. */
    KPUBLICTRANSPORT_PROPERTY(KPublicTransport::RentalVehicleNetwork, network, setNetwork)

    /** Not an empty/default constructed object. */
    Q_PROPERTY(bool isValid READ isValid STORED false)

    /** Supported vehicle types at this station. */
    Q_PROPERTY(QList<KPublicTransport::RentalVehicleType> supportedVehicleTypes READ supportedVehicleTypes STORED false)
    /** Available vehicle types at this station. */
    Q_PROPERTY(QList<KPublicTransport::RentalVehicleType> availableVehicleTypes READ availableVehicleTypes STORED false)

    /** Deep booking link via a web UI. */
    KPUBLICTRANSPORT_PROPERTY(QUrl, webBookingUrl, setWebBookingUrl)
    /** Deep booking link via an app. */
    KPUBLICTRANSPORT_PROPERTY(QUrl, appBookingUrl, setAppBookingUrl)

    /** Opening hours for this station (in OSM format). */
    KPUBLICTRANSPORT_PROPERTY(QString, openingHours, setOpeningHours)

    /** Icon representing this rental vehicle station.
     *  Can be a qrc: URL or an XDG icon name.
     */
    Q_PROPERTY(QString iconName READ iconName STORED false)

public:
    [[nodiscard]] bool isValid() const;
    [[nodiscard]] QList<RentalVehicleType> supportedVehicleTypes() const;
    [[nodiscard]] QList<RentalVehicleType> availableVehicleTypes() const;

    /** Capacity for a given vehicle type. */
    [[nodiscard]] Q_INVOKABLE int capacityByType(const KPublicTransport::RentalVehicleType &type) const;
    /** Set the capacity for a set of vehicle types. */
    void setCapacity(const std::vector<RentalVehicleType> &types, int capacity);

    /** Available vehicles for a given vehicle type. */
    [[nodiscard]] Q_INVOKABLE int availableVehiclesByType(const KPublicTransport::RentalVehicleType &type) const;
    /** Sets the number of available vehicles for a given vehicle type. */
    void setAvailableVehicles(const RentalVehicleType &type, int count);

    [[nodiscard]] QString iconName() const;

    /** Checks if two instances refer to the same station. */
    [[nodiscard]] static bool isSame(const RentalVehicleStation &lhs, const RentalVehicleStation &rhs);

    /** Serializes one object to JSON. */
    [[nodiscard]] static QJsonObject toJson(const RentalVehicleStation &station);
    /** Deserialize an object from JSON. */
    [[nodiscard]] static RentalVehicleStation fromJson(const QJsonObject &obj);
};

class RentalVehicleNetworkPrivate;

/** A vehicle sharing system/network.
 *  Typically one operator/area, needing an account/app for that operator to rent vehicles.
 *
 *  @see https://github.com/NABSA/gbfs/blob/master/gbfs.md#system_informationjson
 */
class KPUBLICTRANSPORT_EXPORT RentalVehicleNetwork
{
    KPUBLICTRANSPORT_GADGET(RentalVehicleNetwork)
    /** Human-visible name of this network. */
    KPUBLICTRANSPORT_PROPERTY(QString, name, setName)
    /** URL e.g. to a website of this network. */
    KPUBLICTRANSPORT_PROPERTY(QUrl, url, setUrl)
    /** Brand color of this network, if available. */
    KPUBLICTRANSPORT_PROPERTY(QColor, brandColor, setBrandColor)
    /** @c true if a brand color is set. */
    Q_PROPERTY(bool hasBrandColor READ hasBrandColor STORED false)
    /** Opening hours for the entire network (in OSM format). */
    KPUBLICTRANSPORT_PROPERTY(QString, openingHours, setOpeningHours)

    /** Not an empty/default constructed object. */
    Q_PROPERTY(bool isValid READ isValid STORED false)

public:
    [[nodiscard]] bool hasBrandColor() const;
    [[nodiscard]] bool isValid() const;

    /** Checks if two instances refer to the same network. */
    [[nodiscard]] static bool isSame(const RentalVehicleNetwork &lhs, const RentalVehicleNetwork &rhs);

    /** Serializes one object to JSON. */
    [[nodiscard]] static QJsonObject toJson(const RentalVehicleNetwork &network);
    /** Deserialize an object from JSON. */
    [[nodiscard]] static RentalVehicleNetwork fromJson(const QJsonObject &obj);
};
}

Q_DECLARE_METATYPE(KPublicTransport::RentalVehicleNetwork)
Q_DECLARE_METATYPE(KPublicTransport::RentalVehicleStation)
Q_DECLARE_METATYPE(KPublicTransport::RentalVehicleType)
Q_DECLARE_METATYPE(KPublicTransport::RentalVehicle)

#endif // KPUBLICTRANSPORT_RENTALVEHICLE_H
