/*
 * 86Box    A hypervisor and IBM PC system emulator that specializes in
 *          running old operating systems and software designed for IBM
 *          PC systems and compatibles from 1981 through fairly recent
 *          system designs based on the PCI bus.
 *
 *          This file is part of the 86Box distribution.
 *
 *          Header for 86Box VM manager system module
 *
 * Authors: cold-brewed
 *
 *          Copyright 2024 cold-brewed
 */
#ifndef QT_VMMANAGER_SYSTEM_H
#define QT_VMMANAGER_SYSTEM_H

#include <QFileInfo>
#include <QDir>
#include <QProcess>
#include <QLocalServer>
#include <QWidget>
#include "qt_vmmanager_serversocket.hpp"
#include "qt_vmmanager_config.hpp"
#include "qt_deviceconfig.hpp"

// This macro helps give us the required `qHash()` function in order to use the
// enum as a hash key
#define QHASH_FOR_CLASS_ENUM(T)                                                       \
    inline uint qHash(const T &t, uint seed)                                          \
    {                                                                                 \
        return ::qHash(static_cast<typename std::underlying_type<T>::type>(t), seed); \
    }

namespace VMManager {
Q_NAMESPACE
namespace Display {
    Q_NAMESPACE
    enum class Name {
        Machine,
        CPU,
        Memory,
        Video,
        Disks,
        Floppy,
        CD,
        RDisk,
        MO,
        Tape,
        SCSIController,
        StorageController,
        MidiOut,
        Joystick,
        Serial,
        Parallel,
        Audio,
        Voodoo,
        NIC,
        Keyboard,
        Mouse,
        IsaRtc,
        IsaMem,
        IsaRom,
        Unknown
    };
    Q_ENUM_NS(Name)
    QHASH_FOR_CLASS_ENUM(Name)
}
}

class VMManagerSystem : public QWidget {
    Q_OBJECT

    typedef QHash<VMManager::Display::Name, QString> display_table_t;
    typedef QHash<QString, QHash<QString, QString>>  config_hash_t;

public:
    enum class ProcessStatus {
        Stopped,
        Running,
        Paused,
        PausedWaiting,
        RunningWaiting,
        Unknown,
    };
    Q_ENUM(ProcessStatus);

    explicit VMManagerSystem(const QString &sysconfig_file);
    // Default constructor will generate a temporary filename as the config file
    // but it will not be valid (isValid() will return false)
    VMManagerSystem()
        : VMManagerSystem(generateTemporaryFilename())
    {
    }

    ~VMManagerSystem() override;

    static QVector<VMManagerSystem *> scanForConfigs(QWidget *parent = nullptr, const QString &searchPath = {});
    static QString                    generateTemporaryFilename();

    QFileInfo   config_file;
    QString     config_name;
    QString     config_dir;
    QString     shortened_dir;
    QString     uuid;
    QString     displayName;
    QString     notes;
    QString     icon;
    QStringList searchTerms;

    config_hash_t config_hash;

    [[nodiscard]] QString                                 getAll(const QString &category) const;
    [[nodiscard]] QHash<QString, QString>                 getCategory(const QString &category) const;
    [[nodiscard]] QHash<QString, QHash<QString, QString>> getConfigHash() const;

    void setDisplayName(const QString &newDisplayName);
    void setNotes(const QString &newNotes);

    [[nodiscard]] bool   isValid() const;
    [[nodiscard]] bool   isProcessRunning() const;
    [[nodiscard]] qint64 processId() const;
public slots:
    void launch86Box(bool settings = false);
    void launchSettings();
    void startButtonPressed();
    void restartButtonPressed();
    void pauseButtonPressed();
    void shutdownRequestButtonPressed();
    void shutdownForceButtonPressed();
    void cadButtonPressed();
    void reloadConfig();
    void sendGlobalConfigurationChanged();

public:
    QDateTime timestamp();
    void      setIcon(const QString &newIcon);

    QProcess *process = new QProcess();

    bool window_obscured;

    QString       getDisplayValue(VMManager::Display::Name key);
    QFileInfoList getScreenshots();

    inline bool operator==(const VMManagerSystem &rhs) const
    {
        return config_file.filePath() == rhs.config_file.filePath();
    }

    static QString
                                processStatusToString(VMManagerSystem::ProcessStatus status);
    ProcessStatus               process_status;
    [[nodiscard]] QString       getProcessStatusString() const;
    [[nodiscard]] ProcessStatus getProcessStatus() const;

signals:
    void windowStatusChanged();
    void itemDataChanged();
    void clientProcessStatusChanged();
    void configurationChanged(VMManagerSystem *sysconfig);
    void globalConfigurationChanged();

private:
    void loadSettings();
    void saveSettings();
    void generateSearchTerms();
    void updateTimestamp();

    display_table_t display_table;

    QFileInfo main_binary;
    QString   platform;

    // QDir application_temp_directory;
    // QDir standard_temp_directory;
    // QDir app_data_directory;
    QDir screenshot_directory;

    QString   unique_name;
    QDateTime lastUsedTimestamp;

    VMManagerServerSocket            *socket_server;
    VMManagerServerSocket::ServerType socket_server_type;

    // Configuration file settings
    VMManagerConfig *config_settings;

    WId id;

    bool serverIsRunning;
    bool startServer();

    bool has86BoxBinary();
    void find86BoxBinary();
    void setupPaths();
    void setupVars();
    void setProcessEnvVars();

    void dataReceived();
    void windowStatusChangeReceived(int status);
    void runningStatusChangeReceived(VMManagerProtocol::RunningState state);
    void configurationChangeReceived();
    void processStatusChanged();
    void statusRefresh();
};

#endif // QT_VMMANAGER_SYSTEM_H
