//! Recursive-descent AML parser. Walks the entire byte stream — including method
//! bodies — building the ACPI namespace as it goes. It does not *evaluate*
//! anything (no OperationRegion reads, no arithmetic); it parses structure so the
//! cursor stays aligned and every named object is recorded.
//!
//! The one genuine ambiguity in AML is method invocation: a bare NameString in an
//! operand position is a call whose argument count is only known from the method's
//! (earlier) declaration. Because we build the namespace in the same in-order pass,
//! `resolve` finds that declaration and tells us how many operands to consume.
//!
//! Safety net: every object delimited by a PkgLength (Scope/Device/Method/If/While/
//! Field/Buffer/Package/…) is parsed within its known extent, and the cursor is
//! snapped to that extent afterwards. So a mis-resolved invocation can only desync
//! *within* one such object; the enclosing walk realigns at the boundary.

const std = @import("std");
const opcode = @import("opcodes.zig");
const Namespace = @import("namespace.zig").Namespace;
const Node = @import("namespace.zig").Node;
const NodeKind = @import("namespace.zig").NodeKind;

pub const Error = error{ Truncated, Malformed } || std.mem.Allocator.Error;

const maximum_segments = 64;

/// A parsed NameString: an optional root anchor or some parent hops, then a list
/// of 4-byte segments.
const NamePath = struct {
    rooted: bool = false,
    parents: u8 = 0,
    segments: [maximum_segments][4]u8 = undefined,
    count: usize = 0,

    fn slice(self: *const NamePath) []const [4]u8 {
        return self.segments[0..self.count];
    }
};

pub const Parser = struct {
    aml: []const u8,
    position: usize = 0,
    namespace: *Namespace,

    pub fn init(aml: []const u8, namespace: *Namespace) Parser {
        return .{ .aml = aml, .namespace = namespace };
    }

    /// Parse the whole block as a TermList under the namespace root. Returns the
    /// number of bytes consumed — equal to `aml.len` for a clean full traversal.
    pub fn parseAll(self: *Parser) usize {
        self.termList(self.aml.len, self.namespace.root);
        return self.position;
    }

    // --- cursor primitives --------------------------------------------------

    fn eof(self: *Parser) bool {
        return self.position >= self.aml.len;
    }

    fn peek(self: *Parser) ?u8 {
        return if (self.eof()) null else self.aml[self.position];
    }

    fn readByte(self: *Parser) Error!u8 {
        if (self.eof()) return error.Truncated;
        const b = self.aml[self.position];
        self.position += 1;
        return b;
    }

    fn skip(self: *Parser, n: usize) Error!void {
        if (self.position + n > self.aml.len) return error.Truncated;
        self.position += n;
    }

    fn skipCString(self: *Parser) Error!void {
        while (true) {
            const b = try self.readByte();
            if (b == 0) return;
        }
    }

    /// AML PkgLength: the lead byte's top two bits give how many extra bytes
    /// follow; the value counts from the start of the PkgLength field.
    fn readPackageLength(self: *Parser) Error!usize {
        const lead = try self.readByte();
        const follow: usize = lead >> 6;
        if (follow == 0) return lead & 0x3F;
        var value: usize = lead & 0x0F;
        var i: usize = 0;
        while (i < follow) : (i += 1) {
            const b = try self.readByte();
            value |= @as(usize, b) << @intCast(4 + i * 8);
        }
        return value;
    }

    fn readNameSegment(self: *Parser) Error![4]u8 {
        if (self.position + 4 > self.aml.len) return error.Truncated;
        const segment = self.aml[self.position..][0..4].*;
        self.position += 4;
        return segment;
    }

    fn readNameString(self: *Parser) Error!NamePath {
        var name_path = NamePath{};
        // A NameString is either root-anchored or parent-relative, not both.
        if (self.peek() == opcode.root_char) {
            name_path.rooted = true;
            self.position += 1;
        } else {
            while (self.peek() == opcode.parent_prefix_char) : (self.position += 1) name_path.parents += 1;
        }

        const lead = self.peek() orelse return name_path;
        switch (lead) {
            0x00 => self.position += 1, // NullName
            opcode.dual_name_prefix => {
                self.position += 1;
                try self.appendSegment(&name_path);
                try self.appendSegment(&name_path);
            },
            opcode.multi_name_prefix => {
                self.position += 1;
                const count = try self.readByte();
                var i: usize = 0;
                while (i < count) : (i += 1) try self.appendSegment(&name_path);
            },
            else => {
                if (isNameStart(lead)) try self.appendSegment(&name_path);
            },
        }
        return name_path;
    }

    fn appendSegment(self: *Parser, name_path: *NamePath) Error!void {
        const segment = try self.readNameSegment();
        if (name_path.count < maximum_segments) {
            name_path.segments[name_path.count] = segment;
            name_path.count += 1;
        }
    }

    // --- term list / object -------------------------------------------------

    /// Parse objects until `end`, then snap to `end`. Any parse error resyncs to
    /// the boundary rather than propagating — containment for the rare desync.
    fn termList(self: *Parser, end: usize, scope: *Node) void {
        while (self.position < end) {
            self.object(scope) catch break;
        }
        self.position = end;
    }

    /// Parse exactly one object/term at the cursor. Used for both TermObjs and
    /// operands (TermArg / SuperName / Target all reduce to "one object" for the
    /// purpose of advancing the cursor).
    fn object(self: *Parser, scope: *Node) Error!void {
        const lead = self.peek() orelse return error.Truncated;
        if (isNameStart(lead)) return self.nameInvocation(scope);

        _ = try self.readByte();
        switch (lead) {
            // constants and no-operand statements
            opcode.zero_opcode, opcode.one_opcode, opcode.ones_opcode => {},
            opcode.noop_opcode, opcode.continue_opcode, opcode.break_opcode, opcode.break_point_opcode => {},
            opcode.local0_opcode...opcode.local7_opcode => {},
            opcode.arg0_opcode...opcode.arg6_opcode => {},

            // literal data
            opcode.byte_prefix => try self.skip(1),
            opcode.word_prefix => try self.skip(2),
            opcode.dword_prefix => try self.skip(4),
            opcode.qword_prefix => try self.skip(8),
            opcode.string_prefix => try self.skipCString(),

            // data containers (contents skipped via their PkgLength)
            opcode.buffer_opcode, opcode.package_opcode, opcode.var_package_opcode => try self.skipPackage(),

            // namespace modifiers / named objects
            opcode.name_opcode => try self.parseName(scope),
            opcode.alias_opcode => try self.parseAlias(scope),
            opcode.scope_opcode => try self.parseScopeLike(scope, .scope),
            opcode.method_opcode => try self.parseMethod(scope),
            opcode.external_opcode => try self.parseExternal(scope),
            opcode.extended_opcode_prefix => try self.parseExtended(scope),

            // control flow
            opcode.if_opcode => try self.parseIf(scope),
            opcode.else_opcode => try self.parseElse(scope),
            opcode.while_opcode => try self.parseWhile(scope),
            opcode.return_opcode => try self.object(scope),
            opcode.notify_opcode => try self.args(scope, 2),

            // stores / references / unary+target
            opcode.store_opcode => try self.args(scope, 2),
            opcode.ref_of_opcode, opcode.dereference_of_opcode, opcode.size_of_opcode, opcode.object_type_opcode => try self.args(scope, 1),
            opcode.increment_opcode, opcode.decrement_opcode => try self.args(scope, 1),
            opcode.not_opcode, opcode.find_set_left_bit_opcode, opcode.find_set_right_bit_opcode => try self.args(scope, 2),
            opcode.to_buffer_opcode, opcode.to_decimal_string_opcode, opcode.to_hex_string_opcode, opcode.to_integer_opcode => try self.args(scope, 2),
            opcode.copy_object_opcode => try self.args(scope, 2),

            // binary + target
            opcode.add_opcode, opcode.subtract_opcode, opcode.multiply_opcode, opcode.mod_opcode => try self.args(scope, 3),
            opcode.and_opcode, opcode.nand_opcode, opcode.or_opcode, opcode.nor_opcode, opcode.xor_opcode => try self.args(scope, 3),
            opcode.shift_left_opcode, opcode.shift_right_opcode, opcode.concat_opcode, opcode.concat_resource_opcode, opcode.index_opcode => try self.args(scope, 3),
            opcode.divide_opcode => try self.args(scope, 4),
            opcode.to_string_opcode => try self.args(scope, 3),
            opcode.mid_opcode => try self.args(scope, 4),

            // logical
            opcode.land_opcode, opcode.lor_opcode => try self.args(scope, 2),
            opcode.lequal_opcode, opcode.lgreater_opcode, opcode.lless_opcode => try self.args(scope, 2),
            opcode.lnot_opcode => try self.parseLnot(scope),

            opcode.match_opcode => try self.parseMatch(scope),

            // CreateXField: <source> <index> NameString
            opcode.create_dword_field_opcode,
            opcode.create_word_field_opcode,
            opcode.create_byte_field_opcode,
            opcode.create_bit_field_opcode,
            opcode.create_qword_field_opcode,
            => try self.parseCreateField(scope, 2),

            else => return error.Malformed,
        }
    }

    /// Parse `n` operands.
    fn args(self: *Parser, scope: *Node, n: usize) Error!void {
        var i: usize = 0;
        while (i < n) : (i += 1) try self.object(scope);
    }

    /// A NameString in operand/statement position: a method invocation (consuming
    /// the callee's declared argument count) or a plain name reference.
    fn nameInvocation(self: *Parser, scope: *Node) Error!void {
        const name_path = try self.readNameString();
        if (self.namespace.resolve(scope, name_path.rooted, name_path.parents, name_path.slice())) |node| {
            if ((node.kind == .method or node.kind == .external) and node.arg_count > 0) {
                try self.args(scope, node.arg_count);
            }
        }
    }

    /// Skip a PkgLength-delimited body wholesale (Buffer / Package / VarPackage):
    /// the contents are pure data, never namespace declarations.
    fn skipPackage(self: *Parser) Error!void {
        const start = self.position;
        const len = try self.readPackageLength();
        const end = start + len;
        if (end > self.aml.len) return error.Truncated;
        self.position = end;
    }

    // --- namespace objects --------------------------------------------------

    fn parseName(self: *Parser, scope: *Node) Error!void {
        const name_path = try self.readNameString();
        const value_start = self.position;
        try self.object(scope); // the DataReferenceObject value
        const node = try self.namespace.place(scope, name_path.rooted, name_path.parents, name_path.slice(), .name);
        node.value = self.aml[value_start..self.position];
    }

    fn parseAlias(self: *Parser, scope: *Node) Error!void {
        _ = try self.readNameString(); // source
        const name_path = try self.readNameString(); // the alias name
        _ = try self.namespace.place(scope, name_path.rooted, name_path.parents, name_path.slice(), .alias);
    }

    fn parseMethod(self: *Parser, scope: *Node) Error!void {
        const start = self.position;
        const end = start + try self.readPackageLength();
        const name_path = try self.readNameString();
        const flags = try self.readByte();
        const node = try self.namespace.place(scope, name_path.rooted, name_path.parents, name_path.slice(), .method);
        node.arg_count = flags & 0x7;
        // Capture the body for on-demand evaluation and skip it — objects declared
        // inside a method are created at *runtime*, not at load, so they must not
        // become permanent namespace nodes.
        node.value = self.aml[self.position..@min(end, self.aml.len)];
        self.position = end;
    }

    fn parseExternal(self: *Parser, scope: *Node) Error!void {
        const name_path = try self.readNameString();
        _ = try self.readByte(); // object type
        const arg_count = try self.readByte();
        const node = try self.namespace.place(scope, name_path.rooted, name_path.parents, name_path.slice(), .external);
        node.arg_count = arg_count;
    }

    /// Scope / Device / ThermalZone: PkgLength, NameString, then a nested TermList.
    fn parseScopeLike(self: *Parser, scope: *Node, kind: NodeKind) Error!void {
        const start = self.position;
        const end = start + try self.readPackageLength();
        const name_path = try self.readNameString();
        const node = try self.namespace.place(scope, name_path.rooted, name_path.parents, name_path.slice(), kind);
        self.termList(end, node);
    }

    fn parseProcessor(self: *Parser, scope: *Node) Error!void {
        const start = self.position;
        const end = start + try self.readPackageLength();
        const name_path = try self.readNameString();
        try self.skip(6); // ProcID(byte) + PblkAddress(dword) + PblkLen(byte)
        const node = try self.namespace.place(scope, name_path.rooted, name_path.parents, name_path.slice(), .processor);
        self.termList(end, node);
    }

    fn parsePowerResource(self: *Parser, scope: *Node) Error!void {
        const start = self.position;
        const end = start + try self.readPackageLength();
        const name_path = try self.readNameString();
        try self.skip(3); // SystemLevel(byte) + ResourceOrder(word)
        const node = try self.namespace.place(scope, name_path.rooted, name_path.parents, name_path.slice(), .power_resource);
        self.termList(end, node);
    }

    /// OperationRegion: NameString, RegionSpace(byte), Offset(TermArg), Len(TermArg).
    /// The offset/length expressions are kept as AML for lazy evaluation.
    fn parseRegion(self: *Parser, scope: *Node) Error!void {
        const name_path = try self.readNameString();
        const space = try self.readByte();
        const off_start = self.position;
        try self.object(scope);
        const off_end = self.position;
        try self.object(scope);
        const len_end = self.position;
        const node = try self.namespace.place(scope, name_path.rooted, name_path.parents, name_path.slice(), .region);
        node.region_space = space;
        node.region_offset_aml = self.aml[off_start..off_end];
        node.region_len_aml = self.aml[off_end..len_end];
    }

    fn parseDataRegion(self: *Parser, scope: *Node) Error!void {
        const name_path = try self.readNameString();
        try self.args(scope, 3); // signature, oem id, oem table id (TermArgs)
        _ = try self.namespace.place(scope, name_path.rooted, name_path.parents, name_path.slice(), .region);
    }

    fn parseMutex(self: *Parser, scope: *Node) Error!void {
        const name_path = try self.readNameString();
        try self.skip(1); // sync flags
        _ = try self.namespace.place(scope, name_path.rooted, name_path.parents, name_path.slice(), .mutex);
    }

    fn parseEvent(self: *Parser, scope: *Node) Error!void {
        const name_path = try self.readNameString();
        _ = try self.namespace.place(scope, name_path.rooted, name_path.parents, name_path.slice(), .event);
    }

    /// CreateXField: `count` TermArgs then the new field's NameString.
    fn parseCreateField(self: *Parser, scope: *Node, count: usize) Error!void {
        try self.args(scope, count);
        const name_path = try self.readNameString();
        _ = try self.namespace.place(scope, name_path.rooted, name_path.parents, name_path.slice(), .name);
    }

    /// Field / IndexField / BankField: a region/bank reference, flags, then a
    /// FieldList whose NamedFields become nodes in the current scope. For a plain
    /// Field, the first NameString is the backing region — captured so field units
    /// carry a region + bit position the evaluator can read/write.
    fn parseField(self: *Parser, scope: *Node, name_strings: u8, bank: bool) Error!void {
        const start = self.position;
        const end = start + try self.readPackageLength();
        var region: ?*Node = null;
        var i: u8 = 0;
        while (i < name_strings) : (i += 1) {
            const name_path = try self.readNameString();
            // Only a plain Field's single NameString denotes an OperationRegion.
            if (name_strings == 1) region = self.namespace.resolve(scope, name_path.rooted, name_path.parents, name_path.slice());
        }
        if (bank) try self.object(scope); // bank value TermArg
        const flags = try self.readByte();
        self.fieldList(end, scope, region, flags & 0x0F);
    }

    fn fieldList(self: *Parser, end: usize, scope: *Node, region: ?*Node, initial_access: u8) void {
        var bit_offset: u32 = 0;
        var access = initial_access;
        while (self.position < end) {
            const lead = self.peek() orelse break;
            switch (lead) {
                0x00 => { // ReservedField: advances the bit position
                    self.position += 1;
                    const width = self.readPackageLength() catch break;
                    bit_offset += @intCast(width);
                },
                0x01 => { // AccessField: AccessType (low nibble) + AccessAttrib
                    self.position += 1;
                    const at = self.readByte() catch break;
                    self.skip(1) catch break;
                    access = at & 0x0F;
                },
                0x02 => { // ConnectField: NameString | BufferData
                    self.position += 1;
                    self.object(scope) catch break;
                },
                0x03 => { // ExtendedAccessField: type + attrib + length
                    self.position += 1;
                    self.skip(3) catch break;
                },
                else => { // NamedField: NameSegment + PkgLength (bit width)
                    const segment = self.readNameSegment() catch break;
                    const width = self.readPackageLength() catch break;
                    const unit = self.namespace.newFieldUnit(scope, segment) catch break;
                    unit.region = region;
                    unit.bit_offset = bit_offset;
                    unit.bit_width = @intCast(width);
                    unit.access_type = access;
                    bit_offset += @intCast(width);
                },
            }
        }
        self.position = end;
    }

    // --- control flow -------------------------------------------------------

    fn parseIf(self: *Parser, scope: *Node) Error!void {
        const start = self.position;
        const end = start + try self.readPackageLength();
        try self.object(scope); // predicate
        self.termList(end, scope);
        if (self.peek() == opcode.else_opcode) {
            self.position += 1;
            try self.parseElse(scope);
        }
    }

    fn parseElse(self: *Parser, scope: *Node) Error!void {
        const start = self.position;
        const end = start + try self.readPackageLength();
        self.termList(end, scope);
    }

    fn parseWhile(self: *Parser, scope: *Node) Error!void {
        const start = self.position;
        const end = start + try self.readPackageLength();
        try self.object(scope); // predicate
        self.termList(end, scope);
    }

    fn parseLnot(self: *Parser, scope: *Node) Error!void {
        // 0x92 followed by 0x93/94/95 is a compound comparison (two operands);
        // otherwise it is a plain LNot of one operand.
        const b = self.peek() orelse return error.Truncated;
        switch (b) {
            opcode.lnot.not_equal, opcode.lnot.less_equal, opcode.lnot.greater_equal => {
                self.position += 1;
                try self.args(scope, 2);
            },
            else => try self.object(scope),
        }
    }

    fn parseMatch(self: *Parser, scope: *Node) Error!void {
        try self.object(scope); // search package
        try self.skip(1); // match opcode 1
        try self.object(scope); // operand 1
        try self.skip(1); // match opcode 2
        try self.object(scope); // operand 2
        try self.object(scope); // start index
    }

    // --- extended opcodes (0x5B xx) -----------------------------------------

    fn parseExtended(self: *Parser, scope: *Node) Error!void {
        const e = try self.readByte();
        switch (e) {
            opcode.extended.mutex => try self.parseMutex(scope),
            opcode.extended.event => try self.parseEvent(scope),
            opcode.extended.operation_region => try self.parseRegion(scope),
            opcode.extended.data_region => try self.parseDataRegion(scope),
            opcode.extended.field => try self.parseField(scope, 1, false),
            opcode.extended.index_field => try self.parseField(scope, 2, false),
            opcode.extended.bank_field => try self.parseField(scope, 2, true),
            opcode.extended.device => try self.parseScopeLike(scope, .device),
            opcode.extended.thermal_zone => try self.parseScopeLike(scope, .thermal_zone),
            opcode.extended.processor => try self.parseProcessor(scope),
            opcode.extended.power_resource => try self.parsePowerResource(scope),

            opcode.extended.conditional_reference_of => try self.args(scope, 2), // SuperName, Target
            opcode.extended.create_field => try self.parseCreateField(scope, 3),
            opcode.extended.load_table => try self.args(scope, 6),
            opcode.extended.load => try self.args(scope, 2), // NameString, Target
            opcode.extended.stall, opcode.extended.sleep => try self.args(scope, 1),
            opcode.extended.acquire => {
                try self.object(scope); // mutex SuperName
                try self.skip(2); // timeout WordData
            },
            opcode.extended.signal, opcode.extended.reset, opcode.extended.release, opcode.extended.unload => try self.args(scope, 1),
            opcode.extended.wait => try self.args(scope, 2),
            opcode.extended.from_bcd, opcode.extended.to_bcd => try self.args(scope, 2),
            opcode.extended.fatal => {
                try self.skip(5); // Type(byte) + Code(dword)
                try self.object(scope); // Arg TermArg
            },
            opcode.extended.revision, opcode.extended.debug, opcode.extended.timer => {},

            else => return error.Malformed,
        }
    }
};

fn isNameStart(b: u8) bool {
    return (b >= opcode.name_char_start and b <= opcode.name_char_end) or
        b == opcode.name_char_underscore or
        b == opcode.root_char or
        b == opcode.parent_prefix_char or
        b == opcode.dual_name_prefix or
        b == opcode.multi_name_prefix;
}
