/******************************************************************************/
/* VIRTUAL FILE SYSTEM ********************************************************/
/******************************************************************************/

#ifndef _H_EBCL_VFS
#define _H_EBCL_VFS

#include <ebcl/Sets.hh>
#include <ebcl/Strings.hh>
#include <ebcl/Streams.hh>
#include <ebcl/Files.hh>
#include <ebcl/Registration.hh>
namespace ebcl {


/*= DRIVERS ==================================================================*/

class A_VFSDriver
{
   public:
	virtual ~A_VFSDriver( ) = 0;

	/*
	 * Initialisation and shutdown methods, called by the main VFS code.
	 * These do nothing by default.
	 */
	virtual bool init( );
	virtual void shutdown( );

	/*
	 * Note: the main VFS code will NEVER pass anything but an absolute
	 * path to the methods below.
	 */

	/* Get the type of an entry in the VFS */
	virtual E_FSEntryType typeOf( T_FSPath const& path ) = 0;

	/* Checks that a path exists */
	bool exists( T_FSPath const& path );

	/* List entries matching a VFS path. The method should return true if
	 * the path was found by the driver and was a directory, or false if
	 * it wasn't.
	 */
	virtual bool list( T_FSPath const& path ,
			T_Set< T_FSPath >& values ) = 0;

	/* Try reading from a VFS file. Returns an owning pointer that's either
	 * null if the path in question couldn't be found (or wasn't a file)
	 * or that refers to some unspecified input stream.
	 */
	virtual OP_InputStream read( T_FSPath const& path ) = 0;

	/* Try opening a path as an actual file. If the driver doesn't support
	 * this, or if it cannot open the file for whatever reason, this should
	 * return null (which is what the default implementation does).
	 */
	virtual OP_File file( T_FSPath const& path );
};
M_ABSTRACT_POINTERS( VFSDriver );


/*= VFS FILESYSTEM DRIVER ====================================================*/

/* A driver that allows the VFS to access a part of the actual filesystem. */
class T_VFSFilesystemDriver : public A_VFSDriver
{
   private:
	T_FSPath root_;

   public:
	T_VFSFilesystemDriver( ) = delete;
	T_VFSFilesystemDriver( T_VFSFilesystemDriver const& ) = delete;
	T_VFSFilesystemDriver( T_VFSFilesystemDriver&& ) noexcept = delete;

	explicit T_VFSFilesystemDriver( T_FSPath const& root );

	T_FSPath const& root( ) const;

	// ---------------------------------------------------------------------

	bool init( ) override;

	// ---------------------------------------------------------------------

	E_FSEntryType typeOf( T_FSPath const& path ) override;
	bool list( T_FSPath const& path , T_Set< T_FSPath >& values ) override;

	OP_InputStream read( T_FSPath const& path ) override;
	OP_File file( T_FSPath const& path ) override;

	// ---------------------------------------------------------------------

   protected:
	T_String getFullPath( T_FSPath const& path ) const;
	T_Buffer< char > getOSPath( T_FSPath const& path ) const;
};
M_CLASS_POINTERS( VFSFilesystemDriver );


/*= VFS CORE =================================================================*/

class X_VFSInitialisationFailure : public std::runtime_error
{
    public:
	X_VFSInitialisationFailure( );
};

class T_VFS : public A_PrivateImplementation
{
   public:
	// Initialise the VFS, using the specified RW directory
	T_VFS( T_FSPath const& userDir );

	T_VFS( ) = delete;
	T_VFS( T_VFS const& ) = delete;
	T_VFS( T_VFS&& ) = delete;

	// Add a new VFS driver
	T_RegisteredItem addDriver( OP_VFSDriver&& driver );
	template<
		typename DriverType ,
		typename... ArgTypes
		>
	T_RegisteredItem addDriver( ArgTypes&& ... arguments );

	// Read-only access methods.
	E_FSEntryType typeOf( T_FSPath const& path ) const;
	bool list( T_FSPath const& path , T_Array< T_FSPath >& output ) const;
	OP_InputStream read( T_FSPath const& path ) const;
	OP_File file( T_FSPath const& path ) const;

	// Read/write access for the user directory.
	OP_File file( T_FSPath const& path , E_FileMode mode ) const;
	OP_OutputStream write( T_FSPath const& path ) const;
	bool mkdir( T_FSPath const& path ) const;
	bool rmdir( T_FSPath const& path ) const;
	bool rm( T_FSPath const& path ) const;
	bool move( T_FSPath const& from ,
			T_FSPath const& to ) const;

	// Path style for the VFS
	static constexpr T_FSPathStyle Style( ) noexcept;
};
M_CLASS_POINTERS( VFS );


} // namespace
#endif // _H_EBCL_VFS
#include <ebcl/inline/VFS.hh>