// zig fmt: off
const std = @import("std");
const Store = @import("../store.zig");
const json_utils = @import("../json_utils.zig");
const Character = @import("./character.zig");
const DropRate = @import("./drop_rate.zig");

const Element = Character.Element;

const Monster = @This();

pub const Name = std.BoundedArray(u8, 16);

pub const max_code_size = 16;
pub const Code = std.BoundedArray(u8, max_code_size);

pub const ElementalStats = struct {
    attack: i64,
    resistance: i64,

    pub fn parse(object: std.json.ObjectMap, attack: []const u8, resistance: []const u8) !ElementalStats {
        return ElementalStats{
            .attack = try json_utils.getIntegerRequired(object, attack),
            .resistance = try json_utils.getIntegerRequired(object, resistance),
        };
    }
};

pub const ElementalStatsArray = std.EnumArray(Element, ElementalStats);

pub const Drops = std.BoundedArray(DropRate, 16);

name: Name,
code: Code,
level: u64,
hp: u64,
elemental_stats: ElementalStatsArray,
min_gold: u64,
max_gold: u64,
drops: Drops,

pub fn parse(store: *Store, obj: std.json.ObjectMap) !Monster {
    const name = try json_utils.getStringRequired(obj, "name");
    const code = try json_utils.getStringRequired(obj, "code");
    const level = try json_utils.getPositiveIntegerRequired(obj, "level");
    const hp = try json_utils.getPositiveIntegerRequired(obj, "hp");
    const min_gold = try json_utils.getPositiveIntegerRequired(obj, "min_gold");
    const max_gold = try json_utils.getPositiveIntegerRequired(obj, "max_gold");

    const elemental_stats = ElementalStatsArray.init(.{
        .water = try ElementalStats.parse(obj, "attack_water", "res_water"),
        .fire  = try ElementalStats.parse(obj, "attack_fire", "res_fire"),
        .earth = try ElementalStats.parse(obj, "attack_earth", "res_earth"),
        .air   = try ElementalStats.parse(obj, "attack_air", "res_air"),
    });

    const drops_array = try json_utils.getArrayRequired(obj, "drops");
    var drops = Drops.init(0) catch unreachable;
    drops.len = @intCast(try DropRate.parseDrops(store, drops_array, &drops.buffer));

    return Monster{
        .name = try Name.fromSlice(name),
        .code = try Code.fromSlice(code),
        .level = level,
        .hp = hp,
        .elemental_stats = elemental_stats,
        .min_gold = min_gold,
        .max_gold = max_gold,
        .drops = drops
    };
}

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