module Std::Array {

    extern func bux_alloc(size: uint) -> *void;
    extern func bux_realloc(ptr: *void, size: uint) -> *void;
    extern func bux_free(ptr: *void);
    extern func bux_bounds_check(index: uint, len: uint);

    /// Growable contiguous buffer of `T` (len + capacity).
    struct Array<T> {
        data: *T,
        len: uint,
        cap: uint,
    }

    /// Create an empty array with the given initial capacity.
    func Array_New<T>(cap: uint) -> Array<T> {
        let data = bux_alloc(cap * sizeof(T)) as *T;
        return Array<T> { data: data, len: 0, cap: cap };
    }

    /// Append `value`, growing capacity if needed.
    func Array_Push<T>(self: *Array<T>, value: T) {
        if self.len >= self.cap {
            self.cap = self.cap * 2;
            self.data = bux_realloc(self.data as *void, self.cap * sizeof(T)) as *T;
        }
        self.data[self.len] = value;
        self.len = self.len + 1;
    }

    /// Element at `index` (bounds-checked unless `@[Release]`).
    func Array_Get<T>(self: *Array<T>, index: uint) -> T {
        bux_bounds_check(index, self.len);
        return self.data[index];
    }

    /// Write `value` at `index` (bounds-checked unless `@[Release]`).
    func Array_Set<T>(self: *Array<T>, index: uint, value: T) {
        bux_bounds_check(index, self.len);
        self.data[index] = value;
    }

    /// Number of live elements.
    func Array_Len<T>(self: *Array<T>) -> uint {
        return self.len;
    }

    /// Free the backing buffer and reset length/capacity to zero.
    func Array_Free<T>(self: *Array<T>) {
        bux_free(self.data as *void);
        self.data = null as *T;
        self.len = 0;
        self.cap = 0;
    }

    /// Drop trait entry — same as `Array_Free`.
    func Array_Drop<T>(self: *Array<T>) {
        Array_Free<T>(self);
    }

    func Array_operator_index_get<T>(self: *Array<T>, idx: uint) -> T {
        return Array_Get<T>(self, idx);
    }

    func Array_operator_index_set<T>(self: *Array<T>, idx: uint, value: T) {
        Array_Set<T>(self, idx, value);
    }

    /// True if the array has no elements.
    func Array_IsEmpty<T>(self: *Array<T>) -> bool {
        return self.len == 0;
    }

    /// Current capacity (not length).
    func Array_Cap<T>(self: *Array<T>) -> uint {
        return self.cap;
    }

    /// Drop length to zero; keeps allocated capacity.
    func Array_Clear<T>(self: *Array<T>) {
        self.len = 0;
    }

    /// Ensure capacity is at least `minCap` (does not shrink).
    func Array_Reserve<T>(self: *Array<T>, minCap: uint) {
        if minCap <= self.cap {
            return;
        }
        self.cap = minCap;
        self.data = bux_realloc(self.data as *void, self.cap * sizeof(T)) as *T;
    }

    /// First element (bounds-checked if empty).
    func Array_First<T>(self: *Array<T>) -> T {
        return Array_Get<T>(self, 0);
    }

    /// Last element (bounds-checked if empty).
    func Array_Last<T>(self: *Array<T>) -> T {
        return Array_Get<T>(self, self.len - 1);
    }

    /// Remove and return the last element (bounds-checked if empty).
    func Array_Pop<T>(self: *Array<T>) -> T {
        bux_bounds_check(0, self.len);
        self.len = self.len - 1;
        return self.data[self.len];
    }

    /// Linear search: true if `value` is present (uses `==`).
    func Array_Contains<T>(self: *Array<T>, value: T) -> bool {
        var i: uint = 0;
        while i < self.len {
            if self.data[i] == value {
                return true;
            }
            i = i + 1;
        }
        return false;
    }

    /// Index of first equal element, or `-1` if not found.
    func Array_IndexOf<T>(self: *Array<T>, value: T) -> int {
        var i: uint = 0;
        while i < self.len {
            if self.data[i] == value {
                return i as int;
            }
            i = i + 1;
        }
        return -1;
    }

    /// Append all elements of `other` onto `self`.
    func Array_Extend<T>(self: *Array<T>, other: *Array<T>) {
        var i: uint = 0;
        while i < other.len {
            Array_Push<T>(self, other.data[i]);
            i = i + 1;
        }
    }

}
