/*
 * Copyright (C) by Olivier Goffart <ogoffart@owncloud.com>
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
 * for more details.
 */

#pragma once

#include "qsferasynclib.h"
// Chain in the base include and extend the namespace
#include "common/filesystembase.h"
#include "common/result.h"
#include "libsync/csync.h"

class QFile;

namespace OCC {

class SyncJournal;
class SyncFileItem;

/**
 *  \addtogroup libsync
 *  @{
 */

/**
 * @brief This file contains file system helper
 */
namespace FileSystem {
    /**
     * @brief compare two files with given filename and return true if they have the same content
     */
    bool fileEquals(const QString &fn1, const QString &fn2);


    QSFERA_SYNC_EXPORT time_t fileTimeToTime_t(std::filesystem::file_time_type fileTime);
    QSFERA_SYNC_EXPORT std::filesystem::file_time_type time_tToFileTime(time_t fileTime);
    /**
     * @brief Get the mtime for a filepath
     *
     * Use this over QFileInfo::lastModified() to avoid timezone related bugs. See
     * owncloud/core#9781 for details.
     */

    time_t QSFERA_SYNC_EXPORT getModTime(const std::filesystem::path &filename);
    inline time_t getModTime(const QString &filename)
    {
        return getModTime(toFilesystemPath(filename));
    }

    bool QSFERA_SYNC_EXPORT setModTime(const std::filesystem::path &filename, time_t modTime);
    inline time_t setModTime(const QString &filename, time_t modTime)
    {
        return setModTime(toFilesystemPath(filename), modTime);
    }

    /**
     * @brief Get the size for a file
     *
     * Use this over QFileInfo::size() to avoid bugs with lnk files on Windows.
     * See https://bugreports.qt.io/browse/QTBUG-24831.
     */
    qint64 QSFERA_SYNC_EXPORT getSize(const std::filesystem::path &filename);
    inline qint64 getSize(const QFileInfo &info)
    {
        return getSize(toFilesystemPath(info.absoluteFilePath()));
    }

    /**
     * @brief Retrieve a file inode with csync
     */
    [[nodiscard]] std::optional<uint64_t> QSFERA_SYNC_EXPORT getInode(const std::filesystem::path &filename);
    [[nodiscard]] inline auto getInode(const QString &filename)
    {
        return getInode(toFilesystemPath(filename));
    }


    /**
     * @brief Check if \a fileName has changed given previous size and mtime
     *
     * Nonexisting files are covered through mtime: they have an previousMtime of -1.
     *
     * @return true if the file's mtime or size are not what is expected.
     */
    struct FileChangedInfo
    {
        static QSFERA_SYNC_EXPORT FileChangedInfo fromSyncFileItem(const SyncFileItem *const item);
        static QSFERA_SYNC_EXPORT FileChangedInfo fromSyncFileItemPrevious(const SyncFileItem *const item);
        static QSFERA_SYNC_EXPORT FileChangedInfo fromSyncJournalFileRecord(const SyncJournalFileRecord &record);

        qint64 size = {};
        std::optional<time_t> mtime = {};
        std::optional<quint64> inode = {};
        CSyncEnums::ItemType type = CSyncEnums::ItemTypeUnsupported;
    };
    bool QSFERA_SYNC_EXPORT fileChanged(const std::filesystem::path &path, const FileChangedInfo &previousInfo);


    // canonicalPath returns an empty string if the file does not exist.
    // This function also works with files that does not exist and resolve the symlinks in the
    // parent directories.
    std::filesystem::path QSFERA_SYNC_EXPORT canonicalPath(const std::filesystem::path &p);

    QString QSFERA_SYNC_EXPORT canonicalPath(const QString &p);


    struct RemoveEntry
    {
        const QString path;
        const bool isDir;
    };
    struct RemoveError
    {
        const RemoveEntry entry;
        const QString error;
    };

    using RemoveEntryList = std::vector<RemoveEntry>;
    using RemoveErrorList = std::vector<RemoveError>;

    /**
     * Removes a directory and its contents recursively
     *
     * Returns true if all removes succeeded.
     */
    bool QSFERA_SYNC_EXPORT removeRecursively(const QString &path, RemoveEntryList *success, RemoveEntryList *locked, RemoveErrorList *errors);

    namespace Tags {
        std::optional<QString> QSFERA_SYNC_EXPORT get(const QString &path, const QString &key);
        OCC::Result<void, QString> QSFERA_SYNC_EXPORT set(const QString &path, const QString &key, const QString &value);
        OCC::Result<void, QString> QSFERA_SYNC_EXPORT remove(const QString &path, const QString &key);
    }
}

/** @} */
}
