/**
 * @file Directory.h
 * @author apio (cloudapio.eu)
 * @brief A C++-friendly API for directory access.
 *
 * @copyright Copyright (c) 2023, the Luna authors.
 *
 */

#pragma once
#include <dirent.h>
#include <luna/SharedPtr.h>
#include <luna/String.h>
#include <luna/Vector.h>
#include <os/Path.h>

namespace os
{
    /**
     * @brief An object-oriented directory handle, which is closed when all references to it go out of scope.
     */
    class Directory : public Shareable
    {
      public:
        /**
         * @brief Create a new Directory object from a path, and open the directory.
         *
         * @param path The path to open.
         * @return Result<SharedPtr<Directory>> A new Directory object.
         */
        static Result<SharedPtr<Directory>> open(const Path& path);

        /**
         * @brief A filter to skip certain directory entries.
         */
        enum class Filter
        {
            None,         // Do not skip anything.
            Hidden,       // Skip all hidden files (that start with a '.' character).
            ParentAndBase // Skip only the '.' and '..' entries.
        };

        /**
         * @brief Read the next entry name from this Directory.
         *
         * @param filter The entries to filter out.
         * @return Result<String> The next entry's name as an owned String object, or an empty string if
         * there are no more entries.
         */
        Result<String> next(Filter filter);

        /**
         * @brief Read the list of entry names from this Directory.
         *
         * @param filter The entries to filter out.
         * @return Result<Vector<String>> The list of entry names.
         */
        Result<Vector<String>> list_names(Filter filter);

        /**
         * @brief A directory entry with extra information.
         */
        struct Entry
        {
            String name;  // The entry's name.
            mode_t mode;  // The entry's file permissions.
            usize size;   // The entry's size.
            time_t mtime; // The entry's last modification time.
        };

        /**
         * @brief Read the list of entries from this Directory, fetching extra information for each one.
         *
         * @param filter The entries to filter out.
         * @return Result<Vector<Entry>> The list of entries.
         */
        Result<Vector<Entry>> list(Filter filter);

        /**
         * @brief Rewinds this Directory back to the beginning of its entry list.
         */
        void rewind();

        /**
         * @brief Returns the file descriptor associated with this Directory.
         *
         * @return int The file descriptor.
         */
        int fd()
        {
            return dirfd(m_dirp);
        }

        ~Directory();
        Directory(Badge<Directory>);

      private:
        DIR* m_dirp { nullptr };
    };
}