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

#include "cpath.h"
#include "../allocators/arena/mem_arena_allocator.h"
#include "../../common/aliases/aliases.h"
#include "../../common/misc/misc_utils.h"
#include "../../base/dbl_list/dbl_list.h"
#include "../../base/mem/allocator/mem_allocator.h"
#include "../../base/strings/str8/str8.h"
#include <stdarg.h>

u32 wpCpathJoinPath(WpStr8 *dst, const WpStr8List *parts) {
	if (!dst || !parts) {
		return WP_CPATH_JOIN_RESULT_INVALID_ARGS;
	}

	if (parts->node_count == 0) {
		return WP_CPATH_JOIN_RESULT_EMPTY_PARTS;
	}

	WpStr8 separator = wpStr8Buf(4);
	wpStr8PushBack(&separator, WP_PATH_SEP);

	u64 required_capacity = parts->node_count * separator.size + wpStr8ListTotalSize(parts);
	if (dst->capacity < required_capacity) {
		return WP_CPATH_JOIN_RESULT_INSUFFICIENT_DST_CAPACITY;
	}

	// Handle first node
	WpStr8 *first_node = wpDblListGet(WpStr8, parts, 0);
	wpStr8CopyStr8Capped(dst, first_node);

	// NOTE (Abdelrahman): Uses a while loop instead of a for loop to get rid of
	// MSVC Spectre mitigation warnings
	WpStr8 *node       = first_node;
	u64 node_index   = 1;
	b8 running       = node_index < parts->node_count;
	while (running) {
		node = wpDblListGet(WpStr8, parts, node_index);
		if (node->size == 0) {
			goto CPATH_JOIN_LOOP_END;
		}

		if (dst->size > 0) {
			char dst_last   = wpStr8Get(dst, dst->size - 1);
			char node_start = wpStr8Get(node, 0);
			b8 add_path_sep = dst_last != WP_PATH_SEP && node_start != WP_PATH_SEP;

			if (add_path_sep) {
				wpStr8ConcatCapped(dst, &separator);
			}
		}

		wpStr8ConcatCapped(dst, node);

CPATH_JOIN_LOOP_END:
		++node_index;
		running = node_index < parts->node_count;
	}

	return WP_CPATH_JOIN_RESULT_SUCCESS;
}

WpStr8 *_dirup(const WpAllocator *allocator, WpStr8RO *path, u64 levels) {
	WpStr8 *output = NULL;
	if (!allocator || !path) {
		goto RETURN_DIRUP;
	}

	b8 absolute    = wpStr8Get(path, 0) == WP_PATH_SEP;
	WpStr8 separator = wpStr8Buf(4);
	wpStr8PushBack(&separator, WP_PATH_SEP);

	if (path->size == 0) {
		output = wpStr8AllocBuf(allocator, 16);
		if (!output) {
			goto RETURN_DIRUP;
		}

		wpStr8PushBack(output, absolute ? WP_PATH_SEP : '.');
		goto RETURN_DIRUP;
	}

	if (levels < 1) {
		output = wpStr8AllocStr8(allocator, path);
		goto RETURN_DIRUP;
	}

	WpAllocator tmp_arena = wpMemArenaAllocatorInit(MiB(8));
	if (wpMemAllocatorInvalid(&tmp_arena)) {
		goto RETURN_DIRUP;
	}

	WpStr8List *parts = wpStr8Split(&tmp_arena, path, &separator);
	if (!parts) {
		goto RETURN_DIRUP;
	}

	if (levels >= parts->node_count) {
		output = wpStr8AllocBuf(allocator, 16);
		if (!output) {
			goto LIST_CLEANUP_DIRUP;
		}

		wpStr8PushBack(output, absolute ? WP_PATH_SEP : '.');
	} else {
		for (u64 i = 0; i < levels; ++i) {
			wpDblListPopBack(WpStr8, parts);
		}
		
		u64 alignment  = sizeof(void *) * 2;
		u64 alloc_size = wpStr8ListTotalSize(parts) + parts->node_count * separator.size;
		u64 modulo     = alloc_size & (alignment - 1);
		alloc_size    += alignment - modulo;

		output = wpStr8AllocBuf(allocator, alloc_size);
		if (output) {
			if (absolute) {
				wpStr8PushBack(output, WP_PATH_SEP);
			}

			WpStr8 *joined = wpStr8Join(&tmp_arena, parts, &separator);
			if (joined) {
				wpStr8ConcatCapped(output, joined);
			}
		}
	}

LIST_CLEANUP_DIRUP:
	wpMemArenaAllocatorDestroy(&tmp_arena);

RETURN_DIRUP:
	return output;
}
