///
/// \file Buffer.h
/// \author kyle
/// \date 2023-10-10
/// \brief A buffer is the basic document type.
///

#ifndef KEPP_FRAME_H
#define KEPP_FRAME_H


#include <cstdint>
#include <vector>

#include "Defs.h"
#include "File.h"
#include "Cursor.h"


typedef std::vector<std::vector<uint8_t>> BufferContents;


/// A Buffer is the atom of text editing. It represents a single document,
/// whether a memory-only buffer or a file-backed buffer.
///
/// \details
///
/// There are currently two main types of Buffers: file-backed and virtual.
/// A virtual buffer describes any buffer that isn't backed by a file.
///
/// A virtual buffer can be promoted to a file frame, but a file buffer
/// cannot be demoted to a virtual buffer.
class Buffer {
public:
	enum class FileStatus : uint8_t {
		/// The file operation succeeded correctly.
		FileStatusOK = 0,
		
		/// There was an error, but it wasn't handled correctly.
		/// This is mostly for debugging purposes, and shouldn't
		/// be seen by users.
		///
		/// \detail This is set at the beginning of failable
		///         operations, and indicates that the right
		///	    status wasn't set correctly.
		FileStatusUnspecifiedError = 1,

		/// The file can't be written to because it is marked
		/// read-only. This refers to a buffer being marked as
		/// read-only, not to whether the underlying file is
		/// actually read-only.
		FileStatusReadOnly = 2,

		/// There was an I/O error trying to write to the file.
		FileStatusIOFailed = 3,

		/// The Buffer couldn't be flushed because it is a virtual
		/// buffer. If the user explicitly tried to save the buffer,
		/// they should be prompted for a path.
		FileStatusVirtual = 4,

		/// The underlying file doesn't have the right permissions;
		/// for example, it's not writeable if the user is trying to
		/// write the file.
		FileStatusInvalidPermissions = 5,

		/// The underlying file doesn't exist.
		FileStatusNonExistent = 6,
		
	};

	static std::string FileStatusToString(FileStatus status);


	static bool StatusOK(FileStatus status)
	{ return status == FileStatus::FileStatusOK; }


	/// The constructor with no arguments generates a new anonymous
	/// buffer.
	Buffer();

	/// A single constructor generates a virtual buffer.
	Buffer(std::string fName);

	/// Instantiate a Buffer pointing to fPath.
	Buffer(std::string fName, std::string fPath);

	void Close();

	/// Instantiate a buffer from a filesystem path.
	Buffer(std::filesystem::path fPath);

	std::string Name() const
	{ return this->name; }

	bool IsVirtual()
	{ return !this->file.has_value(); }

	Buffer::FileStatus Flush(OptString altPath);

	/// Refresh reads the contents of the file back into the
	/// buffer.
	///
	/// \warning This does not care if the file is dirty or not -
	/// it WILL overwrite the contents of the buffer.
	///
	/// \return A FileStatus indicating whether the read was successful.
	Buffer::FileStatus Refresh();

	void ChangePath(std::string newPath);

	Cursor Where()
	{ return this->cursor; }

	void MarkDirty();
	bool IsDirty()
	{ return this->dirty; }

	size_t Size();

	/// Does this buffer have a current, on-disk file?
	///
	/// \return True if this is a file buffer with an extant source
	///         file.
	bool			Exists();

	/// PrintBufferStatus is a debug tool to capture the current
	/// buffer state.
	void			PrintBufferStatus(std::ostream &os);
private:
	void clearContents();

	Cursor		cursor;
	bool		dirty;
	std::string	name;
	OptString	path;
	OptFile		file;
	BufferContents	contents;
	FileStatus	status;
};


#endif // KEPP_FRAME_H

