/******************************************************************************/
/* FILES **********************************************************************/
/******************************************************************************/

#ifndef _H_EBCL_FILES
#define _H_EBCL_FILES
#include <ebcl/Filesystem.hh>
#include <ebcl/Streams.hh>
namespace ebcl {


/*= FILE ACCESS ==============================================================*/

enum class E_FileMode {
	READ_ONLY ,
	READ_WRITE ,
	OVERWRITE
};


class T_File final
{
   private:
	T_FSPath path_;
	E_FileMode mode_;
	FILE* file_;
	size_t size_ , pos_;

	T_File( );

   public:
	// Construct from a file path. Does not open the file.
	T_File( T_FSPath const& path , E_FileMode mode );

	// Move constructor and assignment
	T_File( T_File&& other ) noexcept;
	T_File& operator= ( T_File&& other ) noexcept;

	// Disabled copy constructor / assignment operator
	T_File( T_File const& ) = delete;
	T_File& operator= ( T_File const& ) = delete;

	// Destructor - must close the file if it is open
	~T_File( );

	// Swapping
	friend void swap( T_File& lhs , T_File& rhs ) noexcept;

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

	// Get the path
	T_FSPath const& path( ) const noexcept;
	// Get the mode
	E_FileMode mode( ) const noexcept;

	// Open the file. Throws an exception if that fails. Will be called
	// automatically by most methods.
	virtual void open( );

	// Close the file. Never throws, even if the file's already closed or
	// if closing fails.
	void close( ) noexcept;

	// Check if the file is open
	bool isOpen( ) const noexcept;

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

	// Get the file's size
	size_t size( ) const noexcept;
	// Get the current file position
	size_t position( ) const noexcept;

	// Set the position relative to the start or the end of the file
	void position( size_t position , bool fromEnd = false );
	// Set the position relative to the current position
	void move( ssize_t offset );

	// Read/write methods
	size_t read( void* data , size_t size );
	size_t write( void const* data , size_t size );

	// Flushing
	void flush( );
};


M_CLASS_POINTERS( File );


/*= FILE STREAMS =============================================================*/

class T_FileInputStream final : public A_InputStream
{
   protected:
	RP_File fileRaw_;
	OP_File fileOwned_;
	size_t start_;

   public:
	T_FileInputStream( ) = delete;

	// Construct from a file instance
	explicit T_FileInputStream( T_File& file , ssize_t offset = 0 ,
			size_t limit = SIZE_MAX );
	// Construct from a file owning pointer
	explicit T_FileInputStream( OP_File&& file , ssize_t offset = 0 ,
			size_t limit = SIZE_MAX );

	// Copy constructor and assignment
	T_FileInputStream( T_FileInputStream const& );
	T_FileInputStream& operator= ( T_FileInputStream const& );

	// Move constructor and assignment
	T_FileInputStream( T_FileInputStream&& other ) noexcept;
	T_FileInputStream& operator= ( T_FileInputStream&& other ) noexcept;

	// Swapping
	void swap( T_FileInputStream& rhs ) noexcept;

	// Get the file
	T_File& file( ) const noexcept;
	// Get the start offset
	size_t offset( ) const noexcept;

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

	size_t read( void* data , size_t size ) override;

	// -------------------------------------------------------------------
   private:
	void init( ssize_t offset , size_t limit );
};


M_CLASS_POINTERS( FileInputStream );

/*---------------------------------------------------------------------------*/

class T_FileOutputStream final : public A_OutputStream
{
   protected:
	RP_File fileRaw_;
	OP_File fileOwned_;
	size_t start_;

   public:
	T_FileOutputStream( ) = delete;

	// Construct from a file.
	explicit T_FileOutputStream( T_File& file , ssize_t offset = 0 );
	// Construct from a file owning pointer
	explicit T_FileOutputStream( OP_File&& file , ssize_t offset = 0 );

	// Copy constructor and assignment
	T_FileOutputStream( T_FileOutputStream const& );
	T_FileOutputStream& operator= ( T_FileOutputStream const& );

	// Move constructor and assignment
	T_FileOutputStream( T_FileOutputStream&& other ) noexcept;
	T_FileOutputStream& operator= ( T_FileOutputStream&& other ) noexcept;

	// Swapping
	void swap( T_FileOutputStream& rhs ) noexcept;

	// Get the file
	T_File& file( ) const noexcept;
	// Get the start offset
	size_t offset( ) const noexcept;

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

	size_t write( void const* data , size_t size ) override;
	void flush( ) override;

	// -------------------------------------------------------------------
   private:
	void init( ssize_t offset );
};


M_CLASS_POINTERS( FileOutputStream );


} // namespace ebcl
#endif // _H_EBCL_FILES
#include <ebcl/inline/Files.hh>