// SPDX-License-Identifier: MIT
// List of images.
// Copyright (C) 2022 Artem Senichev <artemsen@gmail.com>

#pragma once

#include "image.hpp"

#include <ctime>
#include <filesystem>
#include <shared_mutex>
#include <vector>

/** Thread-safe list of images. */
class ImageList {
public:
    /** Order of the image list. */
    enum class Order : uint8_t {
        None,    ///< Unsorted (system depended)
        Alpha,   ///< Lexicographic sort
        Numeric, ///< Numeric sort
        Mtime,   ///< Modification time sort
        Size,    ///< Size sort
        Random   ///< Random order
    };

    /** Direction of the next entry. */
    enum class Dir : uint8_t {
        First,      ///< First entry in the list
        Last,       ///< Last entry in the list
        Next,       ///< Next entry
        Prev,       ///< Previous entry
        NextParent, ///< Next entry with different parent
        PrevParent, ///< Previous entry with different parent
        Random      ///< Random entry
    };

    /**
     * Get global instance of image list.
     * @return image list instance
     */
    static ImageList& self();

    /** Constructor. */
    ImageList();

    /**
     * Add all images in specified sources.
     * @param sources list of sources to load
     * @return list of added entries in source order
     */
    std::vector<ImageEntryPtr>
    add(const std::vector<std::filesystem::path>& sources);

    /**
     * Remove all given paths from the list.
     * @param sources entries paths to remove
     * @return list of removed entries
     */
    std::vector<ImageEntryPtr>
    remove(const std::vector<std::filesystem::path>& sources);

    /**
     * Remove image entry from the list.
     * @param entry entry to remove
     * @param forward direction of nearest returned entry
     * @return valid nearest entry or nullptr if list is empty
     */
    ImageEntryPtr remove(const ImageEntryPtr& entry, const bool forward = true);

    /**
     * Clear image list.
     * @return list of removed entries
     */
    std::vector<ImageEntryPtr> clear();

    /**
     * Get number of entries in the image list.
     * @return image list size
     */
    size_t size();

    /**
     * Get image list order.
     * @return order of the image list
     */
    Order get_order() const { return order; }

    /**
     * Set order and sort the image list.
     * @param new_order new order of the image list
     */
    void set_order(const Order new_order);

    /**
     * Enable/disable reverse order.
     * @param enable state to set
     */
    void set_reverse(const bool enable);

    /**
     * Find image entry by source path.
     * @param path path to the file or special source
     * @return image entry or nullptr if entry not found
     */
    ImageEntryPtr find(const std::filesystem::path& path);

    /**
     * Get array with copy of all entries in the list.
     * @return array with all entries
     */
    std::vector<ImageEntryPtr> get_all();

    /**
     * Get next entry from the list.
     * @param from starting image entry
     * @param dir next entry direction
     * @return image entry or nullptr if entry not found
     */
    ImageEntryPtr get(const ImageEntryPtr& from, const Dir dir);

    /**
     * Get image entry in specified distance from the start.
     * @param from starting image entry
     * @param distance number entries to skip
     * @return image entry or nullptr if distance if out of bounds
     */
    ImageEntryPtr get(const ImageEntryPtr& from, const ssize_t distance);

    /**
     * Get distance between two image entries.
     * @param from,to image entries
     * @return number of entries in range [start,end]
     */
    ssize_t distance(const ImageEntryPtr& from, const ImageEntryPtr& to);

private:
    /**
     * Get child entries by directory path.
     * @param path parent directory
     * @return list of entries
     */
    std::vector<ImageEntryPtr>
    get_child(const std::filesystem::path& path) const;

    /**
     * Get the nearest entry with different parent.
     * @param from starting image entry
     * @param forward direction of nearest returned entry
     * @return image entry or nullptr if entry not found
     */
    ImageEntryPtr get_diffparent(const ImageEntryPtr& from, const bool forward);

    /**
     * Add file or special source to the list.
     * @param path path to the file or special source
     * @return list of added entries
     */
    std::vector<ImageEntryPtr> add(const std::filesystem::path& path,
                                   const bool ordered);

    /**
     * Add files from the directory to the list.
     * @param path path to the directory
     * @return list with added entries
     */
    std::vector<ImageEntryPtr> add_dir(const std::filesystem::path& path);

    /**
     * Add file to the list.
     * @param path path to the file
     * @param ordered flag to add new entry to ordered position in the list
     * @return added entry
     */
    ImageEntryPtr add_file(const std::filesystem::path& path,
                           const bool ordered);

    /**
     * Add a special source (stdin://, exec://) to the list.
     * @param path path representing the special source
     * @param ordered flag to add new entry to ordered position in the list
     * @return added entry (nullptr if already exists)
     */
    ImageEntryPtr add_special(const std::filesystem::path& path,
                              const bool ordered);

    /**
     * Add new entry to the list.
     * @param entry image entry to add
     * @param ordered flag to add new entry to ordered position in the list
     * @return false if entry already exists
     */
    bool add_entry(const ImageEntryPtr& entry, const bool ordered);

    /**
     * Sort image list.
     */
    void sort();

    /**
     * Reindex the image list.
     * @param index (0-based) to start reindexing from
     */
    void reindex(const size_t index = 0);

private:
    using EntriesArray = std::vector<ImageEntryPtr>;
    using EntriesMap = std::unordered_map<std::filesystem::path, ImageEntryPtr>;
    EntriesArray entries_arr; ///< Array of image entries
    EntriesMap entries_map;   ///< Map of path to image entries

    std::shared_mutex mutex; ///< Image list mutex

    Order order;  ///< Image list order
    bool reverse; ///< Reverse order flag

public:
    bool recursive; ///< Read directories recursively
    bool adjacent;  ///< Open adjacent files from the same directory
    bool fsmon;     ///< FS monitor usage
};
