Vector :: ($N: int, $T: Type) -> Type #builtin;
out :: (str: string) -> void #builtin;
// sqrt :: (x: $T) -> T #builtin;
// sin :: (x: $T) -> T #builtin;
// cos :: (x: $T) -> T #builtin;
size_of :: ($T: Type) -> s64 #builtin;
align_of :: ($T: Type) -> s64 #builtin;
// Low-level libc bindings, used by allocator implementations to avoid
// recursing through `context.allocator`.
libc_malloc :: (size: s64) -> *void #foreign libc "malloc";
libc_free   :: (ptr: *void) -> void #foreign libc "free";

malloc :: (size: s64) -> *void #foreign libc "malloc";
memcpy :: (dst: *void, src: *void, size: s64) -> *void #foreign libc "memcpy";
memset :: (dst: *void, val: s64, size: s64) -> void #foreign libc "memset";
free :: (ptr: *void) -> void #foreign libc "free";
type_of :: (val: $T) -> Type #builtin;
type_name :: ($T: Type) -> string #builtin;
field_count :: ($T: Type) -> s64 #builtin;
field_name :: ($T: Type, idx: s64) -> string #builtin;
field_value :: (s: $T, idx: s64) -> Any #builtin;
is_flags :: ($T: Type) -> bool #builtin;
field_value_int :: ($T: Type, idx: s64) -> s64 #builtin;
field_index :: ($T: Type, val: T) -> s64 #builtin;
string :: []u8 #builtin;

#import "allocators.sx";

// --- Context ---

Context :: struct {
    allocator: Allocator;
    data: *void;
}

// --- Slice & string allocation ---

cstring :: (size: s64) -> string {
    raw := context.allocator.alloc(size + 1);
    memset(raw, 0, size + 1);
    s : string = ---;
    s.ptr = xx raw;
    s.len = size;
    s;
}

alloc_slice :: ($T: Type, count: s64) -> []T {
    raw := context.allocator.alloc(count * size_of(T));
    memset(raw, 0, count * size_of(T));
    s : []T = ---;
    s.ptr = xx raw;
    s.len = count;
    s;
}

int_to_string :: (n: s64) -> string {
    if n == 0 { return "0"; }
    neg := n < 0;
    v := if neg then 0 - n else n;
    // Single pass: fill digits backwards into temp string, then substr
    tmp := cstring(20);
    i := 19;
    while v > 0 {
        tmp[i] = (v % 10) + 48;
        v = v / 10;
        i -= 1;
    }
    if neg { tmp[i] = 45; i -= 1; }
    substr(tmp, i + 1, 20 - i - 1);
}

bool_to_string :: (b: bool) -> string {
    if b then "true" else "false";
}

float_to_string :: (f: f64) -> string {
    neg := f < 0.0;
    v := if neg then 0.0 - f else f;
    int_part := cast(s64) v;
    frac := cast(s64) ((v - cast(f64) int_part) * 1000000.0);
    if frac < 0 { frac = 0 - frac; }
    istr := int_to_string(int_part);
    fstr := int_to_string(frac);
    il := istr.len;
    fl := fstr.len;
    prefix := if neg then 1 else 0;
    total := prefix + il + 1 + 6;
    buf := cstring(total);
    pos := 0;
    if neg { buf[0] = 45; pos = 1; }
    memcpy(@buf[pos], istr.ptr, il);
    pos = pos + il;
    buf[pos] = 46;
    pos += 1;
    pad := 6 - fl;
    memset(@buf[pos], 48, pad);
    pos = pos + pad;
    memcpy(@buf[pos], fstr.ptr, fl);
    buf;
}

hex_group :: (buf: string, offset: s64, val: s64) {
    i := offset + 3;
    v := val;
    while i >= offset {
        d := v % 16;
        buf[i] = if d < 10 then d + 48 else d - 10 + 97;
        v = v / 16;
        i -= 1;
    }
}

int_to_hex_string :: (n: s64) -> string {
    if n == 0 { return "0"; }

    // Split into four 16-bit groups for correct unsigned treatment
    g0 := n % 65536;
    if g0 < 0 { g0 = g0 + 65536; }
    r1 := (n - g0) / 65536;
    g1 := r1 % 65536;
    if g1 < 0 { g1 = g1 + 65536; }
    r2 := (r1 - g1) / 65536;
    g2 := r2 % 65536;
    if g2 < 0 { g2 = g2 + 65536; }
    r3 := (r2 - g2) / 65536;
    g3 := r3 % 65536;
    if g3 < 0 { g3 = g3 + 65536; }

    buf := cstring(16);
    hex_group(buf, 0, g3);
    hex_group(buf, 4, g2);
    hex_group(buf, 8, g1);
    hex_group(buf, 12, g0);

    // Skip leading zeros (keep at least 1 digit)
    start := 0;
    while start < 15 {
        if buf[start] != 48 { break; }
        start += 1;
    }
    substr(buf, start, 16 - start);
}

concat :: (a: string, b: string) -> string {
    al := a.len;
    bl := b.len;
    buf := cstring(al + bl);
    memcpy(buf.ptr, a.ptr, al);
    memcpy(@buf[al], b.ptr, bl);
    buf;
}

substr :: (s: string, start: s64, len: s64) -> string {
    buf := cstring(len);
    memcpy(buf.ptr, @s[start], len);
    buf;
}

// Replace XML special characters with their entity references. Used
// when emitting Info.plist / AndroidManifest content from sx values
// that may contain user-supplied text (bundle id, app name, etc).
xml_escape :: (s: string) -> string {
    result := "";
    i := 0;
    seg_start := 0;
    while i < s.len {
        c := s[i];
        // 38='&', 60='<', 62='>', 34='"', 39='\''
        ent := "";
        if c == 38 { ent = "&amp;"; }
        if c == 60 { ent = "&lt;"; }
        if c == 62 { ent = "&gt;"; }
        if c == 34 { ent = "&quot;"; }
        if c == 39 { ent = "&apos;"; }
        if ent.len > 0 {
            if i > seg_start {
                result = concat(result, substr(s, seg_start, i - seg_start));
            }
            result = concat(result, ent);
            seg_start = i + 1;
        }
        i += 1;
    }
    if seg_start < s.len {
        result = concat(result, substr(s, seg_start, s.len - seg_start));
    }
    result;
}

// Join path components with the POSIX separator ('/'). Skips empty
// components and collapses duplicate separators at component
// boundaries. Used for bundle paths where Apple .app and Android APK
// both expect POSIX-style paths.
path_join :: (parts: ..string) -> string {
    result := "";
    i := 0;
    while i < parts.len {
        p := parts[i];
        if p.len > 0 {
            if result.len > 0 {
                tail := result[result.len - 1];
                head := p[0];
                if tail == 47 {
                    if head == 47 {
                        p = substr(p, 1, p.len - 1);
                    }
                } else {
                    if head != 47 {
                        result = concat(result, "/");
                    }
                }
            }
            result = concat(result, p);
        }
        i += 1;
    }
    result;
}

struct_to_string :: (s: $T) -> string {
    result := concat(type_name(T), "{");
    i := 0;
    while i < field_count(T) {
        if i > 0 { result = concat(result, ", "); }
        result = concat(result, field_name(T, i));
        result = concat(result, ": ");
        result = concat(result, any_to_string(field_value(s, i)));
        i += 1;
    }
    concat(result, "}");
}

vector_to_string :: (v: $T) -> string {
    result := "[";
    i := 0;
    while i < field_count(T) {
        if i > 0 { result = concat(result, ", "); }
        result = concat(result, any_to_string(field_value(v, i)));
        i += 1;
    }
    concat(result, "]");
}

array_to_string :: (a: $T) -> string {
    result := "[";
    i := 0;
    while i < field_count(T) {
        if i > 0 { result = concat(result, ", "); }
        result = concat(result, any_to_string(field_value(a, i)));
        i += 1;
    }
    concat(result, "]");
}

slice_to_string :: (items: []$T) -> string {
    result := "[";
    i := 0;
    while i < items.len {
        if i > 0 { result = concat(result, ", "); }
        result = concat(result, any_to_string(field_value(items, i)));
        i += 1;
    }
    concat(result, "]");
}

pointer_to_string :: (p: $T) -> string {
    addr : s64 = xx p;
    if addr == 0 { "null"; } else {
        concat(type_name(T), concat("@0x", int_to_hex_string(addr)));
    }
}

flags_to_string :: (val: $T) -> string {
    v := cast(s64) val;
    result := "";
    i := 0;
    while i < field_count(T) {
        fv := field_value_int(T, i);
        if v & fv {
            if result.len > 0 { result = concat(result, " | "); }
            result = concat(result, concat(".", field_name(T, i)));
        }
        i += 1;
    }
    if result.len == 0 { result = "0"; }
    result;
}

enum_to_string :: (u: $T) -> string {
    if is_flags(T) { return flags_to_string(u); }
    idx := field_index(T, u);
    result := concat(".", field_name(T, idx));
    payload := field_value(u, idx);
    pstr := any_to_string(payload);
    if pstr.len > 0 {
        result = concat(result, concat("(", concat(pstr, ")")));
    }
    result;
}

any_to_string :: (val: Any) -> string {
    result := "<?>";
    type := type_of(val);
    if type == {
        case void: result = "";
        case int: result = int_to_string(xx val);
        case string: { s : string = xx val; result = s; }
        case bool: result = bool_to_string(xx val);
        case float: result = float_to_string(xx val);
        case struct: result = struct_to_string(cast(type) val);
        case enum: result = enum_to_string(cast(type) val);
        case vector: result = vector_to_string(cast(type) val);
        case array: result = array_to_string(cast(type) val);
        case slice: result = slice_to_string(cast(type) val);
        case pointer: result = pointer_to_string(cast(type) val);
        case type: { s : string = xx val; result = s; }
    }
    result;
}

build_format :: (fmt: string) -> string {
    code := "result := \"\"; ";
    seg_start := 0;
    i := 0;
    arg_idx := 0;
    while i < fmt.len {
        if fmt[i] == 123 {
            if i + 1 < fmt.len {
                if fmt[i + 1] == 125 {
                    if i > seg_start {
                        code = concat(code, "result = concat(result, substr(fmt, ");
                        code = concat(code, int_to_string(seg_start));
                        code = concat(code, ", ");
                        code = concat(code, int_to_string(i - seg_start));
                        code = concat(code, ")); ");
                    }
                    code = concat(code, "result = concat(result, any_to_string(args[");
                    code = concat(code, int_to_string(arg_idx));
                    code = concat(code, "])); ");
                    arg_idx += 1;
                    i += 2;
                    seg_start = i;
                } else if fmt[i + 1] == 123 {
                    code = concat(code, "result = concat(result, substr(fmt, ");
                    code = concat(code, int_to_string(seg_start));
                    code = concat(code, ", ");
                    code = concat(code, int_to_string(i - seg_start + 1));
                    code = concat(code, ")); ");
                    i += 2;
                    seg_start = i;
                } else {
                    i += 1;
                }
            } else {
                i += 1;
            }
        } else if fmt[i] == 125 {
            if i + 1 < fmt.len {
                if fmt[i + 1] == 125 {
                    code = concat(code, "result = concat(result, substr(fmt, ");
                    code = concat(code, int_to_string(seg_start));
                    code = concat(code, ", ");
                    code = concat(code, int_to_string(i - seg_start + 1));
                    code = concat(code, ")); ");
                    i += 2;
                    seg_start = i;
                } else {
                    i += 1;
                }
            } else {
                i += 1;
            }
        } else {
            i += 1;
        }
    }
    if seg_start < fmt.len {
        code = concat(code, "result = concat(result, substr(fmt, ");
        code = concat(code, int_to_string(seg_start));
        code = concat(code, ", ");
        code = concat(code, int_to_string(fmt.len - seg_start));
        code = concat(code, ")); ");
    }
    code;
}

format :: ($fmt: string, args: ..Any) -> string {
    #insert build_format(fmt);
    #insert "result;";
}

print :: ($fmt: string, args: ..Any) {
    #insert build_format(fmt);
    #insert "out(result);";
}

// User-space `xx` extension. `xx val : T` where the built-in conversion
// ladder makes no progress falls through to an `impl Into(T) for Source`
// lookup; the compiler monomorphises `convert` for the (Source, T) pair
// and emits a direct call. Compile-time only — no vtable, no runtime
// dispatch.
Into :: protocol(Target: Type) {
    convert :: () -> Target;
}

List :: struct ($T: Type) {
    items: [*]T = null;
    len: s64 = 0;
    cap: s64 = 0;

    append :: (list: *List(T), item: T, alloc: Allocator = context.allocator) {
        if list.len >= list.cap {
            new_cap := if list.cap == 0 then 4 else list.cap * 2;
            new_items : [*]T = xx alloc.alloc(new_cap * size_of(T));
            if list.len > 0 {
                memcpy(new_items, list.items, list.len * size_of(T));
                alloc.dealloc(list.items);
            }
            list.items = new_items;
            list.cap = new_cap;
        }
        list.items[list.len] = item;
        list.len += 1;
    }

    ensure_capacity :: (list: *List(T), n: s64, alloc: Allocator = context.allocator) {
        if list.cap >= n { return; }
        new_cap := if list.cap == 0 then 4 else list.cap;
        while new_cap < n { new_cap = new_cap * 2; }
        new_items : [*]T = xx alloc.alloc(new_cap * size_of(T));
        if list.len > 0 {
            memcpy(new_items, list.items, list.len * size_of(T));
            alloc.dealloc(list.items);
        }
        list.items = new_items;
        list.cap = new_cap;
    }

    deinit :: (list: *List(T), alloc: Allocator = context.allocator) {
        if list.items != null {
            alloc.dealloc(list.items);
        }
        list.items = null;
        list.len = 0;
        list.cap = 0;
    }
}