const std         = @import("std");
const zm          = @import("zmath");
const misc        = @import("misc.zig");

pub const id = u64;
pub const INVALID_ID: id = 0;

pub const EntityKinds = .{
    Soldier,
    Alien,
};

const Entity = struct {
    
};

pub const Soldier = struct {
    id: id = INVALID_ID,
    hp: i32 = 0,
    pos: zm.Vec = .{0, 0, 0, 0},
    vel: zm.Vec = .{0, 0, 0, 0},

    pub fn update(self: *@This(), dt: f32) void {
        self.pos = self.pos + self.vel * zm.splat(zm.Vec, dt);
    }
};

pub const Alien = struct {
    id: id = INVALID_ID,
    hp: i32 = 0,
    pos: zm.Vec = .{0, 0, 0, 0},
    vel: zm.Vec = .{0, 0, 0, 0},

    pub fn update(self: *@This(), dt: f32) void {
        self.pos = self.pos + self.vel * zm.splat(zm.Vec, dt);
    }
};

fn _Ref() type {
    const FieldCount = EntityKinds.len;

    var tag_fields: [FieldCount]std.builtin.Type.EnumField = undefined;

    inline for (EntityKinds, 0..) |T, i| {
        tag_fields[i] = .{
            .name = misc.short_type_name(T),
            .value = i,
        };
    }

    const Tag = @Type(.{
        .@"enum" = .{
            .tag_type = u8,
            .fields = &tag_fields,
            .decls = &.{},
            .is_exhaustive = true,
        },
    });

    var fields: [FieldCount]std.builtin.Type.UnionField = undefined;

    inline for (EntityKinds, 0..) |T, i| {
        fields[i] = .{
            .name = misc.short_type_name(T),
            .type = *T,
            .alignment = @alignOf(*T),
        };
    }

    return @Type(.{
        .@"union" = .{
            .layout = .auto,
            .fields = &fields,
            .decls = &.{},
            .tag_type = Tag,
        },
    });
}

pub const Ref = _Ref();

pub fn setPos(self: Ref, pos: zm.Vec) void {
    switch (self) {
        .Soldier => |s| { s.pos = pos; },
        .Alien => |a| { a.pos = pos; },
        else => unreachable
    }
}

pub fn makeRef(comptime T: type, ptr: *T) Ref {
    inline for (EntityKinds) |U| {
        if (T == U) {
            return @unionInit(
                Ref,
                misc.short_type_name(U),
                ptr,
            );
        }
    }

    @compileError("Type not in EntityKinds: " ++ @typeName(T));
}

