/**
 * Copyright (c) 2022
 *    Marcus Britanicus (https://gitlab.com/marcusbritanicus)
 *    Abrar (https://gitlab.com/s96abrar)
 *    rahmanshaber (https://gitlab.com/rahmanshaber)
 *
 * DFL::Storage provides a C++ API for accessing devices and
 * partitions that are exposed by udisks2 via dbus.
 **/

#pragma once

#include <QtDBus>
#include <QString>

namespace DFL {
    namespace Storage {
        class Manager;
        class Device;
        class Block;

        typedef QList<Device>   Devices;
        typedef QList<Block>    Blocks;
    }
}

class DFL::Storage::Manager : public QObject {
    Q_OBJECT

    public:
        /* Hook to access global instance */
        static DFL::Storage::Manager *instance();

        /* List all the available devices */
        DFL::Storage::Devices devices();

        /* All the available blocks: will have containers and whole disks and partitions */
        DFL::Storage::Blocks blocks();

        /* Device for a path */
        DFL::Storage::Block blockForPath( QString );

    private Q_SLOTS:
        /* Handle adding of new drives and partitions */
        void interfacesAdded( const QDBusObjectPath&, const QMap<QString, QVariant>& );

        /* Handle removing of new drives and partitions */
        void interfacesRemoved( const QDBusObjectPath&, const QStringList& );

    private:
        /* Initialization */
        Manager();

        /* Rescan all the available devices */
        void rescanStorage();

        /* List of all available devices */
        DFL::Storage::Devices mDevices;

        /* List of all available blocks */
        DFL::Storage::Blocks mBlocks;

        /* A map of drives and it's partitions */
        QMap<QString, QStringList> mDrivePartsMap;

        /* A map of partiton and it's drive */
        QMap<QString, QString> mPartDriveMap;

        /* Static global instance */
        static DFL::Storage::Manager *mManager;

    Q_SIGNALS:
        /* Something was added to /org/freedesktop/UDisks2/drives */
        void deviceAdded( QString );

        /* Something was added to /org/freedesktop/UDisks2/drives */
        void deviceRemoved( QString );

        /* Something was added to /org/freedesktop/UDisks2/block_devices */
        void partitionAdded( QString );

        /* Something was added to /org/freedesktop/UDisks2/block_devices */
        void partitionRemoved( QString );
};

class DFL::Storage::Device {
    public:
        Device();
        Device( QString );

        bool isValid();

        DFL::Storage::Blocks partitions();
        DFL::Storage::Blocks validPartitions();

        QString label();
        QString path();
        QString id();
        bool isRemovable();
        bool isOptical();
        qint64 size();
        int rotationRate();
        QString seat();

        QVariant property( QString key );

    private:
        void readPartitions();

        QStringList mPartNames;
        DFL::Storage::Blocks mParts;
        DFL::Storage::Blocks mValidParts;
        QDBusInterface *iface = nullptr;

        QString mDriveId;

        QString mLabel;
        QString mPath;
        QString mId;
        bool mIsRemovable;
        bool mIsOptical;
        qint64 mSize;
        int mRotationRate;
        QString mSeat;

        bool mIsValid;
};

class DFL::Storage::Block {
    public:
        Block();
        Block( QString );

        bool isValid();

        QString label();
        QString path();
        QString device();
        QString drive();
        QString mountPoint();
        QString fileSystem();

        bool isOptical();
        bool isRemovable();

        qint64 availableSize();
        qint64 totalSize();

        QVariant property( QString interface, QString key );

        bool mount();
        bool unmount();

    private:
        void getMountPoint();

        QString mLabel;
        QString mPath;
        QString mDevice;
        QString mDrive;
        QString mMountPoint;
        QString mFileSystem;

        bool mIsOptical;
        bool mIsRemovable;

        qint64 mAvailableSize;
        qint64 mTotalSize;
};
