#include <BAN/Assert.h>
#include <BAN/String.h>

#include <kernel/ACPI/ACPI.h>
#include <kernel/ACPI/AML/Bytes.h>
#include <kernel/ACPI/AML/Namespace.h>
#include <kernel/ACPI/AML/Node.h>
#include <kernel/ACPI/AML/OpRegion.h>
#include <kernel/Processor.h>
#include <kernel/Timer/Timer.h>

#include <ctype.h>

namespace Kernel::ACPI::AML
{

	static constexpr uint64_t ONES = BAN::numeric_limits<uint64_t>::max();
	static constexpr uint64_t ZERO = BAN::numeric_limits<uint64_t>::min();

	BAN::ErrorOr<NameString> parse_name_string(BAN::ConstByteSpan& aml_data)
	{
		dprintln_if(AML_DUMP_FUNCTION_CALLS, "parse_name_string");

		if (aml_data.empty())
			return BAN::Error::from_errno(ENODATA);

		NameString name {};

		switch (aml_data[0])
		{
			case '^':
				while (!aml_data.empty() && aml_data[0] == '^') {
					name.base++;
					aml_data = aml_data.slice(1);
				}
				break;
			case '\\':
				name.base = NameString::base_root;
				aml_data = aml_data.slice(1);
				break;
		}

		if (aml_data.empty())
			return BAN::Error::from_errno(ENODATA);

		size_t name_seg_count = 1;
		switch (aml_data[0])
		{
			case 0:
				name_seg_count = 0;
				aml_data = aml_data.slice(1);
				break;
			case '.':
				name_seg_count = 2;
				aml_data = aml_data.slice(1);
				break;
			case '/':
				if (aml_data.size() < 2)
					return BAN::Error::from_errno(ENODATA);
				name_seg_count = aml_data[1];
				aml_data = aml_data.slice(2);
				break;
		}

		if (aml_data.size() < name_seg_count * 4)
			return BAN::Error::from_errno(ENODATA);

		TRY(name.parts.resize(name_seg_count));
		for (size_t i = 0; i < name_seg_count; i++)
		{
			if (!is_lead_name_char(aml_data[0]) || !is_name_char(aml_data[1]) || !is_name_char(aml_data[2]) || !is_name_char(aml_data[3]))
			{
				dwarnln("Invalid NameSeg {2H}, {2H}, {2H}, {2H}",
					aml_data[0], aml_data[1], aml_data[2], aml_data[3]
				);
				return BAN::Error::from_errno(EINVAL);
			}
			name.parts[i] = aml_data.as<const uint32_t>();
			aml_data = aml_data.slice(4);
		}

		return name;
	}

	static BAN::ErrorOr<Node> parse_integer(BAN::ConstByteSpan& aml_data)
	{
		dprintln_if(AML_DUMP_FUNCTION_CALLS, "parse_integer");

		ASSERT(!aml_data.empty());

		Node result {};
		result.type = Node::Type::Integer;

		size_t byte_count = 0;
		switch (static_cast<AML::Byte>(aml_data[0]))
		{
			case AML::Byte::ZeroOp:
				result.as.integer.value = ZERO;
				break;
			case AML::Byte::OneOp:
				result.as.integer.value = 1;
				break;
			case AML::Byte::OnesOp:
				result.as.integer.value = ONES;
				break;
			case AML::Byte::BytePrefix:
				byte_count = 1;
				break;
			case AML::Byte::WordPrefix:
				byte_count = 2;
				break;
			case AML::Byte::DWordPrefix:
				byte_count = 4;
				break;
			case AML::Byte::QWordPrefix:
				byte_count = 8;
				break;
			default:
				ASSERT_NOT_REACHED();
		}

		aml_data = aml_data.slice(1);

		if (byte_count)
		{
			if (aml_data.size() < byte_count)
				return BAN::Error::from_errno(ENODATA);
			result.as.integer.value = 0;
			for (size_t i = 0; i < byte_count; i++)
				result.as.integer.value |= static_cast<uint64_t>(aml_data[i]) << (i * 8);
			aml_data = aml_data.slice(byte_count);
		}

		return result;
	}

	static BAN::ErrorOr<Node> parse_string(BAN::ConstByteSpan& aml_data)
	{
		dprintln_if(AML_DUMP_FUNCTION_CALLS, "parse_string");

		ASSERT(!aml_data.empty());
		ASSERT(static_cast<AML::Byte>(aml_data[0]) == AML::Byte::StringPrefix);
		aml_data = aml_data.slice(1);

		size_t len = 0;
		for (; len < aml_data.size() && aml_data[len]; len++)
		{
			if (!(0x01 <= aml_data[len] && aml_data[len] <= 0x7F))
			{
				dwarnln("Invalid byte {2H} in a string", aml_data[len]);
				return BAN::Error::from_errno(EINVAL);
			}
		}
		if (len >= aml_data.size())
			return BAN::Error::from_errno(ENODATA);

		Node result {};
		result.type = Node::Type::String;
		result.as.str_buf = static_cast<Buffer*>(kmalloc(sizeof(Buffer) + len));
		result.as.str_buf->size = len;
		result.as.str_buf->ref_count = 1;
		if (result.as.str_buf == nullptr)
			return BAN::Error::from_errno(ENOMEM);
		memcpy(result.as.str_buf->bytes, aml_data.as_span<const uint8_t>().data(), len);

		aml_data = aml_data.slice(len + 1);

		return result;
	}

	BAN::ErrorOr<BAN::ConstByteSpan> parse_pkg(BAN::ConstByteSpan& aml_data)
	{
		dprintln_if(AML_DUMP_FUNCTION_CALLS, "parse_pkg");

		if (aml_data.empty())
			return BAN::Error::from_errno(ENODATA);

		const uint32_t encoding_length = (aml_data[0] >> 6) + 1;
		if (aml_data.size() < encoding_length)
			return BAN::Error::from_errno(ENODATA);

		uint32_t pkg_length = 0;
		switch (encoding_length)
		{
			case 1:
				pkg_length |= aml_data[0] & 0x3F;
				break;
			case 2:
				pkg_length |= aml_data[0] & 0x0F;
				pkg_length |= aml_data[1] << 4;
				break;
			case 3:
				pkg_length |= aml_data[0] & 0x0F;
				pkg_length |= aml_data[1] << 4;
				pkg_length |= aml_data[2] << 12;
				break;
			case 4:
				pkg_length |= aml_data[0] & 0x0F;
				pkg_length |= aml_data[1] << 4;
				pkg_length |= aml_data[2] << 12;
				pkg_length |= aml_data[3] << 20;
				break;
		}

		if (aml_data.size() < pkg_length)
			return BAN::Error::from_errno(ENODATA);

		auto result = aml_data.slice(0, pkg_length).slice(encoding_length);
		aml_data = aml_data.slice(pkg_length);
		return result;
	}

	BAN::ErrorOr<void> resolve_package_element(Package::Element& element, bool error_if_not_exists)
	{
		if (element.resolved)
		{
			if (element.value.node)
				return {};
			element.value.node = new Node();
			if (element.value.node == nullptr)
				return BAN::Error::from_errno(ENOMEM);
			element.value.node->type = Node::Type::Uninitialized;
			return {};
		}

		ASSERT(element.value.location);
		auto [_, resolved_obj] = TRY(Namespace::root_namespace().find_named_object(element.value.location->scope, element.value.location->name));
		if (resolved_obj == nullptr)
		{
			if (!error_if_not_exists)
				return {};
			dwarnln("Could not resolve '{}'.'{}'");
			return BAN::Error::from_errno(ENOENT);
		}

		Node* new_node = new Node();
		if (new_node == nullptr)
			return BAN::Error::from_errno(ENOMEM);

		switch (resolved_obj->node.type)
		{
			case Node::Type::Device:
			case Node::Type::Event:
			case Node::Type::Method:
			case Node::Type::Mutex:
			case Node::Type::OpRegion:
			case Node::Type::PowerResource:
			case Node::Type::Processor:
			case Node::Type::ThermalZone:
			case Node::Type::PredefinedScope:
				new_node->type = Node::Type::Reference;
				new_node->as.reference = resolved_obj;
				new_node->as.reference->ref_count++;
				break;
			default:
				*new_node = TRY(resolved_obj->node.copy());
				break;
		}

		delete element.value.location;
		element.resolved = true;
		element.value.node = new_node;

		return {};
	}

	static BAN::ErrorOr<Node> parse_package_op(ParseContext& context)
	{
		dprintln_if(AML_DUMP_FUNCTION_CALLS, "parse_package_op");

		ASSERT(!context.aml_data.empty());
		const auto opcode = static_cast<AML::Byte>(context.aml_data[0]);
		context.aml_data = context.aml_data.slice(1);

		auto package_pkg = TRY(parse_pkg(context.aml_data));
		if (package_pkg.empty())
			return BAN::Error::from_errno(ENODATA);

		auto old_aml_data = context.aml_data;
		context.aml_data = package_pkg;

		uint64_t num_elements = 0;
		switch (opcode)
		{
			case AML::Byte::PackageOp:
				if (context.aml_data.empty())
					return BAN::Error::from_errno(ENODATA);
				num_elements = context.aml_data[0];
				context.aml_data = context.aml_data.slice(1);
				break;
			case AML::Byte::VarPackageOp:
				num_elements = TRY(convert_node(TRY(parse_node(context)), ConvInteger, sizeof(uint64_t))).as.integer.value;
				break;
			default:
				ASSERT_NOT_REACHED();
		}

		Node result {};
		result.type = Node::Type::Package;
		result.as.package = static_cast<Package*>(kmalloc(sizeof(Package) + num_elements * sizeof(Package::Element)));
		if (result.as.package == nullptr)
			return BAN::Error::from_errno(ENOMEM);
		result.as.package->num_elements = num_elements;
		result.as.package->ref_count = 1;

		size_t i = 0;
		for (; i < num_elements && !context.aml_data.empty(); i++)
		{
			auto& element = result.as.package->elements[i];

			if (is_name_string_start(context.aml_data[0]))
			{
				auto name_string = TRY(parse_name_string(context.aml_data));

				element.resolved = false;
				element.value.location = new Package::Element::Location();
				if (element.value.location == nullptr)
					return BAN::Error::from_errno(ENOMEM);
				element.value.location->name = BAN::move(name_string);
				element.value.location->scope = TRY(context.scope.copy());

				TRY(resolve_package_element(element, false));
				continue;
			}

			element.resolved = true;
			element.value.node = new Node();
			element.value.node->type = Node::Type::Uninitialized;
			if (element.value.node == nullptr)
				return BAN::Error::from_errno(ENOMEM);
			*element.value.node = TRY(parse_node(context));
		}
		for (; i < num_elements; i++)
		{
			result.as.package->elements[i].resolved = true;
			result.as.package->elements[i].value.node = nullptr;
		}
		context.aml_data = old_aml_data;

		return result;
	}

	static BAN::ErrorOr<Node> parse_buffer_op(ParseContext& context)
	{
		dprintln_if(AML_DUMP_FUNCTION_CALLS, "parse_buffer_op");

		ASSERT(!context.aml_data.empty());
		ASSERT(static_cast<AML::Byte>(context.aml_data[0]) == AML::Byte::BufferOp);
		context.aml_data = context.aml_data.slice(1);

		auto buffer_pkg = TRY(parse_pkg(context.aml_data));
		if (buffer_pkg.empty())
			return BAN::Error::from_errno(ENODATA);

		auto old_aml_data = context.aml_data;
		context.aml_data = buffer_pkg;

		auto buffer_size_node = TRY(convert_node(TRY(parse_node(context)), ConvInteger, sizeof(uint64_t)));

		const uint64_t buffer_size = BAN::Math::max<uint64_t>(buffer_size_node.as.integer.value, context.aml_data.size());

		Node result {};
		result.type = Node::Type::Buffer;
		result.as.str_buf = static_cast<Buffer*>(kmalloc(sizeof(Buffer) + buffer_size));
		if (result.as.str_buf == nullptr)
			return BAN::Error::from_errno(ENOMEM);
		result.as.str_buf->size = buffer_size;
		result.as.str_buf->ref_count = 1;

		if (context.aml_data.size() > 0)
			memcpy(result.as.str_buf->bytes, context.aml_data.data(), context.aml_data.size());
		if (context.aml_data.size() < buffer_size)
			memset(result.as.str_buf->bytes + context.aml_data.size(), 0, buffer_size - context.aml_data.size());

		context.aml_data = old_aml_data;

		return result;
	}

// FIXME: WHY TF IS THIS USING OVER 1 KiB of stack
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wstack-usage="
	static BAN::ErrorOr<Node> parse_logical_op(ParseContext& context)
	{
		dprintln_if(AML_DUMP_FUNCTION_CALLS, "parse_logical_op");

		ASSERT(!context.aml_data.empty());
		const auto opcode = static_cast<AML::Byte>(context.aml_data[0]);
		context.aml_data = context.aml_data.slice(1);

		Node dummy_integer {};
		dummy_integer.type = Node::Type::Integer;
		dummy_integer.as.integer.value = 0;

		auto lhs = TRY(parse_node(context));

		if (opcode == AML::Byte::LNotOp)
		{
			lhs = TRY(convert_node(BAN::move(lhs), ConvInteger, sizeof(uint64_t)));

			Node result {};
			result.type = Node::Type::Integer;
			result.as.integer.value = lhs.as.integer.value ? ZERO : ONES;
			return result;
		}

		auto rhs = TRY(parse_node(context));

		bool (*compare)(int) = nullptr;
		switch (opcode)
		{
			case AML::Byte::LAndOp:
			case AML::Byte::LOrOp:
			{
				lhs = TRY(convert_node(BAN::move(lhs), ConvInteger, sizeof(uint64_t)));
				rhs = TRY(convert_node(BAN::move(rhs), ConvInteger, sizeof(uint64_t)));

				Node result {};
				result.type = Node::Type::Integer;
				if (opcode == AML::Byte::LAndOp)
					result.as.integer.value = (lhs.as.integer.value && rhs.as.integer.value) ? ONES : ZERO;
				else
					result.as.integer.value = (lhs.as.integer.value || rhs.as.integer.value) ? ONES : ZERO;

				return result;
			}
			case AML::Byte::LEqualOp:
				compare = [](int val) { return val == 0; };
				break;
			case AML::Byte::LLessOp:
				compare = [](int val) { return val < 0; };
				break;
			case AML::Byte::LGreaterOp:
				compare = [](int val) { return val > 0; };
				break;
			default:
				ASSERT_NOT_REACHED();
		}

		lhs = TRY(convert_node(BAN::move(lhs), ConvInteger | ConvString | ConvBuffer, ONES));

		int (*normalize)(const Node&, const Node&) = nullptr;
		switch (lhs.type)
		{
			case Node::Type::Integer:
				normalize = [](const Node& a, const Node& b) -> int {
					if (a.as.integer.value == b.as.integer.value)
						return 0;
					return a.as.integer.value < b.as.integer.value ? -1 : 1;
				};
				break;
			case Node::Type::String:
			case Node::Type::Buffer:
				normalize = [](const Node& a, const Node& b) -> int {
					const size_t bytes = BAN::Math::min(a.as.str_buf->size, b.as.str_buf->size);
					if (int ret = memcmp(a.as.str_buf->bytes, b.as.str_buf->bytes, bytes))
						return ret;
					return a.as.str_buf->size < b.as.str_buf->size;
				};
				break;
			default:
				ASSERT_NOT_REACHED();
		}

		rhs = TRY(convert_node(BAN::move(rhs), lhs));

		Node result {};
		result.type = Node::Type::Integer;
		result.as.integer.value = compare(normalize(lhs, rhs)) ? ONES : ZERO;

		return result;
	}
#pragma GCC diagnostic pop

	static BAN::ErrorOr<Node> parse_index_op(ParseContext& context);

	enum class TargetType { Reference, Local, Arg, Debug };

	using SuperNameResult = Pair<TargetType, Reference*>;
	static BAN::ErrorOr<SuperNameResult> parse_super_name(ParseContext& context, bool error_if_not_exists)
	{
		dprintln_if(AML_DUMP_FUNCTION_CALLS, "parse_super_name");

		auto& aml_data = context.aml_data;

		const auto opcode     = static_cast<AML::Byte>(aml_data[0]);
		const auto ext_opcode = static_cast<AML::ExtOp>(aml_data.size() >= 2 ? aml_data[1] : 0);
		if (opcode == AML::Byte::IndexOp)
		{
			// FIXME: memory leak

			Reference* reference = new Reference();
			if (reference == nullptr)
				return BAN::Error::from_errno(ENOMEM);
			reference->node = TRY(parse_index_op(context));
			reference->ref_count = 1;

			return SuperNameResult { TargetType::Reference, reference };
		}
		else if (is_name_string_start(aml_data[0]))
		{
			auto target = TRY(parse_name_string(aml_data));
			auto [_, named_object] = TRY(Namespace::root_namespace().find_named_object(context.scope, target));
			if (error_if_not_exists && named_object == nullptr)
			{
				dwarnln("SuperName '{}'.'{}' not found", context.scope, target);
				return BAN::Error::from_errno(ENOENT);
			}
			return SuperNameResult { TargetType::Reference, named_object };
		}
		else if (opcode == AML::Byte::ExtOpPrefix && ext_opcode == AML::ExtOp::DebugOp)
		{
			aml_data = aml_data.slice(2);
			return SuperNameResult { TargetType::Debug, nullptr };
		}
		else if (AML::Byte::Local0 <= opcode && opcode <= AML::Byte::Local7)
		{
			const uint8_t local_index = aml_data[0] - static_cast<uint8_t>(AML::Byte::Local0);
			aml_data = aml_data.slice(1);
			ASSERT(context.locals[local_index]);
			return SuperNameResult { TargetType::Local, context.locals[local_index] };
		}
		else if (AML::Byte::Arg0 <= opcode && opcode <= AML::Byte::Arg6)
		{
			const uint8_t arg_index = aml_data[0] - static_cast<uint8_t>(AML::Byte::Arg0);
			aml_data = aml_data.slice(1);

			if (context.args[arg_index] == nullptr) {
				dwarnln("Trying to reference uninitialized arg");
				return BAN::Error::from_errno(EINVAL);
			}

			return SuperNameResult { TargetType::Local, context.args[arg_index] };
		}

		const bool is_ext = (opcode == AML::Byte::ExtOpPrefix) && (aml_data.size() >= 2);
		dwarnln("TODO: SuperName {2H}{}", aml_data[is_ext ? 1 : 0], is_ext ? "e" : "");
		return BAN::Error::from_errno(ENOTSUP);
	}

	template<typename T> requires (BAN::is_same_v<T, Node> || BAN::is_same_v<T, const Node>)
	static BAN::ErrorOr<T&> underlying_node(T& node)
	{
		switch (node.type)
		{
			case Node::Type::Index:
				dwarnln("TODO: Underlying node of index");
				return BAN::Error::from_errno(ENOTSUP);
			case Node::Type::Reference:
				return node.as.reference->node;
			default:
				return node;
		}
	}

	BAN::ErrorOr<Node> convert_node(Node&& source, uint8_t conversion, uint64_t max_length)
	{
		dprintln_if(AML_DUMP_FUNCTION_CALLS, "convert_node {} -> 0b{3b}", source, conversion);

		if ((source.type == Node::Type::Integer) && (conversion & Conversion::ConvInteger))
			return source;
		if ((source.type == Node::Type::Buffer) && (conversion & Conversion::ConvBuffer))
			return source;
		if ((source.type == Node::Type::String) && (conversion & Conversion::ConvString))
			return source;

		if (source.type == Node::Type::FieldUnit)
			return convert_from_field_unit(source, conversion, max_length);

		if (conversion & Conversion::ConvInteger)
		{
			Node result {};
			result.type = Node::Type::Integer;
			result.as.integer.value = 0;

			switch (source.type)
			{
				case Node::Type::String:
				case Node::Type::Buffer:
					memcpy(
						&result.as.integer.value,
						source.as.str_buf->bytes,
						BAN::Math::min<size_t>(source.as.str_buf->size, sizeof(uint64_t))
					);
					return result;
				case Node::Type::BufferField:
					for (size_t i = 0; i < BAN::Math::min<size_t>(source.as.buffer_field.bit_count, 64); i++)
					{
						const uint64_t idx = source.as.buffer_field.bit_offset + i;
						const uint64_t bit = (source.as.buffer_field.buffer->bytes[idx / 8] >> (idx % 8)) & 1;
						result.as.integer.value |= bit << i;
					}
					return result;
				default:
					break;
			}
		}

		if (conversion & Conversion::ConvBuffer)
		{
			Node result {};
			result.type = Node::Type::Buffer;
			result.as.str_buf = nullptr;

			switch (source.type)
			{
				case Node::Type::Integer:
					if (source.as.integer.value)
						max_length = BAN::Math::min<size_t>(max_length, BAN::Math::ilog2(source.as.integer.value) / 4 + 1);
					else
						max_length = 1;
					result.as.str_buf = static_cast<Buffer*>(kmalloc(sizeof(Buffer) + max_length));
					if (result.as.str_buf == nullptr)
						return BAN::Error::from_errno(ENOMEM);
					memcpy(result.as.str_buf->bytes, &source.as.integer.value, max_length);
					result.as.str_buf->size = max_length;
					result.as.str_buf->ref_count = 1;
					return result;
				case Node::Type::String:
				{
					max_length = BAN::Math::min<size_t>(max_length, source.as.str_buf->size + 1);

					result.as.str_buf = static_cast<Buffer*>(kmalloc(sizeof(Buffer) + max_length));
					if (result.as.str_buf == nullptr)
						return BAN::Error::from_errno(ENOMEM);
					if (max_length <= source.as.str_buf->size)
						memcpy(result.as.str_buf->bytes, source.as.str_buf->bytes, max_length);
					else
					{
						memcpy(result.as.str_buf->bytes, source.as.str_buf->bytes, max_length - 1);
						result.as.str_buf->bytes[max_length - 1] = 0x00;
					}
					result.as.str_buf->size = max_length;
					result.as.str_buf->ref_count = 1;
					return result;
				}
				case Node::Type::BufferField:
					dwarnln("TODO: buffer field to buffer");
					return BAN::Error::from_errno(ENOTSUP);
				default:
					break;
			}
		}

		if (conversion & Conversion::ConvString)
		{
			Node result {};
			result.type = Node::Type::String;
			result.as.str_buf = nullptr;

			switch (source.type)
			{
				case Node::Type::Integer:
				{
					auto string = TRY(BAN::String::formatted("{}", source.as.integer.value));
					max_length = BAN::Math::min<size_t>(max_length, string.size());
					result.as.str_buf = static_cast<Buffer*>(kmalloc(sizeof(Buffer) + max_length));
					if (result.as.str_buf == nullptr)
						return BAN::Error::from_errno(ENOMEM);
					memcpy(result.as.str_buf->bytes, string.data(), max_length);
					result.as.str_buf->size = max_length;
					result.as.str_buf->ref_count = 1;
					return result;
				}
				case Node::Type::Buffer:
					result = BAN::move(source);
					result.type = Node::Type::String;
					return result;
				case Node::Type::BufferField:
					dwarnln("TODO: buffer field to string");
					return BAN::Error::from_errno(ENOTSUP);
				default:
					break;
			}
		}

		dwarnln("Invalid conversion from {} to 0b{3b}", source, conversion);
		return BAN::Error::from_errno(EINVAL);
	}

	BAN::ErrorOr<Node> convert_node(Node&& source, const Node& target)
	{
		if (target.type == Node::Type::Uninitialized)
			return source;
		if (target.type == Node::Type::Integer)
			return convert_node(BAN::move(source), ConvInteger, 8);
		if (target.type == Node::Type::String)
			return convert_node(BAN::move(source), ConvString,  target.as.str_buf->size);
		if (target.type == Node::Type::Buffer)
			return convert_node(BAN::move(source), ConvBuffer,  target.as.str_buf->size);
		dwarnln("Invalid conversion from {} to {}", source, target);
		return BAN::Error::from_errno(EINVAL);
	}

	static BAN::ErrorOr<void> store_to_buffer_field(const Node& source, Node& target)
	{
		ASSERT(target.type == Node::Type::BufferField);

		const uint64_t bit_count = target.as.buffer_field.bit_count;
		const uint64_t bit_offset = target.as.buffer_field.bit_offset;

		const uint8_t* src_buf = nullptr;
		uint64_t src_len = 0;

		switch (source.type)
		{
			case Node::Type::String:
			case Node::Type::Buffer:
				src_buf = source.as.str_buf->bytes;
				src_len = source.as.str_buf->size;
				break;
			case Node::Type::Integer:
				src_buf = reinterpret_cast<const uint8_t*>(&source.as.integer.value);
				src_len = sizeof(uint64_t);
				break;
			default:
			{
				Node dummy = TRY(convert_node(TRY(source.copy()), ConvInteger | ConvBuffer, sizeof(uint64_t)));
				return store_to_buffer_field(dummy, target);
			}
		}

		uint64_t i = 0;
		while (i < bit_count)
		{
			const uint64_t j = bit_offset + i;

			const uint8_t i_mod = i % 8;
			const uint8_t j_mod = j % 8;

			const uint8_t max_src_bits = BAN::Math::min<uint64_t>(8 - i_mod, bit_count - i);
			const uint8_t max_dst_bits = BAN::Math::min<uint64_t>(8 - j_mod, bit_count - i);
			const uint8_t bits = BAN::Math::min(max_src_bits, max_dst_bits);

			const uint8_t mask = (1 << bits) - 1;

			const uint8_t src_byte = (i / 8 < src_len) ? src_buf[i / 8] : 0x00;

			uint8_t& dst_byte = target.as.buffer_field.buffer->bytes[j / 8];
			dst_byte &= ~(mask << j_mod);
			dst_byte |= ((src_byte >> i_mod) & mask) << j_mod;

			i += bits;
		}

		return {};
	}

	static BAN::ErrorOr<void> perform_store(const Node& source, Reference* target, TargetType target_type)
	{
		dprintln_if(AML_DUMP_FUNCTION_CALLS, "perform_store");

		if (target_type == TargetType::Debug)
		{
			dprintln_if(AML_ENABLE_DEBUG, "DEBUG: {}", source);
			return {};
		}

		ASSERT(target);

		if (target->node.type == Node::Type::BufferField)
			return store_to_buffer_field(source, target->node);

		if (target->node.type == Node::Type::FieldUnit)
			return store_to_field_unit(source, target->node);

		auto source_copy = TRY(source.copy());

		Node* index_node = nullptr;
		if (target->node.type == Node::Type::Reference)
		{
			auto& ref_target = target->node.as.reference->node;

			if (ref_target.type == Node::Type::Index)
				index_node = &ref_target;
			else
			{
				ASSERT(ref_target.type != Node::Type::Reference);
				ref_target = TRY(convert_node(BAN::move(source_copy), ref_target));
				return {};
			}
		}

		if (target->node.type == Node::Type::Index)
			index_node = &target->node;

		if (index_node)
		{
			auto& index = index_node->as.index;
			switch (index.type)
			{
				case Node::Type::String:
				case Node::Type::Buffer:
					ASSERT(index.index < index.as.str_buf->size);
					index.as.str_buf->bytes[index.index] = TRY(convert_node(BAN::move(source_copy), ConvInteger, sizeof(uint64_t))).as.integer.value;
					break;
				case Node::Type::Package:
				{
					ASSERT(index.index < index.as.package->num_elements);

					auto& pkg_element = index.as.package->elements[index.index];
					TRY(resolve_package_element(pkg_element, true));
					ASSERT(pkg_element.value.node);

					if (pkg_element.value.node->type == Node::Type::Reference)
						*pkg_element.value.node = TRY(convert_node(BAN::move(source_copy), pkg_element.value.node->as.reference->node));
					else
						*pkg_element.value.node = TRY(convert_node(BAN::move(source_copy), *pkg_element.value.node));
					break;
				}
				default:
					ASSERT_NOT_REACHED();
			}

			return {};
		}

		if (target_type == TargetType::Reference)
			target->node = TRY(convert_node(BAN::move(source_copy), target->node));
		else
		{
			if (source_copy.type == Node::Type::FieldUnit)
				source_copy = TRY(convert_from_field_unit(source_copy, ConvInteger | ConvBuffer, ONES));
			target->node = BAN::move(source_copy);
		}

		return {};
	}

	static BAN::ErrorOr<void> store_into_target(ParseContext& context, const Node& node)
	{
		dprintln_if(AML_DUMP_FUNCTION_CALLS, "store_into_target");

		auto& aml_data = context.aml_data;

		if (aml_data.empty())
			return BAN::Error::from_errno(ENODATA);

		if (aml_data[0] == 0)
		{
			aml_data = aml_data.slice(1);
			return {};
		}

		auto [target_type, target] = TRY(parse_super_name(context, true));
		return perform_store(node, target, target_type);
	}

	static BAN::ErrorOr<Node> parse_store_op(ParseContext& context)
	{
		dprintln_if(AML_DUMP_FUNCTION_CALLS, "parse_store_op");

		ASSERT(!context.aml_data.empty());
		ASSERT(static_cast<AML::Byte>(context.aml_data[0]) == AML::Byte::StoreOp);
		context.aml_data = context.aml_data.slice(1);

		auto source = TRY(parse_node(context));

		if (context.aml_data.empty())
			return BAN::Error::from_errno(ENODATA);
		if (context.aml_data[0] == 0)
		{
			dwarnln("StoreOp in to null target");
			return BAN::Error::from_errno(EINVAL);
		}

		TRY(store_into_target(context, source));

		return source;
	}

	static BAN::ErrorOr<Node> parse_copy_object_op(ParseContext& context)
	{
		dprintln_if(AML_DUMP_FUNCTION_CALLS, "parse_copy_object_op");

		ASSERT(!context.aml_data.empty());
		ASSERT(static_cast<AML::Byte>(context.aml_data[0]) == AML::Byte::CopyObjectOp);
		context.aml_data = context.aml_data.slice(1);

		auto source = TRY(parse_node(context));

		if (context.aml_data.empty())
			return BAN::Error::from_errno(ENODATA);

		auto [target_type, target] = TRY(parse_super_name(context, true));
		switch (target_type)
		{
			case TargetType::Arg:
			case TargetType::Local:
			case TargetType::Reference:
				break;
			case TargetType::Debug:
				dwarnln("CopyObjectOp target is Debug");
				return BAN::Error::from_errno(EINVAL);
		}

		target->node = TRY(source.copy());

		return source;
	}

	static BAN::ErrorOr<Node> parse_index_op(ParseContext& context)
	{
		dprintln_if(AML_DUMP_FUNCTION_CALLS, "parse_index_op");

		ASSERT(!context.aml_data.empty());
		ASSERT(static_cast<AML::Byte>(context.aml_data[0]) == AML::Byte::IndexOp);
		context.aml_data = context.aml_data.slice(1);

		auto source_dummy = TRY(parse_node(context, true));
		auto index        = TRY(convert_node(TRY(parse_node(context)), ConvInteger, sizeof(uint64_t)));

		auto& source = TRY_REF(underlying_node(source_dummy));

		Node result {};

		switch (source.type)
		{
			case Node::Type::String:
			case Node::Type::Buffer:
				if (index.as.integer.value >= source.as.str_buf->size)
				{
					dwarnln("Invalid index {} to buffer of size {}", index.as.integer.value, source.as.str_buf->size);
					return BAN::Error::from_errno(EINVAL);
				}
				result.as.index.as.str_buf = source.as.str_buf;
				result.as.index.as.str_buf->ref_count++;
				break;
			case Node::Type::Package:
				if (index.as.integer.value >= source.as.package->num_elements)
				{
					dwarnln("Invalid index {} to package of size {}", index.as.integer.value, source.as.package->num_elements);
					return BAN::Error::from_errno(EINVAL);
				}
				result.as.index.as.package = source.as.package;
				result.as.index.as.package->ref_count++;
				break;
			default:
				dwarnln("Invalid IndexOp({}, {})", source, index);
				return BAN::Error::from_errno(EINVAL);
		}

		result.type = Node::Type::Index;
		result.as.index.type = source.type;
		result.as.index.index = index.as.integer.value;

		TRY(store_into_target(context, result));

		return result;
	}

	static BAN::ErrorOr<Node> parse_object_type_op(ParseContext& context)
	{
		dprintln_if(AML_DUMP_FUNCTION_CALLS, "parse_object_type_op");

		ASSERT(!context.aml_data.empty());
		ASSERT(static_cast<AML::Byte>(context.aml_data[0]) == AML::Byte::ObjectTypeOp);
		context.aml_data = context.aml_data.slice(1);

		auto [object_type, object] = TRY(parse_super_name(context, true));

		uint64_t value = 0;
		if (object_type == TargetType::Debug)
			value = 16;
		else
		{
			ASSERT(object);

			auto* node = &object->node;
			while (node->type == Node::Type::Reference)
				if (node->type == Node::Type::Reference)
					node = &node->as.reference->node;

			auto node_type = node->type;
			if (node_type == Node::Type::Index)
			{
				const auto& index = node->as.index;
				switch (index.type)
				{
					case Node::Type::String:
					case Node::Type::Buffer:
						node_type = Node::Type::BufferField;
						break;
					case Node::Type::Package:
						ASSERT(index.index < index.as.package->num_elements);
						TRY(resolve_package_element(index.as.package->elements[index.index], true));
						node_type = index.as.package->elements[index.index].value.node->type;
						break;
					default:
						ASSERT_NOT_REACHED();
				}
			}

			switch (node_type)
			{
				case Node::Type::Uninitialized:
				case Node::Type::PredefinedScope: value =  0; break;
				case Node::Type::Integer:         value =  1; break;
				case Node::Type::String:          value =  2; break;
				case Node::Type::Buffer:          value =  3; break;
				case Node::Type::Package:         value =  4; break;
				case Node::Type::FieldUnit:       value =  5; break;
				case Node::Type::Device:          value =  6; break;
				case Node::Type::Event:           value =  7; break;
				case Node::Type::Method:          value =  8; break;
				case Node::Type::Mutex:           value =  9; break;
				case Node::Type::OpRegion:        value = 10; break;
				case Node::Type::PowerResource:   value = 11; break;
				case Node::Type::Processor:       value = 12; break;
				case Node::Type::ThermalZone:     value = 13; break;
				case Node::Type::BufferField:     value = 14; break;
				case Node::Type::Debug:           value = 16; break;
				case Node::Type::Index:
				case Node::Type::Reference:
				case Node::Type::Count:           ASSERT_NOT_REACHED();
			}
		}

		Node result;
		result.type = Node::Type::Integer;
		result.as.integer.value = value;
		return result;
	}

	static bool match_compare(const Package::Element& element, uint8_t opcode, uint64_t target)
	{
		if (!element.resolved || element.value.node == nullptr)
			return false;

		auto element_copy = element.value.node->copy();
		if (element_copy.is_error())
			return false;

		auto element_conv = convert_node(element_copy.release_value(), ConvInteger, sizeof(uint64_t));
		if (element_conv.is_error())
			return false;

		switch (opcode)
		{
			case 0: return true;
			case 1: return element_conv.value().as.integer.value == target;
			case 2: return element_conv.value().as.integer.value <= target;
			case 3: return element_conv.value().as.integer.value <  target;
			case 4: return element_conv.value().as.integer.value >= target;
			case 5: return element_conv.value().as.integer.value >  target;
		}

		return false;
	}

	static BAN::ErrorOr<Node> parse_match_op(ParseContext& context)
	{
		dprintln_if(AML_DUMP_FUNCTION_CALLS, "parse_match_op");

		ASSERT(!context.aml_data.empty());
		ASSERT(static_cast<AML::Byte>(context.aml_data[0]) == AML::Byte::MatchOp);
		context.aml_data = context.aml_data.slice(1);

		auto search = TRY(parse_node(context));
		if (search.type != Node::Type::Package)
		{
			dwarnln("Match search package is {}", search);
			return BAN::Error::from_errno(EINVAL);
		}

		if (context.aml_data.empty())
			return BAN::Error::from_errno(ENODATA);
		const uint8_t opcode1 = context.aml_data[0];
		const uint64_t operand1 = TRY(convert_node(TRY(parse_node(context)), ConvInteger, sizeof(uint64_t))).as.integer.value;

		if (context.aml_data.empty())
			return BAN::Error::from_errno(ENODATA);
		const uint8_t opcode2 = context.aml_data[0];
		const uint64_t operand2 = TRY(convert_node(TRY(parse_node(context)), ConvInteger, sizeof(uint64_t))).as.integer.value;

		const uint64_t start_idx = TRY(convert_node(TRY(parse_node(context)), ConvInteger, sizeof(uint64_t))).as.integer.value;

		Node result;
		result.type = Node::Type::Integer;
		result.as.integer.value = ONES;

		for (uint64_t i = start_idx; i < search.as.package->num_elements; i++)
		{
			auto& element = search.as.package->elements[i];
			if (!element.resolved)
				TRY(resolve_package_element(element, false));

			if (!match_compare(element, opcode1, operand1))
				continue;
			if (!match_compare(element, opcode2, operand2))
				continue;

			result.as.integer.value = i;
			break;
		}

		return result;
	}

	static BAN::ErrorOr<Node> sizeof_impl(const Node& node)
	{
		Node result {};
		result.type = Node::Type::Integer;
		result.as.integer.value = 0;

		switch (node.type)
		{
			case Node::Type::String:
			case Node::Type::Buffer:
				result.as.integer.value = node.as.str_buf->size;
				break;
			case Node::Type::Package:
				result.as.integer.value = node.as.package->num_elements;
				break;
			case Node::Type::Reference:
				return sizeof_impl(node.as.reference->node);
			default:
				dwarnln("SizeofOp on {}", node);
				return BAN::Error::from_errno(EINVAL);
		}

		return result;
	}

	static BAN::ErrorOr<void> parse_createfield_op(ParseContext& context)
	{
		dprintln_if(AML_DUMP_FUNCTION_CALLS, "parse_createfield_op");

		ASSERT(!context.aml_data.empty());
		const auto opcode = static_cast<AML::Byte>(context.aml_data[0]);
		context.aml_data = context.aml_data.slice(1);

		uint64_t bit_count = 0;
		bool index_in_bits = false;

		if (opcode == AML::Byte::ExtOpPrefix)
		{
			ASSERT(!context.aml_data.empty());
			ASSERT(static_cast<AML::ExtOp>(context.aml_data[0]) == AML::ExtOp::CreateFieldOp);
			context.aml_data = context.aml_data.slice(1);

			bit_count = 0;
			index_in_bits = true;
		}
		else
		{
			switch (opcode)
			{
				case AML::Byte::CreateBitFieldOp:
					bit_count = 1;
					index_in_bits = true;
					break;
				case AML::Byte::CreateByteFieldOp:
					bit_count = 8;
					index_in_bits = false;
					break;
				case AML::Byte::CreateWordFieldOp:
					bit_count = 16;
					index_in_bits = false;
					break;
				case AML::Byte::CreateDWordFieldOp:
					bit_count = 32;
					index_in_bits = false;
					break;
				case AML::Byte::CreateQWordFieldOp:
					bit_count = 64;
					index_in_bits = false;
					break;
				default:
					ASSERT_NOT_REACHED();
			}
		}

		auto buffer_node_ref = TRY(parse_node(context, true));

		auto* buffer_node = &buffer_node_ref;
		if (buffer_node->type == Node::Type::Reference)
			buffer_node = &buffer_node->as.reference->node;

		if (buffer_node->type != Node::Type::Buffer)
		{
			dwarnln("CreateField buffer is {}", buffer_node);
			return BAN::Error::from_errno(EINVAL);
		}

		auto index_node = TRY(convert_node(TRY(parse_node(context)), ConvInteger, sizeof(uint64_t)));

		if (bit_count == 0)
		{
			auto bit_count_node = TRY(convert_node(TRY(parse_node(context)), ConvInteger, sizeof(uint64_t)));

			bit_count = bit_count_node.as.integer.value;
			if (bit_count == 0)
			{
				dwarnln("CreateField bit count 0");
				return BAN::Error::from_errno(EINVAL);
			}
		}

		auto field_name_string = TRY(parse_name_string(context.aml_data));

		Node buffer_field;
		buffer_field.type = Node::Type::BufferField;
		buffer_field.as.buffer_field.buffer = buffer_node->as.str_buf;
		buffer_field.as.buffer_field.buffer->ref_count++;
		buffer_field.as.buffer_field.bit_count = bit_count;
		buffer_field.as.buffer_field.bit_offset = index_in_bits ? index_node.as.integer.value : index_node.as.integer.value * 8;

		auto absolte_path = TRY(Namespace::root_namespace().add_named_object(context, field_name_string, BAN::move(buffer_field)));
		if (absolte_path.parts.empty())
		{
			dwarnln("Could not add Buffer Field '{}'.'{}' to namespace", context.scope, field_name_string);
			return {};
		}

		return {};
	}

	static BAN::ErrorOr<Node> parse_sizeof_op(ParseContext& context)
	{
		dprintln_if(AML_DUMP_FUNCTION_CALLS, "parse_sizeof_op");

		ASSERT(!context.aml_data.empty());
		ASSERT(static_cast<AML::Byte>(context.aml_data[0]) == AML::Byte::SizeOfOp);
		context.aml_data = context.aml_data.slice(1);

		auto [object_type, object] = TRY(parse_super_name(context, true));
		if (object_type == TargetType::Debug)
		{
			dwarnln("SizeofOp on debug object");
			return BAN::Error::from_errno(EINVAL);
		}

		ASSERT(object);
		return TRY(sizeof_impl(object->node));
	}

	static BAN::ErrorOr<Node> derefof_impl(const Node& source)
	{
		switch (source.type)
		{
			case Node::Type::Reference:
				return TRY(source.as.reference->node.copy());
			case Node::Type::Index:
			{
				switch (source.as.index.type)
				{
					case Node::Type::String:
					case Node::Type::Buffer:
					{
						ASSERT(source.as.index.index < source.as.index.as.str_buf->size);

						Node result;
						result.type = Node::Type::Integer;
						result.as.integer.value = source.as.index.as.str_buf->bytes[source.as.index.index];
						return result;
					}
					case Node::Type::Package:
					{
						ASSERT(source.as.index.index < source.as.index.as.package->num_elements);
						TRY(resolve_package_element(source.as.index.as.package->elements[source.as.index.index], true));
						return TRY(source.as.index.as.package->elements[source.as.index.index].value.node->copy());
					}
					default: ASSERT_NOT_REACHED();
				}
			}
			default:
				dwarnln("DerefOf of non-reference {}", source);
				return BAN::Error::from_errno(EINVAL);
		}
	}

	static BAN::ErrorOr<Node> parse_inc_dec_op(ParseContext& context)
	{
		dprintln_if(AML_DUMP_FUNCTION_CALLS, "parse_inc_dec_op");

		ASSERT(!context.aml_data.empty());
		const uint8_t opcode = context.aml_data[0];
		context.aml_data = context.aml_data.slice(1);

		uint64_t (*function)(uint64_t) = nullptr;
		switch (static_cast<AML::Byte>(opcode))
		{
			case AML::Byte::IncrementOp:
				function = [](uint64_t x) { return x + 1; };
				break;
			case AML::Byte::DecrementOp:
				function = [](uint64_t x) { return x - 1; };
				break;
			default:
				ASSERT_NOT_REACHED();
		}

		auto [type, addend_ref] = TRY(parse_super_name(context, true));
		if (type == TargetType::Debug)
		{
			dprintln_if(AML_DUMP_FUNCTION_CALLS, "UnaryIntegerOp on Debug object");

			Node result {};
			result.type = Node::Type::Integer;
			result.as.integer.value = 0;
			return result;
		}

		ASSERT(addend_ref);

		Node addend_node = TRY(addend_ref->node.copy());
		if (addend_node.type == Node::Type::Reference || addend_node.type == Node::Type::Index)
			addend_node = TRY(derefof_impl(addend_node));

		auto current = TRY(convert_node(BAN::move(addend_node), ConvInteger, sizeof(uint64_t)));

		Node result {};
		result.type = Node::Type::Integer;
		result.as.integer.value = function(current.as.integer.value);

		TRY(perform_store(result, addend_ref, type));
		return result;
	}

	static BAN::ErrorOr<Node> parse_unary_integer_op(ParseContext& context)
	{
		dprintln_if(AML_DUMP_FUNCTION_CALLS, "parse_unary_integer_op");

		ASSERT(!context.aml_data.empty());
		const auto opcode = context.aml_data[0];
		context.aml_data = context.aml_data.slice(1);

		uint64_t (*function)(uint64_t) = nullptr;

		switch (static_cast<AML::Byte>(opcode))
		{
			case AML::Byte::NotOp:
				function = [](uint64_t a) { return ~a; };
				break;
			default:
				ASSERT_NOT_REACHED();
		}

		auto value = TRY(convert_node(TRY(parse_node(context)), ConvInteger, sizeof(uint64_t)));

		Node result {};
		result.type = Node::Type::Integer;
		result.as.integer.value = function(value.as.integer.value);

		TRY(store_into_target(context, result));

		return result;
	}

	static BAN::ErrorOr<Node> parse_binary_integer_op(ParseContext& context)
	{
		dprintln_if(AML_DUMP_FUNCTION_CALLS, "parse_binary_integer_op");

		ASSERT(!context.aml_data.empty());
		const auto opcode = static_cast<AML::Byte>(context.aml_data[0]);
		context.aml_data = context.aml_data.slice(1);

		uint64_t (*function)(uint64_t, uint64_t) = nullptr;

		switch (opcode)
		{
			case AML::Byte::AddOp:
				function = [](uint64_t a, uint64_t b) { return a + b; };
				break;
			case AML::Byte::SubtractOp:
				function = [](uint64_t a, uint64_t b) { return a - b; };
				break;
			case AML::Byte::MultiplyOp:
				function = [](uint64_t a, uint64_t b) { return a * b; };
				break;
			case AML::Byte::ShiftLeftOp:
				function = [](uint64_t a, uint64_t b) { return a << b; };
				break;
			case AML::Byte::ShiftRightOp:
				function = [](uint64_t a, uint64_t b) { return a >> b; };
				break;
			case AML::Byte::AndOp:
				function = [](uint64_t a, uint64_t b) { return a & b; };
				break;
			case AML::Byte::NandOp:
				function = [](uint64_t a, uint64_t b) { return ~(a & b); };
				break;
			case AML::Byte::OrOp:
				function = [](uint64_t a, uint64_t b) { return a | b; };
				break;
			case AML::Byte::NorOp:
				function = [](uint64_t a, uint64_t b) { return ~(a | b); };
				break;
			case AML::Byte::XorOp:
				function = [](uint64_t a, uint64_t b) { return a ^ b; };
				break;
			case AML::Byte::DivideOp:
				function = [](uint64_t a, uint64_t b) { return a / b; };
				break;
			case AML::Byte::ModOp:
				function = [](uint64_t a, uint64_t b) { return a % b; };
				break;
			default:
				ASSERT_NOT_REACHED();
		}

		auto lhs = TRY(convert_node(TRY(parse_node(context)), ConvInteger, sizeof(uint64_t)));
		auto rhs = TRY(convert_node(TRY(parse_node(context)), ConvInteger, sizeof(uint64_t)));

		if (opcode == AML::Byte::DivideOp || opcode == AML::Byte::ModOp)
		{
			if (rhs.as.integer.value == 0)
			{
				dwarnln("DivideOp or ModOp divisor is zero");
				return BAN::Error::from_errno(EINVAL);
			}
		}

		Node result {};
		result.type = Node::Type::Integer;
		result.as.integer.value = function(lhs.as.integer.value, rhs.as.integer.value);

		TRY(store_into_target(context, result));

		if (opcode == AML::Byte::DivideOp)
		{
			Node remainder {};
			remainder.type = Node::Type::Integer;
			remainder.as.integer.value = function(lhs.as.integer.value, rhs.as.integer.value);
			TRY(store_into_target(context, remainder));
		}

		return result;
	}

	static BAN::ErrorOr<Node> parse_find_set_bit_op(ParseContext& context)
	{
		dprintln_if(AML_DUMP_FUNCTION_CALLS, "parse_find_set_bit_op");

		ASSERT(!context.aml_data.empty());
		const auto opcode = context.aml_data[0];
		context.aml_data = context.aml_data.slice(1);

		uint64_t (*function)(uint64_t) = nullptr;

		switch (static_cast<AML::Byte>(opcode))
		{
			case AML::Byte::FindSetLeftBitOp:
				function = [](uint64_t a) -> uint64_t {
					for (int i = 63; i >= 0; i--)
						if ((a >> i) & 1)
							return i + 1;
					return 0;
				};
				break;
			case AML::Byte::FindSetRightBitOp:
				function = [](uint64_t a) -> uint64_t {
					for (int i = 0; i < 64; i++)
						if ((a >> i) & 1)
							return i + 1;
					return 0;
				};
				break;
			default:
				ASSERT_NOT_REACHED();
		}

		auto value = TRY(convert_node(TRY(parse_node(context)), ConvInteger, sizeof(uint64_t)));

		Node result {};
		result.type = Node::Type::Integer;
		result.as.integer.value = function(value.as.integer.value);

		TRY(store_into_target(context, result));

		return result;
	}

	static BAN::ErrorOr<Node> parse_mid_op(ParseContext& context)
	{
		dprintln_if(AML_DUMP_FUNCTION_CALLS, "parse_mid_op");

		ASSERT(!context.aml_data.empty());
		ASSERT(static_cast<AML::Byte>(context.aml_data[0]) == AML::Byte::MidOp);
		context.aml_data = context.aml_data.slice(1);

		auto source           = TRY(convert_node(TRY(parse_node(context)), ConvBuffer | ConvString, ONES));
		const uint64_t index  = TRY(convert_node(TRY(parse_node(context)), ConvInteger, sizeof(uint64_t))).as.integer.value;
		const uint64_t length = TRY(convert_node(TRY(parse_node(context)), ConvInteger, sizeof(uint64_t))).as.integer.value;

		const uint64_t source_len = source.as.str_buf->size;

		uint64_t result_size = 0;
		if (index + length <= source_len)
			result_size = length;
		else if (index < source_len)
			result_size = source_len - index;

		Node result;
		result.type = source.type;
		result.as.str_buf = static_cast<Buffer*>(kmalloc(sizeof(Buffer) + result_size));
		if (result.as.str_buf == nullptr)
			return BAN::Error::from_errno(ENOTSUP);
		memcpy(result.as.str_buf->bytes, source.as.str_buf->bytes + index, result_size);
		result.as.str_buf->size = result_size;
		result.as.str_buf->ref_count = 1;

		return result;
	}

	static BAN::ErrorOr<Node> parse_concat_op(ParseContext& context)
	{
		dprintln_if(AML_DUMP_FUNCTION_CALLS, "parse_concat_op");

		ASSERT(!context.aml_data.empty());
		ASSERT(static_cast<AML::Byte>(context.aml_data[0]) == AML::Byte::ConcatOp);
		context.aml_data = context.aml_data.slice(1);

		auto source1 = TRY(convert_node(TRY(parse_node(context)), ConvInteger | ConvString | ConvBuffer, ONES));
		auto source2 = TRY(convert_node(TRY(parse_node(context)), source1));

		Node result {};
		switch (source1.type)
		{
			case AML::Node::Type::Integer:
				dwarnln("TODO: ConcatOp with integers");
				return BAN::Error::from_errno(ENOTSUP);
			case AML::Node::Type::String:
			case AML::Node::Type::Buffer:
				result.type = source1.type;
				result.as.str_buf = static_cast<Buffer*>(kmalloc(sizeof(Buffer) + source1.as.str_buf->size + source2.as.str_buf->size));
				if (result.as.str_buf == nullptr)
					return BAN::Error::from_errno(ENOMEM);
				result.as.str_buf->size = source1.as.str_buf->size + source2.as.str_buf->size;
				result.as.str_buf->ref_count = 1;
				memcpy(result.as.str_buf->bytes,                            source1.as.str_buf->bytes, source1.as.str_buf->size);
				memcpy(result.as.str_buf->bytes + source1.as.str_buf->size, source2.as.str_buf->bytes, source2.as.str_buf->size);
				break;
			default:
				ASSERT_NOT_REACHED();
		}

		TRY(store_into_target(context, result));

		return result;
	}

// FIXME: WHY TF IS THIS USING OVER 1 KiB of stack
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wstack-usage="
	static BAN::ErrorOr<Node> parse_explicit_conversion(ParseContext& context)
	{
		dprintln_if(AML_DUMP_FUNCTION_CALLS, "parse_explicit_conversion");

		ASSERT(!context.aml_data.empty());
		const uint8_t opcode = context.aml_data[0];
		context.aml_data = context.aml_data.slice(1);

		auto source = TRY(parse_node(context));
		if (source.type == Node::Type::FieldUnit || source.type == Node::Type::BufferField)
			source = TRY(convert_node(BAN::move(source), ConvInteger | ConvBuffer, ONES));

		switch (source.type)
		{
			case Node::Type::Buffer:
			case Node::Type::String:
			case Node::Type::Integer:
				break;
			default:
				dwarnln("Explicit conversion source is {}", source);
				return BAN::Error::from_errno(EINVAL);
		}

		Node result {};

		switch (static_cast<AML::Byte>(opcode))
		{
			case AML::Byte::ToDecimalStringOp:
				if (source.type == Node::Type::String)
					result = BAN::move(source);
				else
				{
					BAN::String ban_string;

					if (source.type == Node::Type::Integer)
						ban_string = TRY(BAN::String::formatted("{}", source.as.integer.value));
					else if (source.type == Node::Type::Buffer)
						for (size_t i = 0; i < source.as.str_buf->size; i++)
							TRY(ban_string.append(TRY(BAN::String::formatted("{},", source.as.str_buf->bytes[i]))));
					else
						ASSERT_NOT_REACHED();

					if (!ban_string.empty() && ban_string.back() == ',')
						ban_string.pop_back();

					result.type = Node::Type::String;
					result.as.str_buf = static_cast<Buffer*>(kmalloc(sizeof(Buffer) + ban_string.size()));
					if (result.as.str_buf == nullptr)
						return BAN::Error::from_errno(EINVAL);
					memcpy(result.as.str_buf->bytes, ban_string.data(), ban_string.size());
					result.as.str_buf->size = ban_string.size();
					result.as.str_buf->ref_count = 1;
				}
				break;
			case AML::Byte::ToHexStringOp:
				if (source.type == Node::Type::String)
					result = BAN::move(source);
				else
				{
					BAN::String ban_string;

					if (source.type == Node::Type::Integer)
						ban_string = TRY(BAN::String::formatted("0x{H}", source.as.integer.value));
					else if (source.type == Node::Type::Buffer)
						for (size_t i = 0; i < source.as.str_buf->size; i++)
							TRY(ban_string.append(TRY(BAN::String::formatted("0x{2H},", source.as.str_buf->bytes[i]))));
					else
						ASSERT_NOT_REACHED();

					if (!ban_string.empty() && ban_string.back() == ',')
						ban_string.pop_back();

					result.type = Node::Type::String;
					result.as.str_buf = static_cast<Buffer*>(kmalloc(sizeof(Buffer) + ban_string.size()));
					if (result.as.str_buf == nullptr)
						return BAN::Error::from_errno(EINVAL);
					memcpy(result.as.str_buf->bytes, ban_string.data(), ban_string.size());
					result.as.str_buf->size = ban_string.size();
					result.as.str_buf->ref_count = 1;
				}
				break;
			case AML::Byte::ToIntegerOp:
				if (source.type != Node::Type::String)
					result = TRY(convert_node(BAN::move(source), ConvInteger, sizeof(uint64_t)));
				else
				{
					const auto get_base_val =
						[](char c, uint8_t base) -> BAN::Optional<uint8_t>
						{
							if (isdigit(c) && c - '0' < base)
								return c - '0';
							if (isalpha(c) && tolower(c) - 'a' + 10 < base)
								return tolower(c) - 'a' + 10;
							return {};
					};

					auto source_sv = source.as.str_buf->as_sv();

					while (!source_sv.empty() && !isdigit(source_sv[0]) && source_sv[0] != '-' && source_sv[0] != '+')
						source_sv = source_sv.substring(1);

					const bool is_negative = source_sv.empty() ? false : source_sv[0] == '-';
					if (!source_sv.empty() && (source_sv[0] == '-' || source_sv[0] == '+'))
						source_sv = source_sv.substring(1);

					uint8_t base = 10;
					if (!source_sv.empty() && source_sv[0] == '0')
					{
						base = 8;
						source_sv = source_sv.substring(1);
						if (!source_sv.empty() && tolower(source_sv[0]) == 'x')
						{
							base = 16;
							source_sv = source_sv.substring(1);
						}
					}

					uint64_t value = 0;
					for (size_t i = 0; i < source_sv.size(); i++)
					{
						const auto to_add = get_base_val(source_sv[i], base);
						if (!to_add.has_value())
							break;

						if (BAN::Math::will_multiplication_overflow<uint64_t>(value, base) ||
							BAN::Math::will_addition_overflow<uint64_t>(value * base, to_add.value()))
						{
							value = BAN::numeric_limits<uint64_t>::max();
							break;
						}

						value = (value * base) + to_add.value();
					}

					if (is_negative)
						value = -value;

					result.type = Node::Type::Integer;
					result.as.integer.value = value;
				}
				break;
			case AML::Byte::ToBufferOp:
				result = TRY(convert_node(BAN::move(source), ConvBuffer, ONES));
				break;
			default:
				ASSERT_NOT_REACHED();
		}

		TRY(store_into_target(context, result));

		return result;
	}
#pragma GCC diagnostic pop

	static BAN::ErrorOr<Node> parse_to_string_op(ParseContext& context)
	{
		dprintln_if(AML_DUMP_FUNCTION_CALLS, "parse_to_string_op");

		ASSERT(!context.aml_data.empty());
		ASSERT(static_cast<AML::Byte>(context.aml_data[0]) == AML::Byte::ToStringOp);
		context.aml_data = context.aml_data.slice(1);

		auto source = TRY(convert_node(TRY(parse_node(context)), ConvBuffer, ONES));
		auto length = TRY(convert_node(TRY(parse_node(context)), ConvInteger, sizeof(uint64_t))).as.integer.value;

		if (source.as.str_buf->size < length)
			length = source.as.str_buf->size;

		for (size_t i = 0; i < length; i++)
		{
			if (source.as.str_buf->bytes[i] != 0x00)
				continue;
			length = i;
			break;
		}

		Node result;
		result.type = Node::Type::String;
		result.as.str_buf = static_cast<Buffer*>(kmalloc(sizeof(Buffer) + length));
		if (result.as.str_buf == nullptr)
			return BAN::Error::from_errno(ENOMEM);
		memcpy(result.as.str_buf->bytes, source.as.str_buf->bytes, length);
		result.as.str_buf->size = length;
		result.as.str_buf->ref_count = 1;

		TRY(store_into_target(context, source));

		return result;
	}

	static BAN::ErrorOr<void> parse_alias_op(ParseContext& context)
	{
		dprintln_if(AML_DUMP_FUNCTION_CALLS, "parse_alias_op");

		ASSERT(!context.aml_data.empty());
		ASSERT(static_cast<AML::Byte>(context.aml_data[0]) == AML::Byte::AliasOp);
		context.aml_data = context.aml_data.slice(1);

		auto source_name_string = TRY(parse_name_string(context.aml_data));
		auto object_name_string = TRY(parse_name_string(context.aml_data));

		auto [_, source_ref] = TRY(Namespace::root_namespace().find_named_object(context.scope, source_name_string));
		if (source_ref == nullptr)
		{
			dwarnln("AliasOp source does not exists");
			return {};
		}

		TRY(Namespace::root_namespace().add_alias(context, object_name_string, source_ref));

		return {};
	}

	static BAN::ErrorOr<void> parse_name_op(ParseContext& context)
	{
		dprintln_if(AML_DUMP_FUNCTION_CALLS, "parse_name_op");

		ASSERT(!context.aml_data.empty());
		ASSERT(static_cast<AML::Byte>(context.aml_data[0]) == AML::Byte::NameOp);
		context.aml_data = context.aml_data.slice(1);

		auto name_string = TRY(parse_name_string(context.aml_data));
		auto object      = TRY(parse_node(context));

		auto absolute_path = TRY(Namespace::root_namespace().add_named_object(context, name_string, BAN::move(object)));
		if (absolute_path.parts.empty())
		{
			dwarnln("Could not add Name '{}'.'{}' to namespace", context.scope, name_string);
			return {};
		}

		return {};
	}

	static BAN::ErrorOr<Node> parse_refof_op(ParseContext& context)
	{
		dprintln_if(AML_DUMP_FUNCTION_CALLS, "parse_refof_op");

		ASSERT(!context.aml_data.empty());
		ASSERT(static_cast<AML::Byte>(context.aml_data[0]) == AML::Byte::RefOfOp);
		context.aml_data = context.aml_data.slice(1);

		auto [type, target] = TRY(parse_super_name(context, true));
		if (type == TargetType::Debug)
		{
			dwarnln("TODO: RefOf debug");
			return BAN::Error::from_errno(ENOTSUP);
		}

		Reference* to_reference = target;
		if (target->node.type == Node::Type::Reference)
			to_reference = target->node.as.reference;

		Node result {};
		result.type = Node::Type::Reference;
		result.as.reference = to_reference;
		result.as.reference->ref_count++;
		return result;
	}

	static BAN::ErrorOr<Node> parse_condrefof_op(ParseContext& context)
	{
		dprintln_if(AML_DUMP_FUNCTION_CALLS, "parse_cond_refof_op");

		ASSERT(context.aml_data.size() >= 2);
		ASSERT(static_cast<AML::Byte>(context.aml_data[0]) == AML::Byte::ExtOpPrefix);
		ASSERT(static_cast<AML::ExtOp>(context.aml_data[1]) == AML::ExtOp::CondRefOfOp);
		context.aml_data = context.aml_data.slice(2);

		auto [source_type, source] = TRY(parse_super_name(context, false));
		if (source_type == TargetType::Debug)
		{
			dwarnln("TODO: CondRefOf debug");
			return BAN::Error::from_errno(ENOTSUP);
		}

		if (context.aml_data.empty())
			return BAN::Error::from_errno(ENODATA);

		if (context.aml_data[0] == 0x00)
			context.aml_data = context.aml_data.slice(1);
		else
		{
			auto [target_type, target] = TRY(parse_super_name(context, true));

			if (source)
			{
				Node reference {};
				reference.type = Node::Type::Reference;
				reference.as.reference = source;
				reference.as.reference->ref_count++;
				TRY(perform_store(reference, target, target_type));
			}
		}

		Node result {};
		result.type = Node::Type::Integer;
		result.as.integer.value = source ? ONES : ZERO;
		return result;
	}

	static BAN::ErrorOr<Node> parse_derefof_op(ParseContext& context)
	{
		dprintln_if(AML_DUMP_FUNCTION_CALLS, "parse_derefof_op");

		ASSERT(!context.aml_data.empty());
		ASSERT(static_cast<AML::Byte>(context.aml_data[0]) == AML::Byte::DerefOfOp);
		context.aml_data = context.aml_data.slice(1);

		return derefof_impl(TRY(parse_node(context)));
	}

	static BAN::ErrorOr<void> parse_scope_contents(Scope&& scope, BAN::ConstByteSpan aml_data)
	{
		ParseContext scope_context;
		scope_context.scope      = BAN::move(scope);
		scope_context.aml_data   = aml_data;
		scope_context.call_depth = 0;

		while (!scope_context.aml_data.empty())
		{
			auto parse_result = parse_node_or_execution_flow(scope_context);
			if (parse_result.is_error())
			{
				dwarnln("Failed to parse scope {}: {}", scope_context.scope, parse_result.error());
				return parse_result.release_error();
			}

			auto [execution_flow, node] = parse_result.release_value();
			if (execution_flow != ExecutionFlow::Normal)
			{
				dwarnln("Scope got execution flow {}", static_cast<int>(execution_flow));
				return BAN::Error::from_errno(EINVAL);
			}
		}

		return {};
	}

	static BAN::ErrorOr<void> parse_scope_op(ParseContext& context)
	{
		dprintln_if(AML_DUMP_FUNCTION_CALLS, "parse_scope_op");

		ASSERT(!context.aml_data.empty());
		ASSERT(static_cast<AML::Byte>(context.aml_data[0]) == AML::Byte::ScopeOp);
		context.aml_data = context.aml_data.slice(1);

		auto scope_pkg = TRY(parse_pkg(context.aml_data));
		auto scope_name = TRY(parse_name_string(scope_pkg));

		auto [object_path, object] = TRY(Namespace::root_namespace().find_named_object(context.scope, scope_name));
		if (object == nullptr)
		{
			dwarnln("ScopeOp scope '{}'.'{}' does not exists", context.scope, scope_name);
			return {};
		}

		switch (object->node.type)
		{
			case Node::Type::Device:
			case Node::Type::Processor:
			case Node::Type::PowerResource:
			case Node::Type::ThermalZone:
			case Node::Type::PredefinedScope:
				break;
			default:
				dwarnln("ScopeOp on non-scope object");
				return BAN::Error::from_errno(EINVAL);
		}

		TRY(parse_scope_contents(BAN::move(object_path), scope_pkg));
		return {};
	}

	static BAN::ErrorOr<void> parse_notify_op(ParseContext& context)
	{
		dprintln_if(AML_DUMP_FUNCTION_CALLS, "parse_scope_op");

		ASSERT(!context.aml_data.empty());
		ASSERT(static_cast<AML::Byte>(context.aml_data[0]) == AML::Byte::NotifyOp);
		context.aml_data = context.aml_data.slice(1);

		auto [obj_path, obj_ref] = TRY(parse_super_name(context, true));
		auto value = TRY(convert_node(TRY(parse_node(context)), ConvInteger, sizeof(uint64_t)));

		if (obj_ref == nullptr)
			return {};

		dwarnln("TODO: handle notify({}, {})", obj_ref->node, value);

		return {};
	}

	static BAN::ErrorOr<void> parse_event_op(ParseContext& context)
	{
		dprintln_if(AML_DUMP_FUNCTION_CALLS, "parse_device_op");

		ASSERT(context.aml_data.size() >= 2);
		ASSERT(static_cast<AML::Byte>(context.aml_data[0]) == AML::Byte::ExtOpPrefix);
		ASSERT(static_cast<AML::ExtOp>(context.aml_data[1]) == AML::ExtOp::EventOp);
		context.aml_data = context.aml_data.slice(2);

		auto event_name = TRY(parse_name_string(context.aml_data));

		Node event_node {};
		event_node.type = Node::Type::Event;
		event_node.as.event.signal_count = 0;

		auto absolute_path = TRY(Namespace::root_namespace().add_named_object(context, event_name, BAN::move(event_node)));
		if (absolute_path.parts.empty())
		{
			dwarnln("Could not add Event '{}'.'{}' to namespace", context.scope, event_name);
			return {};
		}

		return {};
	}

	static BAN::ErrorOr<void> parse_reset_signal_op(ParseContext& context)
	{
		dprintln_if(AML_DUMP_FUNCTION_CALLS, "parse_reset_signal_op");

		ASSERT(context.aml_data.size() >= 2);
		ASSERT(static_cast<AML::Byte>(context.aml_data[0]) == AML::Byte::ExtOpPrefix);
		const auto opcode = static_cast<AML::ExtOp>(context.aml_data[1]);
		context.aml_data = context.aml_data.slice(2);

		auto [_, event_ref] = TRY(parse_super_name(context, true));
		if (event_ref == nullptr)
		{
			dwarnln("ResetOp/SignalOp event is null");
			return BAN::Error::from_errno(EINVAL);
		}
		if (event_ref->node.type != Node::Type::Event)
		{
			dwarnln("ResetOp/SignalOp Object '{}' is not an event", event_ref->node);
			return BAN::Error::from_errno(EINVAL);
		}

		switch (opcode)
		{
			case AML::ExtOp::ResetOp:
				event_ref->node.as.event.signal_count = 0;
				break;
			case AML::ExtOp::SignalOp:
				event_ref->node.as.event.signal_count++;
				break;
			default:
				ASSERT_NOT_REACHED();
		}

		return {};
	}


	static BAN::ErrorOr<Node> parse_wait_op(ParseContext& context)
	{
		dprintln_if(AML_DUMP_FUNCTION_CALLS, "parse_wait_op");

		ASSERT(context.aml_data.size() >= 2);
		ASSERT(static_cast<AML::Byte>(context.aml_data[0]) == AML::Byte::ExtOpPrefix);
		ASSERT(static_cast<AML::ExtOp>(context.aml_data[1]) == AML::ExtOp::WaitOp);
		context.aml_data = context.aml_data.slice(2);

		auto [_, event_ref] = TRY(parse_super_name(context, true));
		if (event_ref == nullptr)
		{
			dwarnln("WaitOp event is null");
			return BAN::Error::from_errno(EINVAL);
		}
		if (event_ref->node.type != Node::Type::Event)
		{
			dwarnln("WaitOp Object '{}' is not an event", event_ref->node);
			return BAN::Error::from_errno(EINVAL);
		}

		const uint64_t timeout_ms = TRY(convert_node(TRY(parse_node(context)), ConvInteger, sizeof(uint64_t))).as.integer.value;
		const uint64_t wake_time_ms = (timeout_ms >= 0xFFFF) ? BAN::numeric_limits<uint64_t>::max() : SystemTimer::get().ms_since_boot() + timeout_ms;

		uint64_t return_value = 0;
		for (;;)
		{
			if (event_ref->node.as.event.signal_count > 0)
			{
				event_ref->node.as.event.signal_count--;
				return_value = ZERO;
				break;
			}

			if (wake_time_ms >= SystemTimer::get().ms_since_boot())
			{
				return_value = ONES;
				break;
			}

			Processor::yield();
		}

		Node result;
		result.type = Node::Type::Integer;
		result.as.integer.value = return_value;
		return result;
	}

	static BAN::ErrorOr<void> parse_sleep_op(ParseContext& context)
	{
		dprintln_if(AML_DUMP_FUNCTION_CALLS, "parse_sleep_op");

		ASSERT(context.aml_data.size() >= 2);
		ASSERT(static_cast<AML::Byte>(context.aml_data[0]) == AML::Byte::ExtOpPrefix);
		ASSERT(static_cast<AML::ExtOp>(context.aml_data[1]) == AML::ExtOp::SleepOp);
		context.aml_data = context.aml_data.slice(2);

		auto milliseconds = TRY(convert_node(TRY(parse_node(context)), ConvInteger, sizeof(uint64_t)));

		const uint64_t wakeup_ms = SystemTimer::get().ms_since_boot() + milliseconds.as.integer.value;
		for (uint64_t curr_ms = SystemTimer::get().ms_since_boot(); curr_ms < wakeup_ms; curr_ms = SystemTimer::get().ms_since_boot())
			SystemTimer::get().sleep_ms(wakeup_ms - curr_ms);

		return {};
	}

	static BAN::ErrorOr<void> parse_stall_op(ParseContext& context)
	{
		dprintln_if(AML_DUMP_FUNCTION_CALLS, "parse_stall_op");

		ASSERT(context.aml_data.size() >= 2);
		ASSERT(static_cast<AML::Byte>(context.aml_data[0]) == AML::Byte::ExtOpPrefix);
		ASSERT(static_cast<AML::ExtOp>(context.aml_data[1]) == AML::ExtOp::StallOp);
		context.aml_data = context.aml_data.slice(2);

		const auto microseconds = TRY(convert_node(TRY(parse_node(context)), ConvInteger, sizeof(uint64_t))).as.integer.value;

		if (microseconds > 100)
		{
			dwarnln("Stall for {} us", microseconds);
			return BAN::Error::from_errno(EINVAL);
		}

		const uint64_t wakeup_ns = SystemTimer::get().ns_since_boot() + microseconds * 1000;
		while (SystemTimer::get().ns_since_boot() < wakeup_ns)
			Processor::pause();

		return {};
	}

	static BAN::ErrorOr<void> parse_device_op(ParseContext& context)
	{
		dprintln_if(AML_DUMP_FUNCTION_CALLS, "parse_device_op");

		ASSERT(context.aml_data.size() >= 2);
		ASSERT(static_cast<AML::Byte>(context.aml_data[0]) == AML::Byte::ExtOpPrefix);
		ASSERT(static_cast<AML::ExtOp>(context.aml_data[1]) == AML::ExtOp::DeviceOp);
		context.aml_data = context.aml_data.slice(2);

		auto device_pkg = TRY(parse_pkg(context.aml_data));
		auto device_name = TRY(parse_name_string(device_pkg));

		Node device_node {};
		device_node.type = Node::Type::Device;

		auto absolute_path = TRY(Namespace::root_namespace().add_named_object(context, device_name, BAN::move(device_node)));
		if (absolute_path.parts.empty())
		{
			dwarnln("Could not add Device '{}'.'{}' to namespace", context.scope, device_name);
			return {};
		}

		TRY(parse_scope_contents(BAN::move(absolute_path), device_pkg));
		return {};
	}

	static BAN::ErrorOr<void> parse_processor_op(ParseContext& context)
	{
		dprintln_if(AML_DUMP_FUNCTION_CALLS, "parse_processor_op");

		ASSERT(context.aml_data.size() >= 2);
		ASSERT(static_cast<AML::Byte>(context.aml_data[0]) == AML::Byte::ExtOpPrefix);
		ASSERT(static_cast<AML::ExtOp>(context.aml_data[1]) == AML::ExtOp::ProcessorOp);
		context.aml_data = context.aml_data.slice(2);

		auto processor_pkg = TRY(parse_pkg(context.aml_data));
		auto processor_name = TRY(parse_name_string(processor_pkg));

		// FIXME: do something with ProcID, PblkAddr, PblkLen?
		if (processor_pkg.size() < 6)
			return BAN::Error::from_errno(ENODATA);
		processor_pkg = processor_pkg.slice(6);

		Node processor_node {};
		processor_node.type = Node::Type::Processor;

		auto absolute_path = TRY(Namespace::root_namespace().add_named_object(context, processor_name, BAN::move(processor_node)));
		if (absolute_path.parts.empty())
		{
			dwarnln("Could not add Processor '{}'.'{}' to namespace", context.scope, processor_name);
			return {};
		}

		TRY(parse_scope_contents(BAN::move(absolute_path), processor_pkg));
		return {};
	}

	static BAN::ErrorOr<void> parse_power_resource_op(ParseContext& context)
	{
		dprintln_if(AML_DUMP_FUNCTION_CALLS, "parse_power_resource_op");

		ASSERT(context.aml_data.size() >= 2);
		ASSERT(static_cast<AML::Byte>(context.aml_data[0]) == AML::Byte::ExtOpPrefix);
		ASSERT(static_cast<AML::ExtOp>(context.aml_data[1]) == AML::ExtOp::PowerResOp);
		context.aml_data = context.aml_data.slice(2);

		auto power_resource_pkg = TRY(parse_pkg(context.aml_data));
		auto power_resource_name = TRY(parse_name_string(power_resource_pkg));

		// FIXME: do something with SystemLevel, ResourceOrder?
		if (power_resource_pkg.size() < 3)
			return BAN::Error::from_errno(ENODATA);
		power_resource_pkg = power_resource_pkg.slice(3);

		Node power_resource_node {};
		power_resource_node.type = Node::Type::PowerResource;

		auto absolute_path = TRY(Namespace::root_namespace().add_named_object(context, power_resource_name, BAN::move(power_resource_node)));
		if (absolute_path.parts.empty())
		{
			dwarnln("Could not add Power Resource '{}'.'{}' to namespace", context.scope, power_resource_name);
			return {};
		}

		TRY(parse_scope_contents(BAN::move(absolute_path), power_resource_pkg));
		return {};
	}

	static BAN::ErrorOr<void> parse_thermal_zone_op(ParseContext& context)
	{
		dprintln_if(AML_DUMP_FUNCTION_CALLS, "parse_thermal_zone_op");

		ASSERT(context.aml_data.size() >= 2);
		ASSERT(static_cast<AML::Byte>(context.aml_data[0]) == AML::Byte::ExtOpPrefix);
		ASSERT(static_cast<AML::ExtOp>(context.aml_data[1]) == AML::ExtOp::ThermalZoneOp);
		context.aml_data = context.aml_data.slice(2);

		auto thermal_zone_pkg = TRY(parse_pkg(context.aml_data));
		auto thermal_zone_name = TRY(parse_name_string(thermal_zone_pkg));

		Node thermal_zone_node {};
		thermal_zone_node.type = Node::Type::ThermalZone;

		auto absolute_path = TRY(Namespace::root_namespace().add_named_object(context, thermal_zone_name, BAN::move(thermal_zone_node)));
		if (absolute_path.parts.empty())
		{
			dwarnln("Could not add Thermal Zone '{}'.'{}' to namespace", context.scope, thermal_zone_name);
			return {};
		}

		TRY(parse_scope_contents(BAN::move(absolute_path), thermal_zone_pkg));
		return {};
	}

	static BAN::ErrorOr<void> parse_mutex_op(ParseContext& context)
	{
		dprintln_if(AML_DUMP_FUNCTION_CALLS, "parse_mutex_op");

		ASSERT(context.aml_data.size() >= 2);
		ASSERT(static_cast<AML::Byte>(context.aml_data[0]) == AML::Byte::ExtOpPrefix);
		ASSERT(static_cast<AML::ExtOp>(context.aml_data[1]) == AML::ExtOp::MutexOp);
		context.aml_data = context.aml_data.slice(2);

		auto mutex_name = TRY(parse_name_string(context.aml_data));
		if (context.aml_data.empty())
			return BAN::Error::from_errno(ENODATA);
		const uint8_t mutex_flags = context.aml_data[0];
		context.aml_data = context.aml_data.slice(1);

		if (mutex_flags & 0xF0)
		{
			dwarnln("MutexOp flags has bits in top nibble set");
			return BAN::Error::from_errno(EINVAL);
		}

		Node mutex;
		mutex.type = Node::Type::Mutex;
		mutex.as.mutex = new Mutex();
		if (mutex.as.mutex == nullptr)
			return BAN::Error::from_errno(ENOMEM);
		mutex.as.mutex->sync_level = mutex_flags;
		mutex.as.mutex->global_lock = false;

		auto absolute_path = TRY(Namespace::root_namespace().add_named_object(context, mutex_name, BAN::move(mutex)));
		if (absolute_path.parts.empty())
		{
			dwarnln("Could not add Mutex '{}'.'{}' to namespace", context.scope, mutex);
			return {};
		}

		return {};
	}

	static BAN::ErrorOr<void> parse_fatal_op(ParseContext& context)
	{
		dprintln_if(AML_DUMP_FUNCTION_CALLS, "parse_fatal_op");

		ASSERT(context.aml_data.size() >= 2);
		ASSERT(static_cast<AML::Byte>(context.aml_data[0]) == AML::Byte::ExtOpPrefix);
		ASSERT(static_cast<AML::ExtOp>(context.aml_data[1]) == AML::ExtOp::FatalOp);
		context.aml_data = context.aml_data.slice(2);

		if (context.aml_data.size() < 5)
			return BAN::Error::from_errno(ENODATA);

		const uint8_t fatal_type = context.aml_data[0];

		const uint32_t fatal_code = 0
			| ((uint32_t)context.aml_data[1] <<  0)
			| ((uint32_t)context.aml_data[2] <<  8)
			| ((uint32_t)context.aml_data[3] << 16)
			| ((uint32_t)context.aml_data[4] << 24);

		context.aml_data = context.aml_data.slice(5);

		const auto fatal_arg = TRY(parse_node(context));

		derrorln("FATAL: type {2H}, code {8H}, arg {}", fatal_type, fatal_code, fatal_arg);

		return {};
	}

	static BAN::ErrorOr<Node> parse_acquire_op(ParseContext& context)
	{
		dprintln_if(AML_DUMP_FUNCTION_CALLS, "parse_acquire_op");

		ASSERT(context.aml_data.size() >= 2);
		ASSERT(static_cast<AML::Byte>(context.aml_data[0]) == AML::Byte::ExtOpPrefix);
		ASSERT(static_cast<AML::ExtOp>(context.aml_data[1]) == AML::ExtOp::AcquireOp);
		context.aml_data = context.aml_data.slice(2);

		auto [type, sync_object] = TRY(parse_super_name(context, true));
		if (type == TargetType::Debug)
		{
			dwarnln("AquireOp sync object is Debug");
			return BAN::Error::from_errno(EINVAL);
		}

		ASSERT(sync_object);
		if (sync_object->node.type != Node::Type::Mutex)
		{
			dwarnln("AquireOp sync object is {}", sync_object->node);
			return BAN::Error::from_errno(EINVAL);
		}

		if (context.aml_data.size() < 2)
			return BAN::Error::from_errno(ENODATA);

		const uint16_t timeout_ms = context.aml_data.as<const uint16_t>();
		context.aml_data = context.aml_data.slice(2);

		Node result;
		result.type = Node::Type::Integer;
		result.as.integer.value = BAN::numeric_limits<uint64_t>::max();

		const uint64_t wake_time_ms = (timeout_ms >= 0xFFFF)
			? BAN::numeric_limits<uint64_t>::max()
			: SystemTimer::get().ms_since_boot() + timeout_ms;

		result.as.integer.value = 0;
		while (true)
		{
			if (sync_object->node.as.mutex->mutex.try_lock())
				break;
			if (SystemTimer::get().ms_since_boot() >= wake_time_ms)
				return result;
			SystemTimer::get().sleep_ms(1);
		}

		if (sync_object->node.as.mutex->global_lock)
			ACPI::acquire_global_lock();

		result.as.integer.value = 0;
		return result;
	}

	static BAN::ErrorOr<void> parse_release_op(ParseContext& context)
	{
		dprintln_if(AML_DUMP_FUNCTION_CALLS, "parse_release_op");

		ASSERT(context.aml_data.size() >= 2);
		ASSERT(static_cast<AML::Byte>(context.aml_data[0]) == AML::Byte::ExtOpPrefix);
		ASSERT(static_cast<AML::ExtOp>(context.aml_data[1]) == AML::ExtOp::ReleaseOp);
		context.aml_data = context.aml_data.slice(2);

		auto [type, sync_object] = TRY(parse_super_name(context, true));
		if (type == TargetType::Debug)
		{
			dwarnln("AquireOp sync object is Debug");
			return BAN::Error::from_errno(EINVAL);
		}

		ASSERT(sync_object);
		if (sync_object->node.type != Node::Type::Mutex)
		{
			dwarnln("AquireOp sync object is {}", sync_object->node);
			return BAN::Error::from_errno(EINVAL);
		}

		if (sync_object->node.as.mutex->global_lock)
			ACPI::release_global_lock();

		sync_object->node.as.mutex->mutex.unlock();

		return {};
	}

	static BAN::ErrorOr<void> parse_method_op(ParseContext& context)
	{
		dprintln_if(AML_DUMP_FUNCTION_CALLS, "parse_method_op");

		ASSERT(!context.aml_data.empty());
		ASSERT(static_cast<AML::Byte>(context.aml_data[0]) == AML::Byte::MethodOp);
		context.aml_data = context.aml_data.slice(1);

		auto method_pkg = TRY(parse_pkg(context.aml_data));
		auto method_name = TRY(parse_name_string(method_pkg));
		if (method_pkg.empty())
			return BAN::Error::from_errno(ENODATA);
		const uint8_t method_flags = method_pkg[0];
		method_pkg = method_pkg.slice(1);

		Node method_node {};
		method_node.type = Node::Type::Method;
		method_node.as.method.start      = method_pkg.data();
		method_node.as.method.length     = method_pkg.size();
		method_node.as.method.arg_count  = method_flags & 0x07;
		method_node.as.method.serialized = !!(method_flags & 0x80);
		method_node.as.method.mutex      = nullptr;
		if (method_node.as.method.serialized)
		{
			method_node.as.method.mutex = new Mutex();
			method_node.as.method.mutex->sync_level  = method_flags >> 4;
			method_node.as.method.mutex->global_lock = false;
			method_node.as.method.mutex->ref_count   = 1;
		}

		auto absolute_path = TRY(Namespace::root_namespace().add_named_object(context, method_name, BAN::move(method_node)));
		if (absolute_path.parts.empty())
		{
			dwarnln("Could not add Method '{}'.'{}' to namespace", context.scope, method_name);
			return {};
		}

		return {};
	}

	static BAN::ErrorOr<ExecutionFlowResult> parse_if_op(ParseContext& context)
	{
		dprintln_if(AML_DUMP_FUNCTION_CALLS, "parse_if_op");

		ASSERT(!context.aml_data.empty());
		ASSERT(static_cast<AML::Byte>(context.aml_data[0]) == AML::Byte::IfOp);
		context.aml_data = context.aml_data.slice(1);

		auto if_pkg = TRY(parse_pkg(context.aml_data));

		auto old_aml_data = context.aml_data;
		context.aml_data = if_pkg;

		auto predicate_node = TRY(convert_node(TRY(parse_node(context)), ConvInteger, sizeof(uint64_t)));
		const bool predicate = predicate_node.as.integer.value;

		BAN::ConstByteSpan code_path;
		if (predicate)
			code_path = context.aml_data;

		if (!old_aml_data.empty() && static_cast<AML::Byte>(old_aml_data[0]) == AML::Byte::ElseOp)
		{
			old_aml_data = old_aml_data.slice(1);
			auto else_pkg = TRY(parse_pkg(old_aml_data));
			if (!predicate)
				code_path = else_pkg;
		}

		Node return_value {};
		ExecutionFlow execution_flow = ExecutionFlow::Normal;

		context.aml_data = code_path;
		while (!context.aml_data.empty() && execution_flow == ExecutionFlow::Normal)
		{
			auto parse_result = parse_node_or_execution_flow(context);
			if (parse_result.is_error())
			{
				dwarnln("Failed to parse if body in {}: {}", context.scope, parse_result.error());
				return parse_result.release_error();
			}

			auto [new_execution_flow, node] = parse_result.release_value();
			execution_flow = new_execution_flow;

			switch (execution_flow)
			{
				case ExecutionFlow::Normal:
					break;
				case ExecutionFlow::Break:
					break;
				case ExecutionFlow::Continue:
					break;
				case ExecutionFlow::Return:
					ASSERT(node.has_value());
					return_value = node.release_value();
					break;
			}
		}
		context.aml_data = old_aml_data;

		return ExecutionFlowResult {
			.elem1 = execution_flow,
			.elem2 = BAN::move(return_value),
		};
	}

	static BAN::ErrorOr<ExecutionFlowResult> parse_while_op(ParseContext& context)
	{
		dprintln_if(AML_DUMP_FUNCTION_CALLS, "parse_while_op");

		ASSERT(!context.aml_data.empty());
		ASSERT(static_cast<AML::Byte>(context.aml_data[0]) == AML::Byte::WhileOp);
		context.aml_data = context.aml_data.slice(1);

		auto while_pkg = TRY(parse_pkg(context.aml_data));

		auto old_aml_data = context.aml_data;

		Node return_value {};
		ExecutionFlow execution_flow = ExecutionFlow::Normal;

		const uint64_t while_loop_start_ms = SystemTimer::get().ms_since_boot();
		while (execution_flow == ExecutionFlow::Normal)
		{
			if (auto current_ms = SystemTimer::get().ms_since_boot(); current_ms >= while_loop_start_ms + 5000)
			{
				dwarnln("While loop terminated after {} ms", current_ms - while_loop_start_ms);
				break;
			}

			context.aml_data = while_pkg;

			auto predicate = TRY(convert_node(TRY(parse_node(context)), ConvInteger, sizeof(uint64_t)));
			if (predicate.as.integer.value == 0)
				break;

			while (!context.aml_data.empty() && execution_flow == ExecutionFlow::Normal)
			{
				auto parse_result = parse_node_or_execution_flow(context);
				if (parse_result.is_error())
				{
					dwarnln("Failed to parse while body in {}: {}", context.scope, parse_result.error());
					return parse_result.release_error();
				}

				auto [new_execution_flow, node] = parse_result.release_value();
				execution_flow = new_execution_flow;

				switch (execution_flow)
				{
					case ExecutionFlow::Normal:
						break;
					case ExecutionFlow::Break:
						break;
					case ExecutionFlow::Continue:
						break;
					case ExecutionFlow::Return:
						ASSERT(node.has_value());
						return_value = node.release_value();
						break;
				}
			}

			if (execution_flow == ExecutionFlow::Continue)
				execution_flow = ExecutionFlow::Normal;
		}

		context.aml_data = old_aml_data;

		if (execution_flow == ExecutionFlow::Break)
			execution_flow = ExecutionFlow::Normal;

		return ExecutionFlowResult {
			.elem1 = execution_flow,
			.elem2 = BAN::move(return_value),
		};
	}

	static BAN::ErrorOr<Node> parse_load_op(ParseContext& context)
	{
		dprintln_if(AML_DUMP_FUNCTION_CALLS, "parse_load_op");

		ASSERT(context.aml_data.size() >= 2);
		ASSERT(static_cast<AML::Byte>(context.aml_data[0]) == AML::Byte::ExtOpPrefix);
		ASSERT(static_cast<AML::ExtOp>(context.aml_data[1]) == AML::ExtOp::LoadOp);
		context.aml_data = context.aml_data.slice(2);

		auto load_name = TRY(parse_name_string(context.aml_data));
		auto [named_object_path, named_object] = TRY(Namespace::root_namespace().find_named_object(context.scope, load_name));

		Node result;
		result.type = Node::Type::Integer;
		result.as.integer.value = 0;

		if (named_object == nullptr)
			dwarnln("Load target does not exist");
		else
		{
			switch (named_object->node.type)
			{
				case Node::Type::Buffer:
				{
					auto data = BAN::ConstByteSpan(named_object->node.as.str_buf->bytes, named_object->node.as.str_buf->size);
					if (auto ret = Namespace::root_namespace().parse(data); ret.is_error())
						dwarnln("Failed to load {}: {}", named_object->node, ret.error());
					else
						result.as.integer.value = BAN::numeric_limits<uint64_t>::max();
					break;
				}
				default:
					dwarnln("TODO: Load({}): {}", named_object_path, named_object->node);
					return BAN::Error::from_errno(ENOTSUP);
			}
		}

		TRY(store_into_target(context, result));

		return result;
	}

	BAN::ErrorOr<Node> parse_timer_op(ParseContext& context)
	{
		dprintln_if(AML_DUMP_FUNCTION_CALLS, "parse_load_op");

		ASSERT(context.aml_data.size() >= 2);
		ASSERT(static_cast<AML::Byte>(context.aml_data[0]) == AML::Byte::ExtOpPrefix);
		ASSERT(static_cast<AML::ExtOp>(context.aml_data[1]) == AML::ExtOp::TimerOp);
		context.aml_data = context.aml_data.slice(2);

		Node result;
		result.type = Node::Type::Integer;
		result.as.integer.value = SystemTimer::get().ns_since_boot() / 100;
		return result;
	}

	BAN::ErrorOr<Node> evaluate_node(const Scope& node_path, const Node& node)
	{
		switch (node.type)
		{
			case Node::Type::Uninitialized:
			case Node::Type::Debug:
			case Node::Type::OpRegion:
			case Node::Type::Event:
			case Node::Type::Device:
			case Node::Type::Processor:
			case Node::Type::PowerResource:
			case Node::Type::ThermalZone:
			case Node::Type::Mutex:
			case Node::Type::PredefinedScope:
			case Node::Type::Count:
				break;
			case Node::Type::Integer:
			case Node::Type::String:
			case Node::Type::Package:
			case Node::Type::Buffer:
			case Node::Type::Index:
			case Node::Type::Reference:
				return TRY(node.copy());
			case Node::Type::BufferField:
				dwarnln("TODO: evaluate BufferField");
				return BAN::Error::from_errno(ENOTSUP);
			case Node::Type::FieldUnit:
				return convert_from_field_unit(node, ConvInteger | ConvBuffer, sizeof(uint64_t));
			case Node::Type::Method:
				if (node.as.method.arg_count != 0)
					return BAN::Error::from_errno(EFAULT);
				return TRY(method_call(node_path, node, BAN::Array<Reference*, 7>{}));
		}

		dwarnln("evaluate {}", node);
		return BAN::Error::from_errno(EINVAL);
	}

	static BAN::ErrorOr<Node> method_call_impl(ParseContext& context)
	{
		dprintln_if(AML_DUMP_FUNCTION_CALLS, "method_call '{}'", context.scope);

		if (context.call_depth >= 128)
		{
			dwarnln("Terminating recursive method call");

			Node return_value {};
			return_value.type = Node::Type::Integer;
			return_value.as.integer.value = 0;
			return return_value;
		}

		TRY(context.allocate_locals());

		Node return_value {};

		context.call_depth++;
		while (!context.aml_data.empty() && return_value.type == Node::Type::Uninitialized)
		{
			auto [execution_flow, node] = TRY(parse_node_or_execution_flow(context));

			switch (execution_flow)
			{
				case ExecutionFlow::Normal:
					break;
				case ExecutionFlow::Break:
					dwarnln("BreakOp in method");
					return BAN::Error::from_errno(EINVAL);
				case ExecutionFlow::Continue:
					dwarnln("ContinueOp in method");
					return BAN::Error::from_errno(EINVAL);
				case ExecutionFlow::Return:
					ASSERT(node.has_value());
					return_value = node.release_value();
					break;
			}
		}
		context.call_depth--;

		while (!context.created_nodes.empty())
		{
			TRY(Namespace::root_namespace().remove_named_object(context.created_nodes.back()));
			context.created_nodes.pop_back();
		}

		// In the absence of an explicit Return () statement, the return value to the caller is undefined.
		// We will just return 0 to keep things simple :)
		if (return_value.type == Node::Type::Uninitialized)
		{
			return_value.type = Node::Type::Integer;
			return_value.as.integer.value = 0;
		}

		return return_value;
	}

	BAN::ErrorOr<Node> method_call(const Scope& scope, const Node& method_node, BAN::Array<Reference*, 7>&& args, uint32_t call_depth)
	{
		ASSERT(method_node.type == Node::Type::Method);

		size_t argc = 0;
		for (argc = 0; argc < args.size(); argc++)
			if (args[argc] == nullptr)
				break;
		for (size_t i = argc; i < args.size(); i++)
			ASSERT(args[i] == nullptr);

		const auto& method = method_node.as.method;

		if (argc != method.arg_count)
		{
			dwarnln("{} takes {} arguments but {} were provided", scope, method.arg_count, argc);
			return BAN::Error::from_errno(EINVAL);
		}

		if (method.override_func)
			return method.override_func(args);

		if (method.serialized)
		{
			ASSERT(method.mutex);
			method.mutex->mutex.lock();
		}

		// FIXME: I'm pretty sure arguments are leaking memory

		ParseContext context;
		context.scope = TRY(scope.copy());
		context.aml_data = BAN::ConstByteSpan(method.start, method.length);
		context.call_depth = call_depth;
		for (size_t i = 0; i < args.size(); i++)
			context.args[i] = args[i];

		auto result = method_call_impl(context);
		if (method.serialized)
			method.mutex->mutex.unlock();

		return result;
	}

	BAN::ErrorOr<Node> method_call(const Scope& scope, const Node& method, Node&& arg0, Node&& arg1, Node&& arg2, Node&& arg3, Node&& arg4, Node&& arg5, Node&& arg6)
	{
		BAN::Array<Reference*, 7> args(nullptr);

#define INIT_ARGn(n)                                    \
		if (arg ## n.type != Node::Type::Uninitialized) \
		{                                               \
			args[n] = new Reference();                  \
			if (args[n] == nullptr)                     \
				return BAN::Error::from_errno(ENOMEM);  \
			args[n]->ref_count = 1;                     \
			args[n]->node = BAN::move(arg ## n);        \
		}

		INIT_ARGn(0);
		INIT_ARGn(1);
		INIT_ARGn(2);
		INIT_ARGn(3);
		INIT_ARGn(4);
		INIT_ARGn(5);
		INIT_ARGn(6);
#undef INIT_ARGn

		return method_call(scope, method, BAN::move(args));
	}

// FIXME: WHY TF IS THIS USING OVER 2 KiB of stack
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wstack-usage="
	BAN::ErrorOr<Node> parse_node(ParseContext& context, bool return_ref)
	{
		if (context.aml_data.empty())
			return BAN::Error::from_errno(ENODATA);

		if (context.aml_data[0] == static_cast<uint8_t>(AML::Byte::ExtOpPrefix))
		{
			if (context.aml_data.size() < 2)
				return BAN::Error::from_errno(ENODATA);

			const uint8_t opcode = context.aml_data[1];
			switch (static_cast<AML::ExtOp>(opcode))
			{
				case AML::ExtOp::CondRefOfOp:
					return TRY(parse_condrefof_op(context));
				case AML::ExtOp::AcquireOp:
					return TRY(parse_acquire_op(context));
				case AML::ExtOp::LoadOp:
					return TRY(parse_load_op(context));
				case AML::ExtOp::TimerOp:
					return TRY(parse_timer_op(context));
				case AML::ExtOp::WaitOp:
					return TRY(parse_wait_op(context));
				case AML::ExtOp::DebugOp:
				{
					context.aml_data = context.aml_data.slice(2);
					Node debug;
					debug.type = Node::Type::Debug;
					return BAN::move(debug);
				}
				default:
					break;
			}

			dwarnln("TODO: AML opcode {2H}e", opcode);
			return BAN::Error::from_errno(ENOTSUP);
		}

		const uint8_t opcode = context.aml_data[0];
		switch (static_cast<AML::Byte>(opcode))
		{
			case AML::Byte::ZeroOp:
			case AML::Byte::OneOp:
			case AML::Byte::OnesOp:
			case AML::Byte::BytePrefix:
			case AML::Byte::WordPrefix:
			case AML::Byte::DWordPrefix:
			case AML::Byte::QWordPrefix:
				return TRY(parse_integer(context.aml_data));
			case AML::Byte::StringPrefix:
				return TRY(parse_string(context.aml_data));
			case AML::Byte::BufferOp:
				return TRY(parse_buffer_op(context));
			case AML::Byte::PackageOp:
			case AML::Byte::VarPackageOp:
				return TRY(parse_package_op(context));
			case AML::Byte::SizeOfOp:
				return TRY(parse_sizeof_op(context));
			case AML::Byte::RefOfOp:
				return TRY(parse_refof_op(context));
			case AML::Byte::DerefOfOp:
				return TRY(parse_derefof_op(context));
			case AML::Byte::StoreOp:
				return TRY(parse_store_op(context));
			case AML::Byte::CopyObjectOp:
				return TRY(parse_copy_object_op(context));
			case AML::Byte::ConcatOp:
				return TRY(parse_concat_op(context));
			case AML::Byte::MidOp:
				return TRY(parse_mid_op(context));
			case AML::Byte::IndexOp:
				return TRY(parse_index_op(context));
			case AML::Byte::ObjectTypeOp:
				return TRY(parse_object_type_op(context));
			case AML::Byte::MatchOp:
				return TRY(parse_match_op(context));
			case AML::Byte::ToBufferOp:
			case AML::Byte::ToDecimalStringOp:
			case AML::Byte::ToHexStringOp:
			case AML::Byte::ToIntegerOp:
				return TRY(parse_explicit_conversion(context));
			case AML::Byte::ToStringOp:
				return TRY(parse_to_string_op(context));
			case AML::Byte::IncrementOp:
			case AML::Byte::DecrementOp:
				return TRY(parse_inc_dec_op(context));
			case AML::Byte::NotOp:
				return TRY(parse_unary_integer_op(context));
			case AML::Byte::AddOp:
			case AML::Byte::SubtractOp:
			case AML::Byte::MultiplyOp:
			case AML::Byte::DivideOp:
			case AML::Byte::ShiftLeftOp:
			case AML::Byte::ShiftRightOp:
			case AML::Byte::AndOp:
			case AML::Byte::NandOp:
			case AML::Byte::OrOp:
			case AML::Byte::NorOp:
			case AML::Byte::XorOp:
			case AML::Byte::ModOp:
				return TRY(parse_binary_integer_op(context));
			case AML::Byte::LAndOp:
			case AML::Byte::LEqualOp:
			case AML::Byte::LGreaterOp:
			case AML::Byte::LLessOp:
			case AML::Byte::LNotOp:
			case AML::Byte::LOrOp:
				return TRY(parse_logical_op(context));
			case AML::Byte::FindSetLeftBitOp:
			case AML::Byte::FindSetRightBitOp:
				return TRY(parse_find_set_bit_op(context));
			case AML::Byte::Local0:
			case AML::Byte::Local1:
			case AML::Byte::Local2:
			case AML::Byte::Local3:
			case AML::Byte::Local4:
			case AML::Byte::Local5:
			case AML::Byte::Local6:
			case AML::Byte::Local7:
			{
				const uint8_t local_index = opcode - static_cast<uint8_t>(AML::Byte::Local0);
				context.aml_data = context.aml_data.slice(1);
				if (context.locals[local_index]->node.type == Node::Type::Uninitialized)
				{
					dwarnln("Trying to access uninitialized local");
					return BAN::Error::from_errno(EINVAL);
				}
				if (!return_ref)
					return TRY(context.locals[local_index]->node.copy());
				Node reference;
				reference.type = Node::Type::Reference;
				reference.as.reference = context.locals[local_index];
				reference.as.reference->ref_count++;
				return reference;
			}
			case AML::Byte::Arg0:
			case AML::Byte::Arg1:
			case AML::Byte::Arg2:
			case AML::Byte::Arg3:
			case AML::Byte::Arg4:
			case AML::Byte::Arg5:
			case AML::Byte::Arg6:
			{
				const uint8_t arg_index = opcode - static_cast<uint8_t>(AML::Byte::Arg0);
				context.aml_data = context.aml_data.slice(1);
				if (context.args[arg_index] == nullptr || context.args[arg_index]->node.type == Node::Type::Uninitialized)
				{
					dwarnln("Trying to access uninitialized arg");
					return BAN::Error::from_errno(EINVAL);
				}
				if (!return_ref)
					return TRY(context.args[arg_index]->node.copy());
				Node reference;
				reference.type = Node::Type::Reference;
				reference.as.reference = context.args[arg_index];
				reference.as.reference->ref_count++;
				return reference;
			}
			default:
				break;
		}

		if (!is_name_string_start(context.aml_data[0]))
		{
			dwarnln("TODO: AML opcode {2H}", opcode);
			return BAN::Error::from_errno(ENOTSUP);
		}

		auto name_string = TRY(parse_name_string(context.aml_data));

		auto [object_scope, named_object] = TRY(Namespace::root_namespace().find_named_object(context.scope, name_string));
		if (named_object == nullptr)
		{
			dwarnln("could not find '{}'.'{}'", context.scope, name_string);
			return BAN::Error::from_errno(ENOENT);
		}

		if (named_object->node.type == Node::Type::Method)
		{
			BAN::Array<Reference*, 7> args;
			for (size_t i = 0; i < named_object->node.as.method.arg_count; i++)
			{
				auto temp = TRY(parse_node(context));
				if (temp.type == Node::Type::Reference)
				{
					args[i] = temp.as.reference;
					args[i]->ref_count++;
				}
				else
				{
					args[i] = new Reference();
					if (args[i] == nullptr)
						return BAN::Error::from_errno(ENOMEM);
					args[i]->node = BAN::move(temp);
					args[i]->ref_count = 1;
				}
			}

			return TRY(method_call(BAN::move(object_scope), named_object->node, BAN::move(args), context.call_depth));
		}

		if (!return_ref)
			return TRY(named_object->node.copy());

		Node reference;
		reference.type = Node::Type::Reference;
		reference.as.reference = named_object;
		reference.as.reference->ref_count++;
		return reference;
	}
#pragma GCC diagnostic pop

// FIXME: WHY TF IS THIS USING ALMOST 2 KiB of stack
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wstack-usage="
	BAN::ErrorOr<ExecutionFlowResult> parse_node_or_execution_flow(ParseContext& context)
	{
		if (context.aml_data.empty())
			return BAN::Error::from_errno(ENODATA);

		auto dummy_return = ExecutionFlowResult {
			.elem1 = ExecutionFlow::Normal,
			.elem2 = BAN::Optional<Node>(),
		};

		if (context.aml_data[0] == static_cast<uint8_t>(AML::Byte::ExtOpPrefix))
		{
			if (context.aml_data.size() < 2)
				return BAN::Error::from_errno(ENODATA);
			switch (static_cast<AML::ExtOp>(context.aml_data[1]))
			{
				case AML::ExtOp::MutexOp:
					TRY(parse_mutex_op(context));
					return dummy_return;
				case AML::ExtOp::FatalOp:
					TRY(parse_fatal_op(context));
					return dummy_return;
				case AML::ExtOp::EventOp:
					TRY(parse_event_op(context));
					return dummy_return;
				case AML::ExtOp::ResetOp:
				case AML::ExtOp::SignalOp:
					TRY(parse_reset_signal_op(context));
					return dummy_return;
				case AML::ExtOp::CreateFieldOp:
					TRY(parse_createfield_op(context));
					return dummy_return;
				case AML::ExtOp::SleepOp:
					TRY(parse_sleep_op(context));
					return dummy_return;
				case AML::ExtOp::StallOp:
					TRY(parse_stall_op(context));
					return dummy_return;
				case AML::ExtOp::ReleaseOp:
					TRY(parse_release_op(context));
					return dummy_return;
				case AML::ExtOp::OpRegionOp:
					TRY(parse_opregion_op(context));
					return dummy_return;
				case AML::ExtOp::FieldOp:
					TRY(parse_field_op(context));
					return dummy_return;
				case AML::ExtOp::IndexFieldOp:
					TRY(parse_index_field_op(context));
					return dummy_return;
				case AML::ExtOp::BankFieldOp:
					TRY(parse_bank_field_op(context));
					return dummy_return;
				case AML::ExtOp::DeviceOp:
					TRY(parse_device_op(context));
					return dummy_return;
				case AML::ExtOp::ProcessorOp:
					TRY(parse_processor_op(context));
					return dummy_return;
				case AML::ExtOp::PowerResOp:
					TRY(parse_power_resource_op(context));
					return dummy_return;
				case AML::ExtOp::ThermalZoneOp:
					TRY(parse_thermal_zone_op(context));
					return dummy_return;
				default:
					break;
			}
		}

		switch (static_cast<AML::Byte>(context.aml_data[0]))
		{
			case AML::Byte::AliasOp:
				TRY(parse_alias_op(context));
				return dummy_return;
			case AML::Byte::NameOp:
				TRY(parse_name_op(context));
				return dummy_return;
			case AML::Byte::MethodOp:
				TRY(parse_method_op(context));
				return dummy_return;
			case AML::Byte::NoopOp:
			case AML::Byte::BreakPointOp:
				context.aml_data = context.aml_data.slice(1);
				return dummy_return;
			case AML::Byte::ScopeOp:
				TRY(parse_scope_op(context));
				return dummy_return;
			case AML::Byte::NotifyOp:
				TRY(parse_notify_op(context));
				return dummy_return;
			case AML::Byte::CreateBitFieldOp:
			case AML::Byte::CreateByteFieldOp:
			case AML::Byte::CreateWordFieldOp:
			case AML::Byte::CreateDWordFieldOp:
			case AML::Byte::CreateQWordFieldOp:
				TRY(parse_createfield_op(context));
				return dummy_return;
			case AML::Byte::IfOp:
				return parse_if_op(context);
			case AML::Byte::WhileOp:
				return parse_while_op(context);
			case AML::Byte::BreakOp:
				dprintln_if(AML_DUMP_FUNCTION_CALLS, "parse_break_op");
				context.aml_data = context.aml_data.slice(1);
				return ExecutionFlowResult {
					.elem1 = ExecutionFlow::Break,
					.elem2 = BAN::Optional<Node>(),
				};
			case AML::Byte::ContinueOp:
				dprintln_if(AML_DUMP_FUNCTION_CALLS, "parse_continue_op");
				context.aml_data = context.aml_data.slice(1);
				return ExecutionFlowResult {
					.elem1 = ExecutionFlow::Continue,
					.elem2 = BAN::Optional<Node>(),
				};
			case AML::Byte::ReturnOp:
			{
				dprintln_if(AML_DUMP_FUNCTION_CALLS, "parse_return_op");
				context.aml_data = context.aml_data.slice(1);
				return ExecutionFlowResult {
					.elem1 = ExecutionFlow::Return,
					.elem2 = TRY(parse_node(context)),
				};
			}
			default:
				break;
		}

		auto node = TRY(parse_node(context));
		return ExecutionFlowResult {
			.elem1 = ExecutionFlow::Normal,
			.elem2 = BAN::move(node)
		};
	}
#pragma GCC diagnostic pop

	BAN::ErrorOr<NameString> NameString::from_string(BAN::StringView name)
	{
		NameString result;

		if (name.front() == '\\')
		{
			result.base = base_root;
			name = name.substring(1);
		}
		else
		{
			for (size_t i = 0; i < name.size() && name[i] == '^'; i++)
				result.base++;
			name = name.substring(result.base);
		}

		if (name.empty())
			return result;

		ASSERT((name.size() % 5) == 4);
		TRY(result.parts.reserve((name.size() / 5) + 1));

		while (!name.empty())
		{
			const uint32_t name_seg {
				(static_cast<uint32_t>(name[0]) <<  0) |
				(static_cast<uint32_t>(name[1]) <<  8) |
				(static_cast<uint32_t>(name[2]) << 16) |
				(static_cast<uint32_t>(name[3]) << 24)
			};
			TRY(result.parts.push_back(name_seg));
			name = name.substring(4);
			if (!name.empty())
			{
				ASSERT(name.front() == '.');
				name = name.substring(1);
			}
		}

		return result;
	}

	ParseContext::~ParseContext()
	{
		for (auto*& local : locals)
		{
			if (local && --local->ref_count == 0)
				delete local;
			local = nullptr;
		}
		for (auto*& arg : args)
		{
			if (arg && --arg->ref_count == 0)
				delete arg;
			arg = nullptr;
		}
	}

	BAN::ErrorOr<void> ParseContext::allocate_locals()
	{
		for (auto*& local : locals)
		{
			ASSERT(local == nullptr);
			local = new Reference();
			if (local == nullptr)
				return BAN::Error::from_errno(ENOMEM);
			local->ref_count = 1;
		}

		return {};
	}

	BAN::ErrorOr<Node> Node::copy() const
	{
		Node result {};
		result.type = this->type;

		switch (this->type)
		{
			case Type::Uninitialized:
				break;
			case Type::Debug:
				break;
			case Type::Integer:
				result.as.integer.value = this->as.integer.value;
				break;
			case Type::String:
			case Type::Buffer:
				ASSERT(this->as.str_buf);
				result.as.str_buf = static_cast<Buffer*>(kmalloc(sizeof(Buffer) + this->as.str_buf->size));
				if (result.as.str_buf == nullptr)
					return BAN::Error::from_errno(ENOMEM);
				memcpy(result.as.str_buf->bytes, this->as.str_buf->bytes, this->as.str_buf->size);
				result.as.str_buf->size = this->as.str_buf->size;
				result.as.str_buf->ref_count = 1;
				break;
			case Type::Package:
				ASSERT(this->as.package);
				result.as.package = static_cast<Package*>(kmalloc(sizeof(Package) + this->as.package->num_elements * sizeof(Package::Element)));
				if (result.as.package == nullptr)
					return BAN::Error::from_errno(ENOMEM);
				result.as.package->num_elements = this->as.package->num_elements;
				result.as.package->ref_count = 1;
				for (size_t i = 0; i < result.as.package->num_elements; i++)
				{
					auto&       dst_elem = result.as.package->elements[i];
					const auto& src_elem =  this->as.package->elements[i];

					if (src_elem.resolved)
					{
						dst_elem.resolved = true;
						dst_elem.value.node = nullptr;
						if (src_elem.value.node)
						{
							dst_elem.value.node = new Node();
							if (dst_elem.value.node == nullptr)
								return BAN::Error::from_errno(ENOMEM);
							*dst_elem.value.node = TRY(src_elem.value.node->copy());
						}
					}
					else
					{
						dst_elem.resolved = false;
						dst_elem.value.location = nullptr;
						if (src_elem.value.location)
						{
							dst_elem.value.location = new Package::Element::Location();
							if (dst_elem.value.location == nullptr)
								return BAN::Error::from_errno(ENOMEM);
							dst_elem.value.location->name = TRY(src_elem.value.location->name.copy());
							dst_elem.value.location->scope = TRY(src_elem.value.location->scope.copy());
						}
					}
				}
				break;
			case Type::BufferField:
				result.as.buffer_field = this->as.buffer_field;
				result.as.buffer_field.buffer->ref_count++;
				break;
			case Type::OpRegion:
				result.as.opregion = this->as.opregion;
				break;
			case Type::FieldUnit:
				result.as.field_unit = this->as.field_unit;
				switch (result.as.field_unit.type)
				{
					case FieldUnit::Type::Field:
						break;
					case FieldUnit::Type::IndexField:
						result.as.field_unit.as.index_field.index->ref_count++;
						result.as.field_unit.as.index_field.data->ref_count++;
						break;
					case FieldUnit::Type::BankField:
						result.as.field_unit.as.bank_field.bank_selector->ref_count++;
						break;
					default:
						ASSERT_NOT_REACHED();
				}
				break;
			case Type::Method:
				result.as.method = this->as.method;
				if (result.as.method.mutex)
					result.as.method.mutex->ref_count++;
				break;
			case Type::Index:
				switch (this->as.index.type)
				{
					case Node::Type::String:
					case Node::Type::Buffer:
						ASSERT(this->as.index.as.str_buf);
						result.as.index.as.str_buf = this->as.index.as.str_buf;
						result.as.index.as.str_buf->ref_count++;
						break;
					case Node::Type::Package:
						ASSERT(this->as.index.as.package);
						result.as.index.as.package = this->as.index.as.package;
						result.as.index.as.package->ref_count++;
						break;
					default: ASSERT_NOT_REACHED();
				}
				result.as.index.type = this->as.index.type;
				result.as.index.index = this->as.index.index;
				break;
			case Type::Reference:
				ASSERT(this->as.reference);
				result.as.reference = this->as.reference;
				result.as.reference->ref_count++;
				break;
			case Type::Event:
				dwarnln("Copy Event");
				return BAN::Error::from_errno(EINVAL);
			case Type::Device:
				dwarnln("Copy Device");
				return BAN::Error::from_errno(EINVAL);
			case Type::Processor:
				dwarnln("Copy Processor");
				return BAN::Error::from_errno(EINVAL);
			case Type::PowerResource:
				dwarnln("Copy PowerResource");
				return BAN::Error::from_errno(EINVAL);
			case Type::ThermalZone:
				dwarnln("Copy ThremalZone");
				return BAN::Error::from_errno(EINVAL);
			case Type::Mutex:
				dwarnln("Copy Mutex");
				return BAN::Error::from_errno(EINVAL);
			case Type::PredefinedScope:
				dwarnln("Copy Scope");
				return BAN::Error::from_errno(EINVAL);
			case Type::Count:
				ASSERT_NOT_REACHED();
		}

		return result;
	}

	Node& Node::operator=(Node&& other)
	{
		clear();

		switch (other.type)
		{
			case Type::Uninitialized:
				break;
			case Type::Debug:
				break;
			case Type::Integer:
				this->as.integer = other.as.integer;
				other.as.integer = {};
				break;
			case Type::String:
			case Type::Buffer:
				this->as.str_buf = other.as.str_buf;
				other.as.str_buf = {};
				break;
			case Type::Package:
				this->as.package = other.as.package;
				other.as.package = {};
				break;
			case Type::BufferField:
				this->as.buffer_field = other.as.buffer_field;
				other.as.buffer_field = {};
				break;
			case Type::OpRegion:
				this->as.opregion = other.as.opregion;
				other.as.opregion = {};
				break;
			case Type::FieldUnit:
				this->as.field_unit = other.as.field_unit;
				other.as.field_unit = {};
				break;
			case Type::Event:
				break;
			case Type::Device:
				break;
			case Type::Processor:
				break;
			case Type::PowerResource:
				break;
			case Type::ThermalZone:
				break;
			case Type::Method:
				this->as.method = other.as.method;
				other.as.method = {};
				break;
			case Type::Mutex:
				this->as.mutex = other.as.mutex;
				other.as.mutex = {};
				break;
			case Type::Index:
				this->as.index = other.as.index;
				other.as.index = {};
				break;
			case Type::Reference:
				this->as.reference = other.as.reference;
				other.as.reference = {};
				break;
			case Type::PredefinedScope:
				break;
			case Type::Count:
				ASSERT_NOT_REACHED();
		}
		this->type = other.type;
		other.type = Node::Type::Uninitialized;

		return *this;
	}

	static void deref_package(Package* package)
	{
		if (package == nullptr || --package->ref_count != 0)
			return;
		for (size_t i = 0; i < package->num_elements; i++)
		{
			auto& elem = package->elements[i];

			if (elem.resolved)
			{
				if (elem.value.node)
					delete elem.value.node;
				elem.value.node = nullptr;
			}
			else
			{
				if (elem.value.location)
					delete elem.value.location;
				elem.value.location = nullptr;
			}
		}
		kfree(package);
	}

	void Node::clear()
	{
		switch (this->type)
		{
			case Type::Uninitialized:
				break;
			case Type::Debug:
				break;
			case Type::Integer:
				break;
			case Type::String:
			case Type::Buffer:
				if (this->as.str_buf && --this->as.str_buf->ref_count == 0)
					kfree(this->as.str_buf);
				this->as.str_buf = {};
				break;
			case Type::Package:
				deref_package(this->as.package);
				this->as.package = {};
				break;
			case Type::BufferField:
				if (this->as.buffer_field.buffer && --this->as.buffer_field.buffer->ref_count == 0)
					delete this->as.buffer_field.buffer;
				this->as.buffer_field = {};
				break;
			case Type::OpRegion:
				this->as.opregion = {};
				break;
			case Type::FieldUnit:
				switch (this->as.field_unit.type)
				{
					case FieldUnit::Type::Field:
						break;
					case FieldUnit::Type::IndexField:
						if (--this->as.field_unit.as.index_field.index->ref_count == 0)
							delete this->as.field_unit.as.index_field.index;
						if (--this->as.field_unit.as.index_field.data->ref_count == 0)
							delete this->as.field_unit.as.index_field.data;
						break;
					case FieldUnit::Type::BankField:
						if (--this->as.field_unit.as.bank_field.bank_selector->ref_count == 0)
							delete this->as.field_unit.as.bank_field.bank_selector;
						break;
					default:
						ASSERT_NOT_REACHED();
				}
				this->as.field_unit = {};
				break;
			case Type::Event:
				break;
			case Type::Device:
				break;
			case Type::Processor:
				break;
			case Type::PowerResource:
				break;
			case Type::ThermalZone:
				break;
			case Type::Method:
				if (this->as.method.mutex && --this->as.method.mutex->ref_count == 0)
					delete this->as.method.mutex;
				this->as.method = {};
				break;
			case Type::Mutex:
				if (this->as.mutex && --this->as.mutex->ref_count == 0)
					delete this->as.mutex;
				this->as.mutex = {};
				break;
			case Type::Index:
				switch (this->as.index.type)
				{
					case Type::Uninitialized:
						break;
					case Type::String:
					case Type::Buffer:
						if (this->as.index.as.str_buf && --this->as.index.as.str_buf->ref_count == 0)
							kfree(this->as.index.as.str_buf);
						break;
					case Type::Package:
						deref_package(this->as.index.as.package);
						break;
					default: ASSERT_NOT_REACHED();
				}
				this->as.index = {};
				break;
			case Type::Reference:
				if (this->as.reference && --this->as.reference->ref_count == 0)
					delete this->as.reference;
				this->as.reference = {};
				break;
			case Type::PredefinedScope:
				break;
			case Type::Count:
				ASSERT_NOT_REACHED();
		}
		this->type = Type::Uninitialized;
	}

}
