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

#include "file_posix.h"
#include "../../../common/platform/platform.h"

#ifdef WP_PLATFORM_POSIX

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

#ifdef WP_PLATFORM_APPLE
#define _FILE_OFFSET_BITS 64
#define lseek64 lseek
#endif // !WP_PLATFORM_APPLE

#define __USE_LARGEFILE64
#include <fcntl.h>
#include <unistd.h>
#include <sys/types.h>

wp_intern i32 file_flags[COUNT_FILE_ACCESS_MODE] = {
	[WP_ACCESS_READ]                       = O_RDONLY,
	[WP_ACCESS_WRITE]                      = O_WRONLY | O_CREAT,
	[WP_ACCESS_APPEND]                     = O_WRONLY | O_APPEND | O_CREAT,
	[WP_ACCESS_READ_EX]                    = O_RDWR,
	[WP_ACCESS_WRITE_EX]                   = O_RDWR | O_CREAT,
	[WP_ACCESS_APPEND_EX]                  = O_RDWR | O_APPEND | O_CREAT,
	[WP_ACCESS_WRITE_FAIL_ON_EXIST]        = O_WRONLY | O_CREAT | O_EXCL,
	[WP_ACCESS_WRITE_FAIL_ON_EXIST_EX]     = O_RDWR | O_CREAT | O_EXCL,
};

wp_intern mode_t file_modes[COUNT_FILE_ACCESS_MODE] = {
	[WP_ACCESS_READ]                       = 0,
	[WP_ACCESS_WRITE]                      = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP,
	[WP_ACCESS_APPEND]                     = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP,
	[WP_ACCESS_READ_EX]                    = 0,
	[WP_ACCESS_WRITE_EX]                   = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP,
	[WP_ACCESS_APPEND_EX]                  = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP,
	[WP_ACCESS_WRITE_FAIL_ON_EXIST]        = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP,
	[WP_ACCESS_WRITE_FAIL_ON_EXIST_EX]     = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP,
};

wp_intern i32 file_seek_origins[COUNT_FILE_SEEK_ORIGIN] = {
	[WP_SEEK_START]   = SEEK_SET,
	[WP_SEEK_CURRENT] = SEEK_CUR,
	[WP_SEEK_END]     = SEEK_END,
};

WpFile *wpFileStdin(void) {
	wp_persist WpFile _stdin  = { .fd = STDIN_FILENO };
	return &_stdin;
}

WpFile *wpFileStdout(void) {
	wp_persist WpFile _stdout = { .fd = STDOUT_FILENO };
	return &_stdout;
}

WpFile *wpFileStderr(void) {
	wp_persist WpFile _stderr = { .fd = STDERR_FILENO };
	return &_stderr;
}

WpFile *_fileOpen(const WpAllocator *allocator, WpStr8RO *filepath, WpFileAccessMode mode) {
	wp_persist c8 tmp[WP_PATH_MAX] = {0};
	memset(tmp, 0, WP_PATH_MAX);
	memcpy(tmp, filepath->buf, filepath->size);

	i32 fd = open((const char *)tmp, file_flags[mode], file_modes[mode]);
	if (fd < 0) {
		return NULL;
	}

	WpFile *output = wpMemAllocatorAlloc(allocator, sizeof(WpFile));
	if (output) {
		output->fd = fd;
	}

	return output;
}

i64 _fileSeek(WpFile *file, i64 offset, WpFileSeekOrigin origin) {
	return lseek64(file->fd, offset, file_seek_origins[origin]);
}

u64 _fileRead(void *dst_buf, u64 byte_count, WpFile *file, u64 file_length) {
	u64 copy_byte_count = file_length <= byte_count ? file_length : byte_count;

	i64 count = read(file->fd, dst_buf, copy_byte_count);
	if (count < 0) { return 0; }

	return count;
}

i64 _fileWrite(const void *src_buf, WpFile *file, u64 byte_count) {
	return write(file->fd, src_buf, byte_count);
}

i32 _fileFlush(WpFile *file) {
	return fsync(file->fd);
}

i32 _fileClose(WpFile *file) {
	return close(file->fd);
}

i32 _fileRename(WpStr8RO *old_filepath, WpStr8RO *new_filepath) {
	wp_persist c8 old_tmp[WP_PATH_MAX] = {0};
	wp_persist c8 new_tmp[WP_PATH_MAX] = {0};
	memset(old_tmp, 0, WP_PATH_MAX);
	memcpy(old_tmp, old_filepath->buf, old_filepath->size);
	memset(new_tmp, 0, WP_PATH_MAX);
	memcpy(new_tmp, new_filepath->buf, new_filepath->size);

	i32 link_result = link((const char *)old_tmp, (const char *)new_tmp);
	if (link_result == 0) {
		_fileRemove(old_filepath);
	}

	return link_result;
}

i32 _fileRemove(WpStr8RO *filepath) {
	wp_persist c8 tmp[WP_PATH_MAX] = {0};
	memset(tmp, 0, WP_PATH_MAX);
	memcpy(tmp, filepath->buf, filepath->size);

	return unlink((const char *)tmp);
}

#endif // !WP_PLATFORM_POSIX
