
#ifndef SPECTRE_SYSTEM_FILE_H
#define SPECTRE_SYSTEM_FILE_H

#include <stdio.h>
#include <string>
#include <system_error>

namespace sp {

class File
{
public :

	enum struct Access {
		READ  		= 1 << 0,
		WRITE 		= 1 << 1,
		READ_WRITE 	= READ | WRITE,
	};

	// NOTE: These are ignored atm. as implementation uses stdio.h
	enum OpenMode {
		// Only open if file exists and
		// in overwrite mode if write is enabled.
		DEFAULT		= 0,
		// Create file if it does not exist (ignored in read-only mode)
		CREATE		= 1 << 0,
		// If file exists any write calls will
		// append to the file. (ignored in read-only mode)
		APPEND		= 1 << 1,
		// If file exists it's content are discarded
		// resulting in an empty file. (ignored in read-only mode)
		TRUNCATE 	= 1 << 2
	};

// Methods.
public :
	File();

	// see `open()`
	File(const std::string& filename,
		Access access = Access::READ,
		unsigned int mode = DEFAULT);

	~File();

	// Opens a file (default in read-only)
	bool open(const std::string& filename,
		Access access = Access::READ,
		unsigned int mode = DEFAULT);

	// Returns true if the file is open, false otherwise.
	bool isOpen() const;

	// Close the file.
	void close();

	std::string getErrorMessage() const;

	// Get the current position in the file.
	size_t pos();

	// Set the file stream position.
	// if `from_end` is true. `offset` will be applied from the end of file, otherwise from beginning.
	// Returns true if the position was updated, false otherwise.
	bool set(long int pos = 0, bool from_end = false);

	// Move the current file stream position with `offset` bytes.
	// This is equivalent to calling set(pos() + offset)
	// Returns true if the position was updated, false otherwise.
	bool seek(long int offset);

	// Get the size of the file (in bytes)
	size_t size() const;

	// Read `size` data from file at the current position.
	// Content is stored in `ptr` and must be alteast `size` bytes.
	// Returns -1 if an error occured. Otherwice the number of bytes read is returned.
	size_t read(void *ptr, size_t size);

	// Read all remaining data from file at the current position. Content is stored in `buffer`.
	// Returns true if exactly all data could be read from file. false otherwise.
	template <typename buffer_t>
	inline bool read(buffer_t& buffer)
	{
		size_t s = size();
		buffer.resize(s);
		return read((void*) &buffer[0], s) == s;
	}

	// Read contents into str until null (\0)
	size_t readString(std::string &str);

	// Write data to file at the current position.
	// Returns -1 if an error occured. Otherwice the number of bytes written is returned.
	size_t write(const void *ptr, size_t size);

	// Write data to file at the current position.
	// Returns true if exactly all data could be written from `buffer`. false otherwise.
	template <typename buffer_t>
	inline bool write(const buffer_t& buffer)
	{
		return write((const void*) &buffer[0], buffer.size()) == buffer.size();
	}

	// Flush any buffered data to the file.
	bool flush();

private :

	FILE *m_handle;

	// Error
	mutable std::error_condition m_error;
};

} //namespace sp

#endif /* SPECTRE_SYSTEM_FILE_H */
