/******************************************************************************/
/* FILESYSTEM ABSTRACTION *****************************************************/
/******************************************************************************/

#ifndef _H_EBCL_FILESYSTEM
#define _H_EBCL_FILESYSTEM

#include <ebcl/Strings.hh>
#include <ebcl/Types.hh>
namespace ebcl {


/*= FILESYSTEM PATH ==========================================================*/

// Supported filesystem path separators
enum class E_FSPathSeparator
{
	SLASH , BACKSLASH
};
using T_OptPathSeparator = T_Optional< E_FSPathSeparator >;

// The style of a filesystem path.
class T_FSPathStyle
{
    private:
	bool caseSensitive_;
	bool hasDriveLetter_;
	T_Flags< E_FSPathSeparator > pathSeparators_;

    public:
	//----------------------------------------------------------------------
	// Constructors and assignment operators

	T_FSPathStyle( ) = delete;

	// Initialise the filesystem path style, specifying:
	//	- whether it is case-sensitive,
	//	- whether is can use a drive letter as its root,
	//	- which separators it supports
	constexpr T_FSPathStyle( bool cs , bool hdl ,
			T_Flags< E_FSPathSeparator > ps ) noexcept;

	// Default copy/move constructors/assignement operators
	constexpr T_FSPathStyle( T_FSPathStyle const& ) noexcept = default;
	constexpr T_FSPathStyle& operator =( T_FSPathStyle const& ) noexcept = default;
	constexpr T_FSPathStyle( T_FSPathStyle&& ) noexcept = default;
	constexpr T_FSPathStyle& operator =( T_FSPathStyle&& ) noexcept = default;

	T_FSPathStyle& swap( T_FSPathStyle& other ) noexcept;

	//----------------------------------------------------------------------
	// Accessors

	constexpr bool caseSensitive( ) const noexcept;
	constexpr T_FSPathStyle& caseSensitive( bool cs ) noexcept;

	constexpr bool hasDriveLetter( ) const noexcept;
	constexpr T_FSPathStyle& hasDriveLetter( bool hdl ) noexcept;

	constexpr T_Flags< E_FSPathSeparator > pathSeparators( ) const noexcept;
	constexpr T_FSPathStyle& pathSeparator( T_Flags< E_FSPathSeparator > ps ) noexcept;

	//----------------------------------------------------------------------
	// Checks

	constexpr bool isSeparator( T_Character chr ) const noexcept;
	bool isValidRoot( T_String const& string ) const noexcept;

	//----------------------------------------------------------------------
	// Pre-initialised styles

	static constexpr T_FSPathStyle Unix( );
	static constexpr T_FSPathStyle Windows( );
	static constexpr T_FSPathStyle System( );
};
M_DECLARE_SWAP( T_FSPathStyle );


// Representation of a path
class T_FSPath
{
    public:
	using T_Components = T_AutoArray< T_String , 16 >;

    private:

	// Path style
	T_FSPathStyle style_;

	// Root will be '/' on Unix, or a drive letter on Windows. Relative
	// paths will have a blank root.
	T_String root_;	
	T_Components components_;

	// Is the path valid?
	bool valid_;

	// String comparison helpers
	using F_StrCmp_ = int (*)( T_String const& , T_String const& );
	F_StrCmp_ cmpFunction_( ) const noexcept;

    public:
	//----------------------------------------------------------------------
	// Helpers

	static bool IsValidComponent( T_String const& string ) noexcept;

	//----------------------------------------------------------------------
	// Basic constructors and assignment operators

	T_FSPath( T_FSPathStyle style = T_FSPathStyle::System( ) ) noexcept;

	T_FSPath( T_FSPath const& other ) noexcept;
	T_FSPath& operator =( T_FSPath const& other ) noexcept;

	T_FSPath( T_FSPath&& other ) noexcept;
	T_FSPath& operator =( T_FSPath&& other ) noexcept;

	T_FSPath& swap( T_FSPath& other ) noexcept;

	// Construct from a string
	T_FSPath( T_String const& path ,
			T_FSPathStyle style = T_FSPathStyle::System( ) ) noexcept;

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

	bool isValid( ) const noexcept;

	T_FSPathStyle const& style( ) const noexcept;

	T_String const& root( ) const noexcept;
	T_Components const& components( ) const noexcept;

	bool isRelative( ) const noexcept;
	bool isAbsolute( ) const noexcept;

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

	// Convert the path into a string
	T_String toString( T_OptPathSeparator separator = {} ) const noexcept;
	// Compute a hash value for the path
	uint32_t computeHash( ) const noexcept;
	// Append to a string builder
	void appendTo( T_StringBuilder& sb ,
			T_OptPathSeparator separator = {} ) const noexcept;

	// Comparisons. On Windows the comparison will be case-insensitive.
	int32_t compareTo( T_FSPath const& other ) const noexcept;
	bool operator ==( T_FSPath const& other ) const noexcept;
	bool operator !=( T_FSPath const& other ) const noexcept;
	bool operator <( T_FSPath const& other ) const noexcept;
	bool operator <=( T_FSPath const& other ) const noexcept;
	bool operator >( T_FSPath const& other ) const noexcept;
	bool operator >=( T_FSPath const& other ) const noexcept;

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

	// Returns the parent's path
	T_FSPath parent( ) const noexcept;

	// Returns the child's path
	T_FSPath child( T_String const& name ) const noexcept;

	// Appends the specified (relative) path to the current path and return
	// the result. If the path is absolute, the result will be invalid.
	T_FSPath operator +( T_FSPath const& other ) const noexcept;

	// Checks if the current path and the specified path have the same
	// parent and are different.
	bool inDirectoryOf( T_FSPath const& other ) const noexcept;

	// Checks if the current path is the direct parent of the specified path
	bool isParentOf( T_FSPath const& other ) const noexcept;
	// Checks if the current path is the direct child of the specified path
	bool isChildOf( T_FSPath const& other ) const noexcept;

	// Checks if the current path is a parent of the specified path
	bool isAbove( T_FSPath const& other ) const noexcept;
	// Checks if the current path is a child of the specified path
	bool isUnder( T_FSPath const& other ) const noexcept;

	// Create a relative path based on the current path and a "parent" path.
	// Both paths must be canonical and valid. If they have different roots,
	// a copy of the current path will be returned.
	T_FSPath makeRelative( T_FSPath const& relTo ) const noexcept;

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

	// Checks whether the specified path is canonical
	bool isCanonical( ) const noexcept;
	// Return a canonical path from the current path
	T_FSPath canonical( ) const noexcept;
};
M_DECLARE_SWAP( T_FSPath );
M_DECLARE_HASH( T_FSPath );
M_LSHIFT_OP( T_StringBuilder , T_FSPath const& );


/*= FILESYSTEM ===============================================================*/

class Filesystem final
{
	Filesystem( ) = delete;
    public:
	// Return the absolute path to the current working directory
	static T_FSPath Cwd( ) noexcept;

};


} // namespace ebcl
#endif // _H_EBCL_FILESYSTEM
#include <ebcl/inline/Filesystem.hh>