// vim:fileencoding=utf-8:foldmethod=marker

#include "file.h"
#include "../cpath/cpath.h"
#include "../../common/assert/assert.h"
#include "../../common/aliases/aliases.h"
#include "../../base/array/array.h"
#include "../../base/strings/str8/str8.h"

WpFile *wpFileOpen(const WpAllocator *allocator, WpStr8RO *filepath, WpFileAccessMode mode) {
	wpDebugAssert(allocator != NULL && filepath != NULL, "`allocator` and `filepath` should not be NULL");
	wpDebugAssert(filepath->size < WP_PATH_MAX, "`filepath` exceeds max path limit.");
	return _fileOpen(allocator, filepath, mode);
}

i64 wpFileGetCurrentPosition(WpFile *file) {
	wpDebugAssert(file != NULL, "`file` should not be NULL.");
	return _fileSeek(file, 0, WP_SEEK_CURRENT);
}

i64 wpFileSeek(WpFile *file, i64 offset, WpFileSeekOrigin origin) {
	wpDebugAssert(file != NULL, "`file` should not be NULL.");
	return _fileSeek(file, offset, origin);
}

i64 wpFileGetLength(WpFile *file) {
	wpDebugAssert(file != NULL, "`file` should not be NULL.");

	i64 current = wpFileGetCurrentPosition(file);

	_fileSeek(file, 0, WP_SEEK_END);

	i64 output = wpFileGetCurrentPosition(file);

	// Restore position
	_fileSeek(file, current, WP_SEEK_START);

	return output;
}

u64 wpFileRead(void *dst_buf, WpFile *file, u64 byte_count) {
	wpDebugAssert(dst_buf != NULL && file != NULL,
	                  "`dst_buf` and `file` should not be NULL.");

	i64 file_length = wpFileGetLength(file);
	if (file_length < 0) {
		return 0;
	}

	return _fileRead(dst_buf, byte_count, file, file_length);
}

i64 wpFileWrite(const void *src_buf, WpFile *file, u64 byte_count) {
	wpDebugAssert(src_buf != NULL && file != NULL,
	                  "`src_buf` and `file` should not be NULL.");
	return _fileWrite(src_buf, file, byte_count);
}

u64 wpFileReadStr8(WpStr8 *str, WpFile *file) {
	wpDebugAssert(str != NULL, "`str` should not be NULL.");
	return wpFileRead((void *)(str->buf), file, str->size);
}

i64 wpFileWriteStr8(WpStr8RO *str, WpFile *file) {
	wpDebugAssert(str != NULL, "`str` should not be NULL.");
	return wpFileWrite((void *)(str->buf), file, str->size);
}

u64 wpFileReadArray(WpArray dst_buf, WpFile *file, u64 item_count) {
	wpDebugAssert(dst_buf != NULL && file != NULL,
	                  "`dst_buf` and `file` should not be NULL.");

	i64 _file_length = wpFileGetLength(file);
	if (_file_length < 0) {
		return 0;
	}

	u64 file_length       = (u64)_file_length;
	u64 item_size         = wpArrayItemSize(dst_buf);
	u64 dst_byte_capacity = wpArrayCapacity(dst_buf) * item_size;
	u64 req_byte_count    = item_count * item_size;
	u64 copy_byte_count   = 0;

	if (req_byte_count <= file_length && req_byte_count <= dst_byte_capacity) {
		copy_byte_count = req_byte_count;
	} else {
		copy_byte_count = file_length <= dst_byte_capacity ? file_length : dst_byte_capacity;
	}

	u64 byte_count = _fileRead(dst_buf, copy_byte_count, file, file_length);
	if (byte_count == 0) {
		return 0;
	}

	wpArraySetCount(dst_buf, byte_count / item_size);

	return wpArrayCount(dst_buf);
}

i64 wpFileWriteArray(const WpArray src_buf, WpFile *file, u64 item_count) {
	wpDebugAssert(src_buf != NULL && file != NULL,
	                  "`src_buf` and `file` should not be NULL.");

	u64 item_size      = wpArrayItemSize(src_buf);
	u64 src_byte_count = wpArrayCount(src_buf) * item_size;
	u64 req_byte_count = item_count * item_size;
	u64 to_copy        = req_byte_count <= src_byte_count ? req_byte_count : src_byte_count;

	i64 bytes_written = _fileWrite(src_buf, file, to_copy);
	if (bytes_written < 0) {
		return 0;
	}

	return (u64)bytes_written / item_size;
}

i32 wpFileFlush(WpFile *file) {
	wpDebugAssert(file != NULL, "`file` should not be NULL.");
	return _fileFlush(file);
}

i32 wpFileClose(WpFile *file) {
	wpDebugAssert(file != NULL, "`file` should not be NULL.");
	return _fileClose(file);
}

i32 wpFileRename(WpStr8RO *old_filepath, WpStr8RO *new_filepath) {
	wpDebugAssert(old_filepath != NULL && new_filepath != NULL,
	                  "`old_filepath` and `new_filepath` should not be NULL");
	wpDebugAssert(old_filepath->size < WP_PATH_MAX, "`old_filepath` exceeds max path limit.");
	wpDebugAssert(new_filepath->size < WP_PATH_MAX, "`new_filepath` exceeds max path limit.");
	return _fileRename(old_filepath, new_filepath);
}

i32 wpFileRemove(WpStr8RO *filepath) {
	wpDebugAssert(filepath != NULL, "`filepath` should not be NULL");
	wpDebugAssert(filepath->size < WP_PATH_MAX, "`filepath` exceeds max path limit.");
	return _fileRemove(filepath);
}
