#pragma once

#include "ehs/EHS.h"
#include "ehs/Str.h"
#include "ehs/Vector.h"
#include "ehs/Array.h"
#include "ehs/Serializer.h"

namespace ehs
{
	enum class Mode
	{
		READ,
		WRITE,
		READ_WRITE
	};

	enum class Disposition
	{
		CREATE_PERSISTENT,
		CREATE,
		OPEN_PERSISTENT,
		OPEN,
		TRUNCATE
	};

	/// A cross-platform wrapper class that handles native file input/output.
	class EHS_LIB_IO BaseFile
	{
	protected:
		Str_8 path;
		Str_8 fullName;
		Str_8 name;
		Str_8 extension;
		Mode mode;
		Disposition disposition;

	public:
		/// Frees all native handles.
		virtual ~BaseFile() = default;

		/// Default members initialization.
		BaseFile();

		/// Initializes members with the given data.
		/// @param [in] filePath The file path to read or write to.
		/// @param [in] mode The mode when accessing the file.
		/// @param [in] disposition How to handle the file.
		BaseFile(const Str_8& filePath, const Mode mode, const Disposition disposition);

		BaseFile(BaseFile&& file) noexcept;

		/// Copy constructor.
		/// @param [in] file The file object to copy from.
		BaseFile(const BaseFile& file) = default;

		BaseFile& operator=(BaseFile&& file) noexcept;

		/// Copy operator.
		/// @param [in] file The file object to copy from.
		BaseFile& operator=(const BaseFile& file) = default;

		virtual operator const Byte*() const = 0;

		virtual operator Byte*() = 0;

		/// Uninitializes the native handle.
		/// @param [in] raiseLog Whether or not to raise a log if already uninitialized. Mostly for deconstructor.
		virtual void Release() = 0;

		virtual bool IsMapped() const = 0;

		virtual UInt_64 MapSize() const = 0;

		virtual void Map(const UInt_64 offset, const UInt_64 size) = 0;

		virtual void Unmap() = 0;

		virtual void FlushMap() = 0;

		/// Writes a C-style byte array to the file.
		/// @param [in] data The C-style byte array to write to the file.
		/// @param [in] size The size of the given C-style byte array.
		virtual UInt_64 Write(const Byte* const data, const UInt_64 size) = 0;

		/// Writes a C-style string to the file.
		/// @tparam T The character data type to use.
		/// @param [in] str The C-style string to write to the file.
		/// @param [in] size The size of the given C-style string.
		void WriteStr_32(const Char_32* const str, const UInt_64 size);

		/// Writes a string to the file.
		/// @tparam T The character data type to use.
		/// @tparam N The data type to use for numbers.
		/// @param [in] str The string to write to the file.
		void WriteStr_32(const Str_32& str);

		/// Writes a C-style string to the file.
		/// @tparam T The character data type to use.
		/// @param [in] str The C-style string to write to the file.
		/// @param [in] size The size of the given C-style string.
		void WriteStr_16(const Char_16* const str, const UInt_64 size);

		/// Writes a string to the file.
		/// @tparam T The character data type to use.
		/// @tparam N The data type to use for numbers.
		/// @param [in] str The string to write to the file.
		void WriteStr_16(const Str_16& str);

		/// Writes a C-style string to the file.
		/// @tparam T The character data type to use.
		/// @param [in] str The C-style string to write to the file.
		/// @param [in] size The size of the given C-style string.
		void WriteStr_8(const Char_8* const str, const UInt_64 size);

		/// Writes a string to the file.
		/// @tparam T The character data type to use.
		/// @tparam N The data type to use for numbers.
		/// @param [in] str The string to write to the file.
		void WriteStr_8(const Str_8& str);

		/// Writes a vector to the file.
		/// @tparam N The data type to use for numbers.
		/// @param [in] vec The vector to write to the file.
		void WriteVector(const Vector<Byte, UInt_64>& vec);

		/// Writes an array to the file.
		/// @tparam N The data type to use for numbers.
		/// @param [in] arr The array to write to the file.
		void WriteArray(const Array<Byte, UInt_64>& arr);

		/// Writes a serializer to the file.
		/// @tparam N The data type to use for numbers.
		/// @param [in] ser The serializer to write to the file.
		void WriteSerializer_64(const Serializer<UInt_64>& ser);

		/// Writes a serializer to the file.
		/// @tparam N The data type to use for numbers.
		/// @param [in] ser The serializer to write to the file.
		void WriteSerializer_32(const Serializer<UInt_32>& ser);

		/// Reads data from the file as a C-style byte array.
		/// @param [out] buffer The buffer to store the data read from the file.
		/// @param [in] size The size of the given buffer and how much data to read.
		virtual UInt_64 Read(Byte* const buffer, const UInt_64 size) = 0;

		/// Reads data from the file as a C-style string.
		/// @tparam T The character data type to use.
		/// @param [out] buffer The buffer to store the data read from the file.
		/// @param [in] size The size of the given buffer and how much data to read.
		void ReadStr_32(Char_32* const buffer, UInt_64& size);

		/// Reads data from the file as a string.
		/// @tparam T The character data type to use.
		/// @tparam N The data type to use for numbers.
		/// @param [in] size The size of the buffer and how much data to read.
		/// @returns The resulting string.
		Str_32 ReadStr_32(const UInt_64 size);

		/// Reads data from the file as a C-style string.
		/// @tparam T The character data type to use.
		/// @param [out] buffer The buffer to store the data read from the file.
		/// @param [in] size The size of the given buffer and how much data to read.
		void ReadStr_16(Char_16* const buffer, UInt_64& size);

		/// Reads data from the file as a string.
		/// @tparam T The character data type to use.
		/// @tparam N The data type to use for numbers.
		/// @param [in] size The size of the buffer and how much data to read.
		/// @returns The resulting string.
		Str_16 ReadStr_16(const UInt_64 size);

		/// Reads data from the file as a C-style string.
		/// @tparam T The character data type to use.
		/// @param [out] buffer The buffer to store the data read from the file.
		/// @param [in] size The size of the given buffer and how much data to read.
		void ReadStr_8(Char_8* const buffer, UInt_64& size);

		/// Reads data from the file as a string.
		/// @tparam T The character data type to use.
		/// @tparam N The data type to use for numbers.
		/// @param [in] size The size of the buffer and how much data to read.
		/// @returns The resulting string.
		Str_8 ReadStr_8(const UInt_64 size);

		/// Reads data from the file as a vector.
		/// @tparam N The data type to use for numbers.
		/// @param [in] size The size of the buffer and how much data to read.
		/// @returns The resulting vector.
		Vector<Byte, UInt_64> ReadVector(const UInt_64 size);

		/// Reads data from the file as an array.
		/// @tparam N The data type to use for numbers.
		/// @param [in] size The size of the buffer and how much data to read.
		/// @returns The resulting array.
		Array<Byte, UInt_64> ReadArray(const UInt_64 size);

		/// Reads data from the file as a serializer.
		/// @tparam N The data type to use for numbers.
		/// @param[in] end The Endianness of the data in the file.
		/// @param[in] size The size of the buffer and how much data to read.
		/// @returns The resulting serializer.
		Serializer<UInt_64> ReadSerializer_64(const Endianness end, const UInt_64 size);

		/// Reads data from the file as a serializer.
		/// @tparam N The data type to use for numbers.
		/// @param[in] end The Endianness of the data in the file.
		/// @param[in] size The size of the buffer and how much data to read.
		/// @returns The resulting serializer.
		Serializer<UInt_32> ReadSerializer_32(const Endianness end, const UInt_32 size);

		virtual void Seek(UInt_64 index) = 0;

		virtual void SeekBeginning() = 0;

		virtual void SeekEnd() = 0;

		virtual void Truncate(const UInt_64 size) = 0;

		/// Retrieves the size of the file.
		/// @returns The result.
		virtual UInt_64 Size() const = 0;

		Str_8 GetPath() const;

		Str_8 GetFullName() const;

		Str_8 GetName() const;

		Str_8 GetExtension() const;

		/// Retrieves whether or not this object is valid.
		/// @returns The result.
		virtual bool IsValid() const = 0;

		static void Rename_32(const Str_32& filePath, const Str_32& newName);

		static void Rename_16(const Str_16& filePath, const Str_16& newName);

		static void Rename_8(const Str_8& filePath, const Str_8& newName);

		static Str_32 ParseFullName_32(const Str_32& filePath);

		static Str_16 ParseFullName_16(const Str_16& filePath);

		static Str_8 ParseFullName_8(const Str_8& filePath);

		static Str_32 ParseName_32(const Str_32& filePath);

		static Str_16 ParseName_16(const Str_16& filePath);

		static Str_8 ParseName_8(const Str_8& filePath);

		static Str_32 ParseExt_32(const Str_32& filePath);

		static Str_16 ParseExt_16(const Str_16& filePath);

		static Str_8 ParseExt_8(const Str_8& filePath);
	};
}