// zig fmt: off
const std = @import("std");
const EnumStringUtils = @import("../enum_string_utils.zig").EnumStringUtils;
const Store = @import("../store.zig");
const json_utils = @import("../json_utils.zig");
const Craft = @import("./craft.zig");

const Item = @This();

pub const Type = enum {
    utility,
    consumable,
    body_armor,
    weapon,
    resource,
    leg_armor,
    helmet,
    boots,
    shield,
    amulet,
    ring,
    artifact,
    currency,

    const Utils = EnumStringUtils(Type, .{
        .{ "utility"   ,  .consumable },
        .{ "consumable",  .consumable },
        .{ "body_armor",  .body_armor },
        .{ "weapon"    ,  .weapon     },
        .{ "resource"  ,  .resource   },
        .{ "leg_armor" ,  .leg_armor  },
        .{ "helmet"    ,  .helmet     },
        .{ "boots"     ,  .boots      },
        .{ "shield"    ,  .shield     },
        .{ "amulet"    ,  .amulet     },
        .{ "ring"      ,  .ring       },
        .{ "artifact"  ,  .artifact   },
        .{ "currency"  ,  .currency   },
    });

    pub const toString = Utils.toString;
    pub const fromString = Utils.fromString;
};

pub const max_code_size = 32;
pub const Name = std.BoundedArray(u8, 32);
pub const Code = std.BoundedArray(u8, max_code_size);
pub const Subtype = std.BoundedArray(u8, 32);
pub const Description = std.BoundedArray(u8, 128);

name: Name,
code: Code,
level: u64,
type: Type,
subtype: Subtype,
description: Description,
craft: ?Craft,

pub fn parse(store: *Store, obj: std.json.ObjectMap) !Item {
    const level = json_utils.getInteger(obj, "level") orelse return error.MissingProperty;
    if (level < 1) return error.InvalidLevel;

    const code        = try json_utils.getStringRequired(obj, "code");
    const name        = try json_utils.getStringRequired(obj, "name");
    const subtype     = try json_utils.getStringRequired(obj, "subtype");
    const description = try json_utils.getStringRequired(obj, "description");
    const item_type   = try json_utils.getStringRequired(obj, "type");
    const craft       = json_utils.getObject(obj, "craft");

    return Item{
        .name = try Name.fromSlice(name),
        .code = try Code.fromSlice(code),
        .level = @intCast(level),
        .type = Type.fromString(item_type) orelse return error.InvalidType,
        .subtype = try Subtype.fromSlice(subtype),
        .description = try Description.fromSlice(description),
        .craft = if (craft != null) try Craft.parse(store, craft.?) else null
    };
}

pub fn parseAndAppend(store: *Store, obj: std.json.ObjectMap) !Store.Id {
    return try store.items.appendOrUpdate(try Item.parse(store, obj));
}

pub fn format(
    self: Item,
    comptime fmt: []const u8,
    options: std.fmt.FormatOptions,
    writer: anytype,
) !void {
    _ = fmt;
    _ = options;

    try writer.print("{s}{{ ", .{ @typeName(Item) });

    try writer.print(".name = \"{s}\", ", .{ self.name.slice() });
    try writer.print(".code = \"{s}\", ", .{ self.code.slice() });
    try writer.print(".level = {}, ", .{ self.level });
    try writer.print(".type = {}, ", .{ self.type });
    try writer.print(".subtype = \"{s}\", ", .{ self.subtype.slice() });
    try writer.print(".description = \"{s}\", ", .{ self.description.slice() });
    if (self.craft) |craft| {
        try writer.print(".craft = {}, ", .{ craft });
    }

    try writer.writeAll("}");
}
