#pragma once

#include <vector>
#include <string>
#include <libudev.h>
#include <filesystem>
#include <unordered_set>

#include "os_enums.h"
#include "aqsi-commons-global.h"

namespace aqsi {
namespace os {

/// Имя целевого устройства раздела DATA. Для Cube T-C.
constexpr auto kCubeTCDataDeviceName = "mmcblk1gp3p1";
///Имя подсистемы для блочных устройств.
constexpr auto kBlockSubsystemName = "block";
/// Адрес System on Chip (SoC) блочного устройства раздела DATA. Для Cube T-C.
constexpr auto kCubeTCDataDeviceSocAddress = "soc@3000000";
/// Адрес контроллера MMC. блочного устройства раздела DATA. Для Cube T-C.
constexpr auto kCubeTCDataDeviceMmcAddress = "4022000.mmc";

/**
    \brief Класс для хранения информации об устройстве. получаемой из getDeviceInfoBySubsystem
 */
class AQSICOMMONS_EXPORT DeviceInfo
{
public:
    /**
        \brief Конструктор класса DeviceInfo.
        \param path Путь к устройству.
        \param subsystem Subsystem устройства.
        \param syspath Системный путь устройства.
     */
    [[deprecated(
        "Используйте DeviceInfo(struct udev_device *device, std::string const& subsystem) "
        "взамен")]] explicit DeviceInfo(std::filesystem::path const& path, std::string const& subsystem, std::filesystem::path const& syspath)
        : m_dev_path(path),
          m_dev_subsystem(getSubsystemEnum(subsystem.c_str())),
          m_dev_subsystem_string(subsystem),
          m_syspath(syspath)
    {}

    /**
        \brief Конструктор класса DeviceInfo.
        \param [in] device Устройство.
        \param [in] subsystem Subsystem устройства.
        \throw AqsiException с кодом AqsiErrorCode::kResourceError, если имя устройства отсутствует или путь
        путь к устройству некорректный.
     */
    explicit DeviceInfo(struct udev_device* device, std::string const& subsystem);

    /**
        \brief Возвращает путь к устройству.
        \return Путь к устройству.
     */
    std::filesystem::path const&
    path() const
    {
        return m_dev_path;
    }

    /**
        \brief Возвращает subsystem устройства.
        \return Subsystem устройства.
     */
    Subsystems
    subsystemEnum() const
    {
        return m_dev_subsystem;
    }

    /**
        \brief Возвращает subsystem устройства.
        \return Subsystem устройства.
     */
    [[deprecated("Используйте Subsystems subsystemEnum взамен")]] std::string const&
    subsystem() const
    {
        return m_dev_subsystem_string;
    }

    /**
    \brief Возвращает системный путь устройства.
    \return Системный путь устройства.
 */
    std::filesystem::path const&
    syspath() const
    {
        return m_syspath;
    }

private:
    std::filesystem::path m_dev_path;    ///< Путь к устройству.
    Subsystems m_dev_subsystem;          ///< Subsystem устройства.
    std::string m_dev_subsystem_string;  ///< Subsystem устройства.
    std::filesystem::path m_syspath;     ///< Системный путь устройства.
};

/**
    \brief Класс для представления данных о точке монтирования.
 */
class AQSICOMMONS_EXPORT MountPointInfo
{
public:
    /**
        \brief Конструктор класса MountPointInfo.
        \param source Исходное устройство или точка монтирования.
        \param target Целевая точка монтирования.
        \param fstype Тип файловой системы.
     */
    MountPointInfo(std::string source, std::string target, std::string fstype)
        : m_source(std::move(source)),
          m_target(std::move(target)),
          m_fstype(getFStypeEnum(fstype.c_str())),
          m_fstype_string(fstype)
    {}

    /**
        \brief Возвращает  Монтируемое устройство (device).
        \return  Монтируемое устройство (device).
     */
    std::string const&
    source() const
    {
        return m_source;
    }

    /**
        \brief Возвращает целевую точку монтирования.
        \return Целевая точка монтирования.
     */
    std::filesystem::path const&
    target() const
    {
        return m_target;
    }

    /**
        \brief Возвращает тип файловой системы.
        \return Тип файловой системы.
     */
    FSTypes
    fstypeEnum() const
    {
        return m_fstype;
    }

    /**
        \brief Возвращает тип файловой системы.
        \return Тип файловой системы.
     */
    [[deprecated("Используйте FSTypes fstypeEnum взамен")]] std::string const&
    fstype() const
    {
        return m_fstype_string;
    }

private:
    std::string m_source;            ///< Источник монтирования.
    std::filesystem::path m_target;  ///< Целевая точка монтирования.
    FSTypes m_fstype;                ///< Тип файловой системы.
    std::string m_fstype_string;     ///< Тип файловой системы.
};

[[deprecated(
    "Используйте AQSICOMMONS_EXPORT bool sendSignal(std::string const& proc_name, KillSignals signal) "
    "взамен")]] AQSICOMMONS_EXPORT bool
sendSignal(std::string const& proc_name, int signal);
AQSICOMMONS_EXPORT bool sendSignal(std::string const& proc_name, KillSignals signal);

/**
    \brief Получает информацию об устройствах для указанного subsystem.
    \details Эта функция использует библиотеку udev для перечисления устройств с соответствующей subsystem,
        присутствующих в системе. (например,  "block")
    \param [in] subsystem Subsystem для фильтрации устройств.
    \return Контейнер линейной последовательности объектов DeviceInfo, содержащие информацию об устройствах.
    \throw aqsi::AqsiException При возникновении ошибок получения списка.
 */
AQSICOMMONS_EXPORT std::vector<DeviceInfo> getDeviceInfoBySubsystemVect(std::string const& subsystem);

/**
    \brief Получает информацию об устройствах для указанного subsystem.
    \details Эта функция использует библиотеку udev для перечисления устройств с соответствующей subsystem,
        присутствующих в системе. (например,  "block")
    \param [in] subsystem Subsystem для фильтрации устройств.
    \return Хэш-контейнер смарт-поинтеров на объекты DeviceInfo, содержащие информацию об устройствах.
    \throw aqsi::AqsiException При возникновении ошибок получения списка.
 */
[[deprecated(
    "Используйте AQSICOMMONS_EXPORT std::vector<DeviceInfo> getDeviceInfoBySubsystemVect(std::string const& subsystem) "
    "взамен")]] AQSICOMMONS_EXPORT std::unordered_set<std::shared_ptr<DeviceInfo>>
getDeviceInfoBySubsystem(std::string const& subsystem);

/**
    \brief Получает информацию о точках монтирования.
    \details Эта функция считывает информацию из системного файла mountinfo и возвращает
    Контейнер линейной последовательности объектов MountPointInfo.
    \return Контейнер линейной последовательности объектов MountPointInfo, представляющие точки монтирования.
    \throw aqsi::AqsiException При возникновении ошибок получения списка.
 */
AQSICOMMONS_EXPORT std::vector<MountPointInfo> getMountPointsInfoVect();

/**
    \brief Получает информацию о точках монтирования.
    \details Эта функция считывает информацию из системного файла mountinfo и возвращает
    Хэш-контейнер смарт-поинтеров на объекты MountPointInfo.
    \return Хэш-контейнер смарт-поинтеров на объекты MountPointInfo, представляющие точки монтирования.
    \throw aqsi::AqsiException При возникновении ошибок получения списка.
 */
[[deprecated(
    "Используйте AQSICOMMONS_EXPORT std::vector<MountPointInfo> getMountPointsInfoVect() взамен")]] AQSICOMMONS_EXPORT
    std::unordered_set<std::shared_ptr<MountPointInfo>>
    getMountPointsInfo();

/**
    \brief Получает все точки монтирования для заданного устройства.
    \details Эта функция принимает путь к устройству , и возвращает список всех точек монтирования,
    где смонтирован указанный `device_path`. Вызывая функцию getMountedDevices для получения списка
    всех смонтированных устройств.
    \param [in] device Путь к устройству, которое нужно проверить (например, "/dev/sda1").
    \return std::vector<std::filesystem::path> Множество указателей на пути к точкам
    монтирования, связанных с данным устройством. Если устройство не смонтировано, возвращается пустое множество. \throw
    AqsiException с кодом AqsiErrorCode::kInvalidArgumentsError, если указан пустой или некорректный путь к устройству.
 */
std::vector<std::filesystem::path> getMountPointsForDeviceVect(std::filesystem::path const& device_path);

/**
    \brief Получает все точки монтирования для заданного устройства.
    \details Эта функция принимает путь к устройству , и возвращает список всех точек монтирования,
    где смонтирован указанный `devicePath`. Вызывая функцию getMountedDevices для получения списка
    всех смонтированных устройств.
    \param [in] device Путь к устройству, которое нужно проверить (например, "/dev/sda1").
    \return std::unordered_set<std::shared_ptr<std::filesystem::path>> Множество указателей на пути к точкам
    монтирования, связанных с данным устройством. Если устройство не смонтировано, возвращается пустое множество. \throw
    AqsiException с кодом AqsiErrorCode::kInvalidArgumentsError, если указан пустой или некорректный путь к устройству.
 */
[[deprecated(
    "Используйте std::vector<std::filesystem::path> getMountPointsForDeviceVect(std::filesystem::path const& "
    "device_path) взамен")]] std::unordered_set<std::shared_ptr<std::filesystem::path>>
getMountPointsForDevice(std::filesystem::path const& device_path);

/**
    \brief Проверяет наличие заданной точки монтирования в списке.
    \details Эта функция принимает список точек монтирования для устройства и проверяет,
    присутствует ли указанная точка монтирования (`mount_path`) в этом списке.
    \param [in] mount_points  Множество указателей на точки монтирования для устройства (возвращаемое
    `getMountPointsForDevice`).
    \param [in] mount_path Путь к точке монтирования, которую нужно проверить.
    \return true Если точка монтирования присутствует в списке. false Если точка монтирования отсутствует
    \throw AqsiException с кодом AqsiErrorCode::kInvalidArgumentsError, если указан пустой
    или некорректный путь к устройству.
 */
[[deprecated("Используйте алгоритм поиска std::find взамен")]] bool isMountPointPresent(
    std::vector<std::filesystem::path> const& mount_points, std::filesystem::path const& mount_path);

/**
    \brief Проверяет наличие заданной точки монтирования в списке.
    \details Эта функция принимает список точек монтирования для устройства и проверяет,
    присутствует ли указанная точка монтирования (`mountPath`) в этом списке.
    \param [in] mountPoints  Множество указателей на точки монтирования для устройства (возвращаемое
    `getMountPointsForDevice`).
    \param [in] mountPath Путь к точке монтирования, которую нужно проверить.
    \return true Если точка монтирования присутствует в списке. false Если точка монтирования отсутствует
    \throw AqsiException с кодом AqsiErrorCode::kInvalidArgumentsError, если указан пустой
    или некорректный путь к устройству.
 */
[[deprecated("Используйте алгоритм поиска std::find_if взамен")]] bool isMountPointPresent(
    std::unordered_set<std::shared_ptr<std::filesystem::path>> const& mount_points,
    std::filesystem::path const& mount_path);

}  // namespace os
}  // namespace aqsi
