const std = @import("../../../std.zig");

const magic = 0xeb9f;
const version = 1;

pub const ext = @import("btf_ext.zig");

/// All offsets are in bytes relative to the end of this header
pub const Header = packed struct {
    magic: u16,
    version: u8,
    flags: u8,
    hdr_len: u32,

    /// offset of type section
    type_off: u32,

    /// length of type section
    type_len: u32,

    /// offset of string section
    str_off: u32,

    /// length of string section
    str_len: u32,
};

/// Max number of type identifiers
pub const max_type = 0xfffff;

/// Max offset into string section
pub const max_name_offset = 0xffffff;

/// Max number of struct/union/enum member of func args
pub const max_vlen = 0xffff;

pub const Type = packed struct {
    name_off: u32,
    info: packed struct {
        /// number of struct's members
        vlen: u16,

        unused_1: u8,
        kind: Kind,
        unused_2: u3,

        /// used by Struct, Union, and Fwd
        kind_flag: bool,
    },

    /// size is used by Int, Enum, Struct, Union, and DataSec, it tells the size
    /// of the type it is describing
    ///
    /// type is used by Ptr, Typedef, Volatile, Const, Restrict, Func,
    /// FuncProto, and Var. It is a type_id referring to another type
    size_type: union { size: u32, typ: u32 },
};

/// For some kinds, Type is immediately followed by extra data
pub const Kind = enum(u4) {
    unknown,
    int,
    ptr,
    array,
    structure,
    kind_union,
    enumeration,
    fwd,
    typedef,
    kind_volatile,
    constant,
    restrict,
    func,
    funcProto,
    variable,
    dataSec,
};

/// Int kind is followed by this struct
pub const IntInfo = packed struct {
    bits: u8,
    unused: u8,
    offset: u8,
    encoding: enum(u4) {
        signed = 1 << 0,
        char = 1 << 1,
        boolean = 1 << 2,
    },
};

test "IntInfo is 32 bits" {
    try std.testing.expectEqual(@bitSizeOf(IntInfo), 32);
}

/// Enum kind is followed by this struct
pub const Enum = packed struct {
    name_off: u32,
    val: i32,
};

/// Array kind is followd by this struct
pub const Array = packed struct {
    typ: u32,
    index_type: u32,
    nelems: u32,
};

/// Struct and Union kinds are followed by multiple Member structs. The exact
/// number is stored in vlen
pub const Member = packed struct {
    name_off: u32,
    typ: u32,

    /// if the kind_flag is set, offset contains both member bitfield size and
    /// bit offset, the bitfield size is set for bitfield members. If the type
    /// info kind_flag is not set, the offset contains only bit offset
    offset: packed struct {
        bit: u24,
        bitfield_size: u8,
    },
};

/// FuncProto is followed by multiple Params, the exact number is stored in vlen
pub const Param = packed struct {
    name_off: u32,
    typ: u32,
};

pub const VarLinkage = enum {
    static,
    global_allocated,
    global_extern,
};

pub const FuncLinkage = enum {
    static,
    global,
    external,
};

/// Var kind is followd by a single Var struct to describe additional
/// information related to the variable such as its linkage
pub const Var = packed struct {
    linkage: u32,
};

/// Datasec kind is followed by multible VarSecInfo to describe all Var kind
/// types it contains along with it's in-section offset as well as size.
pub const VarSecInfo = packed struct {
    typ: u32,
    offset: u32,
    size: u32,
};