/*
    SPDX-FileCopyrightText: 2024 Méven Car <meven@kde.org>

    SPDX-License-Identifier: LGPL-2.0-or-later
*/
#ifndef STAT_UNIX_H
#define STAT_UNIX_H

#include "qplatformdefs.h"

#include <config-stat-unix.h>
#include <kio/statjob.h>

#if HAVE_STATX
#include <sys/stat.h>
#include <sys/sysmacros.h> // for makedev()
#endif

#ifdef Q_OS_WIN
// QT_LSTAT on Windows
#include "kioglobal_p.h"
#endif

// NOTE: these calls would be nicer if they used real dirfds instead of AT_FDCWD, but benchmarking suggests that
// it makes no real difference since we need to concatenate a complete path anyway (for UDSEntry). The kernel seems
// to not care much whether the input is relative or absolute either.

#if HAVE_STATX
// statx syscall is available
inline int LSTAT(const char *path, struct statx *buff, KIO::StatDetails details)
{
    uint32_t mask = 0;
    if (details & KIO::StatBasic) {
        // filename, access, type, size, linkdest
        mask |= STATX_SIZE | STATX_TYPE;
    }
    if (details & KIO::StatUser) {
        // uid, gid
        mask |= STATX_UID | STATX_GID;
    }
    if (details & KIO::StatTime) {
        // atime, mtime, btime
        mask |= STATX_ATIME | STATX_MTIME | STATX_BTIME;
    }
    if (details & KIO::StatInode) {
        // dev, inode
        mask |= STATX_INO;
    }
#if HAVE_STATX_SUBVOL
    if (details & KIO::StatSubVolId) {
        // subvol id
        mask |= STATX_SUBVOL;
    }
#endif
#if HAVE_STATX_MNT_ID_UNIQUE
    if (details & KIO::StatMountId) {
        // mount unique id
        mask |= STATX_MNT_ID_UNIQUE;
    }
#endif
    return statx(AT_FDCWD, path, AT_SYMLINK_NOFOLLOW, mask, buff);
}
inline int STAT(const char *path, struct statx *buff, const KIO::StatDetails &details)
{
    uint32_t mask = 0;
    // KIO::StatAcl needs type
    if (details & (KIO::StatBasic | KIO::StatAcl | KIO::StatResolveSymlink)) {
        // filename, access, type
        mask |= STATX_TYPE;
    }
    if (details & (KIO::StatBasic | KIO::StatResolveSymlink)) {
        // size, linkdest
        mask |= STATX_SIZE;
    }
    if (details & KIO::StatUser) {
        // uid, gid
        mask |= STATX_UID | STATX_GID;
    }
    if (details & KIO::StatTime) {
        // atime, mtime, btime
        mask |= STATX_ATIME | STATX_MTIME | STATX_BTIME;
    }
#if HAVE_STATX_SUBVOL
    if (details & KIO::StatSubVolId) {
        // subvol id
        mask |= STATX_SUBVOL;
    }
#endif
#if HAVE_STATX_MNT_ID_UNIQUE
    if (details & KIO::StatMountId) {
        // mount unique id
        mask |= STATX_MNT_ID_UNIQUE;
    }
#endif
    // KIO::Inode is ignored as when STAT is called, the entry inode field has already been filled
    return statx(AT_FDCWD, path, AT_STATX_SYNC_AS_STAT, mask, buff);
}
inline static uint16_t stat_mode(const struct statx &buf)
{
    return buf.stx_mode;
}
inline static dev_t stat_dev(const struct statx &buf)
{
    return makedev(buf.stx_dev_major, buf.stx_dev_minor);
}
inline static uint64_t stat_ino(const struct statx &buf)
{
    return buf.stx_ino;
}
inline static size_t stat_size(const struct statx &buf)
{
    return buf.stx_size;
}
inline static uint32_t stat_uid(const struct statx &buf)
{
    return buf.stx_uid;
}
inline static uint32_t stat_gid(const struct statx &buf)
{
    return buf.stx_gid;
}
inline static int64_t stat_atime(const struct statx &buf)
{
    return buf.stx_atime.tv_sec;
}
inline static int64_t stat_atime_ns(const struct statx &buf)
{
    return buf.stx_atime.tv_nsec;
}
inline static int64_t stat_mtime(const struct statx &buf)
{
    return buf.stx_mtime.tv_sec;
}
inline static int64_t stat_mtime_ns(const struct statx &buf)
{
    return buf.stx_mtime.tv_nsec;
}
#if HAVE_STATX_SUBVOL
inline static uint64_t stat_subvol(const struct statx &buf)
{
    return buf.stx_subvol;
}
#endif
#if HAVE_STATX_MNT_ID_UNIQUE
inline static uint64_t stat_mnt_id(const struct statx &buf)
{
    return buf.stx_mnt_id;
}
#endif
#else
// regular stat struct (no statx)
inline int LSTAT(const char *path, QT_STATBUF *buff, KIO::StatDetails details)
{
    Q_UNUSED(details)
    return QT_LSTAT(path, buff);
}
inline int STAT(const char *path, QT_STATBUF *buff, KIO::StatDetails details)
{
    Q_UNUSED(details)
    return QT_STAT(path, buff);
}
#endif

// QT_STATBUF overloads (always available regardless of HAVE_STATX)
inline static mode_t stat_mode(const QT_STATBUF &buf)
{
    return buf.st_mode;
}
inline static dev_t stat_dev(const QT_STATBUF &buf)
{
    return buf.st_dev;
}
inline static ino_t stat_ino(const QT_STATBUF &buf)
{
    return buf.st_ino;
}
inline static size_t stat_size(const QT_STATBUF &buf)
{
    return buf.st_size;
}
inline static uint32_t stat_uid(const QT_STATBUF &buf)
{
    return buf.st_uid;
}
inline static uint32_t stat_gid(const QT_STATBUF &buf)
{
    return buf.st_gid;
}
inline static time_t stat_atime(const QT_STATBUF &buf)
{
    return buf.st_atime;
}
inline static int64_t stat_atime_ns(const QT_STATBUF &buf)
{
#if defined(Q_OS_WIN)
    return 0;
#elif defined(Q_OS_DARWIN)
    return buf.st_atimespec.tv_nsec;
#else
    return buf.st_atim.tv_nsec;
#endif
}
inline static time_t stat_mtime(const QT_STATBUF &buf)
{
    return buf.st_mtime;
}
inline static int64_t stat_mtime_ns(const QT_STATBUF &buf)
{
#if defined(Q_OS_WIN)
    return 0;
#elif defined(Q_OS_DARWIN)
    return buf.st_mtimespec.tv_nsec;
#else
    return buf.st_mtim.tv_nsec;
#endif
}

#endif // STAT_UNIX_H
