/* +------------------------------------------------------------------------+
   |                     Mobile Robot Programming Toolkit (MRPT)            |
   |                          https://www.mrpt.org/                         |
   |                                                                        |
   | Copyright (c) 2005-2022, Individual contributors, see AUTHORS file     |
   | See: https://www.mrpt.org/Authors - All rights reserved.               |
   | Released under BSD License. See: https://www.mrpt.org/License          |
   +------------------------------------------------------------------------+ */
#pragma once

#include <mrpt/system/os.h>

#include <mutex>
#include <queue>
#include <thread>

namespace mrpt::system
{
/** This class subscribes to notifications of file system changes, thus it can
 * be used to efficiently stay informed about changes in a directory tree.
 *  - Windows: Requires Windows 2000 or newer.
 *  - Linux: Requires kernel 2.6.13 or newer.
 *  Using this class in an old Linux or other unsoported system (Unix,etc...)
 * has no effect, i.e. no notification will be ever received.
 *  \sa CDirectoryExplorer
 * \ingroup mrpt_system_grp
 */
class CFileSystemWatcher
{
   public:
	/** Each of the changes detected by mrpt::system::CFileSystemWatcher
	 */
	struct TFileSystemChange
	{
		TFileSystemChange() = default;
		/** Complete path of the file/directory that has changed. */
		std::string path{};
		/** Whether the event happened to a file or a directory. */
		bool isDir{false};
		bool eventModified{false};
		bool eventCloseWrite{false};
		bool eventDeleted{false};
		bool eventMovedTo{false};
		bool eventMovedFrom{false};
		bool eventCreated{false};
		bool eventAccessed{false};
	};

	using TFileSystemChangeList = std::deque<TFileSystemChange>;

	/** Creates the subscription to a specified path.
	 * \param path The file or directory to watch.
	 */
	CFileSystemWatcher(const std::string& path);

	/** Destructor
	 */
	virtual ~CFileSystemWatcher();

	/** Call this method sometimes to get the list of changes in the watched
	 * directory.
	 *  \sa processChange
	 */
	void getChanges(TFileSystemChangeList& out_list);

   private:
	/** Ended in "/" */
	std::string m_watchedDirectory;
#ifdef _WIN32
	void* m_hNotif{nullptr};
	std::thread m_watchThread;
	/** Watch thread; only needed in win32 */
	void thread_win32_watch();
	std::queue<TFileSystemChange*> m_queue_events_win32_msgs;
	mutable std::mutex m_queue_events_win32_cs;
#endif

#if defined(MRPT_OS_LINUX) || defined(__APPLE__)
	/** The fd returned by inotify_init. */
	int m_fd;
	/** The fd of the watch. */
	int m_wd;
#endif

};	// End of class def.

}  // namespace mrpt::system
