Jane = {
    config = {
        wee_sizemod = 1.25,
        texture_pack = "default",
        bans = {"j_cry_crustulum", "c_cry_hammerspace"}
    },
}

Jane.misprintize = (Cryptid or {}).misprintize
Jane.misprintize_tbl = (Cryptid or {}).misprintize_tbl
Jane.misprinitze_val = (Cryptid or {}).misprintize_val

if not Jane.misprintize then
    assert(SMODS.load_file("src/misprintize.lua"))()
end

function Jane.can_use()
    return not (((G.play and #G.play.cards > 0) or
        (G.CONTROLLER.locked) or
        (G.GAME.STOP_USE and G.GAME.STOP_USE > 0)) and
        G.STATE ~= G.STATES.HAND_PLAYED and
        G.STATE ~= G.STATES.DRAW_TO_HAND and
        G.STATE ~= G.STATES.PLAY_TAROT)
end

function Jane.card_status_text(
    card,
    text,
    xoffset,
    yoffset,
    colour,
    size,
    delay,
    juice,
    jiggle,
    align,
    sound,
    volume,
    pitch,
    trig,
    f
)
    if (delay or 0) <= 0 then
        if type(f) == "function" then
            f(card)
        end

        attention_text({
            text = text,
            scale = size or 1,
            hold = 0.7,
            backdrop_colour = colour or (G.C.FILTER),
            align = align or "bm",
            major = card,
            offset = {x = xoffset or 0, y = yoffset or (-0.05*G.CARD_H)}
        })

        if sound then
            play_sound(sound, pitch or (0.9 + (0.2 * math.random())), volume or 1)
        end

        if juice then
            if type(juice) == "table" then
                card:juice_up(juice[1], juice[2])
            elseif type(juice) == "number" and juice ~= 0 then
                card:juice_up(juice, juice / 6)
            end
        end

        if jiggle then
            G.ROOM.jiggle = G.ROOM.jiggle + jiggle
        end
    else
        G.E_MANAGER:add_event(Event({
            trigger = trig,
            delay = delay,
            func = function()
                if f and type(f) == "function" then
                    f(card)
                end

                attention_text({
                    text = text,
                    scale = size or 1,
                    hold = 0.7 + (delay or 0),
                    backdrop_colour = colour or (G.C.FILTER),
                    align = align or "bm",
                    major = card,
                    offset = {x = xoffset or 0, y = yoffset or (-0.05*G.CARD_H)}
                })

                if sound then
                    play_sound(sound, pitch or (0.9 + (0.2*math.random())), volume or 1)
                end

                if juice then
                    if type(juice) == "table" then
                        card:juice_up(juice[1], juice[2])
                    elseif type(juice) == "number" and juice ~= 0 then
                        card:juice_up(juice, juice / 6)
                    end
                end

                if jiggle then
                    G.ROOM.jiggle = G.ROOM.jiggle + jiggle
                end

                return true
            end
        }))
    end
end

function Jane.empowered()
    if Cryptid then
        add_tag(Tag("tag_cry_empowered"))
        return true
    end

    local card = create_card("Spectral", G.consumeables, nil, nil, nil, nil, "c_soul", "acceleration_soul")
    card:set_edition({negative = true})
    card:add_to_deck()
    G.consumeables:emplace(card)
end

function Jane.get_chipmult_sum(chips, mult)
    chips = chips or 0
    mult = mult or 0

    local break_infinity = ((Talisman or {}).config_file or {}).break_infinity
    local big = (break_infinity == "" or break_infinity == nil) and function (x) return x end or to_big
    local op = Jane.get_operator()

    if op >= 3 then
        return big(chips) ^ big(mult)
    elseif op == 2 then
        return big(chips) * big(mult)
    end

    return big(chips) + big(mult)
end

function Jane.get_operator()
    if not G.GAME then
        return 0
    end

    G.GAME.operator = G.GAME.operator or 2
    return math.max(math.min(G.GAME.operator, 3), 1)
end

function Jane.get_small_chipmult_sum(chips, mult)
    local ret = Jane.get_chipmult_sum()
    return type(ret) == "table" and ret:to_number() or ret
end

function Jane.hidden(card)
    return G.GAME and not
        G.GAME.obsidian and
        type(card) == 'table' and
        (card.name == "Black Hole" or card.name == "The Soul" or card.hidden)
end

function Jane.play_sound(sound, per, vol)
    G.E_MANAGER:add_event(Event({
        func = function()
            play_sound(sound,per,vol)
            return true
        end
    }))
end

function Jane.q(fc, de, t, tr, bl, ba)
    G.E_MANAGER:add_event(Event({
        timer = t,
        trigger = tr,
        delay = de,
        blockable = bl,
        blocking = ba,
        func = fc
    }))
end

function Card:gc()
    return (self.config or {}).center or {}
end

function Card:norank()
    return self.ability.name == "Stone Card" or self.config.center.no_rank
end

function Card:nosuit()
    return self.ability.name == "Stone Card" or self.config.center.no_suit
end

local orig_debuff = Card.set_debuff

function Card:set_debuff(should_debuff)
    if should_debuff and ((self.config or {}).center or {}).debuff_immune then
        Jane.card_status_text(self, "Immune", nil, 0.05 * self.T.h, G.C.RED, nil, 0.6, nil, nil, "bm", "cancel", 1, 0.9)
        return false
    else
        orig_debuff(self, should_debuff)
    end
end

local orig_menu = Game.main_menu

function Game:main_menu(change_context)
    Jane.sinister = nil
    orig_menu(self, change_context)
end

local orig_update = Game.update

function Game:update(dt)
    local function delete_hardbans()
        if not Jane.config.disable_bans then
            for _, v in pairs(Jane.config.bans) do
                local e = SMODS.Center:get_obj(v)

                if e then
                    e:delete()
                end
            end
        end
    end

    local function hand(name, chip, mul, lv, notif, snd, vol, pit, de)
        local config = {
            delay = de or 0.3,
            pitch = pit or 0.8,
            volume = vol or 0.7,
            sound = type(snd) == "string" and snd or type(snd) == "nil" and "button",
        }

        local vals = {
            level = lv or "?",
            mult = mul or "?",
            StatusText = notif,
            chips = chip or "?",
            handname = name or "????",
        }

        update_hand_text(config, vals)
    end

    local function rebalance_orrery()
        local function small(x)
            return type(x) == "table" and x:to_number() or x
        end

        local orrery = (G.GAME or {}).orrery

        if not orrery then
            return
        end

        local count = 0
        local hands = {}
        local inequalities = 0
        local pools = {chips = 0, level = 0, mult = 0}

        for k, v in pairs(G.GAME.hands) do
            if orrery[k] and
                (orrery[k].mult ~= v.mult or
                orrery[k].chips ~= v.chips or
                orrery[k].level ~= v.level) then
                inequalities = inequalities + 1
            end

            count = count + 1
            hands[#hands + 1] = v
            pools.mult = v.mult + pools.mult
            pools.chips = v.chips + pools.chips
            pools.level = v.level + pools.level
        end

        if inequalities == 0 then
            return
        end

        pools.chips = pools.chips + inequalities * 150
        table.sort(hands, function (x, y) return x.order > y.order end)

        for i, v in ipairs(hands) do
            v.mult = math.floor(pools.mult / count) + (small(pools.mult % count) >= i and 1 or 0)
            v.chips = math.floor(pools.chips / count) + (small(pools.chips % count) >= i and 1 or 0)
            v.level = math.floor(pools.level / count) + (small(pools.level % count) >= i and 1 or 0)
        end

        for k, v in pairs(G.GAME.hands) do
            orrery[k] = {chips = v.chips, level = v.level, mult = v.mult}
        end

        hand(localize("k_all_hands"), math.floor(pools.chips / count), math.floor(pools.mult / count), math.floor(pools.level / count))
        delay(1)
        update_hand_text({sound = "button", volume = 0.7, pitch = 1.1, delay = 0}, {mult = 0, chips = 0, handname = "", level = ""})
    end

    local function hsv(h, s, v)
        if s <= 0 then return v, v, v end
        h = h * 6
        local c = v * s
        local x = (1 - math.abs((h % 2) - 1)) * c
        local m, r, g, b = (v - c), 0, 0, 0

        if h < 1 then
            r, g, b = c, x, 0
        elseif h < 2 then
            r, g, b = x, c, 0
        elseif h < 3 then
            r, g, b = 0, c, x
        elseif h < 4 then
            r, g, b = 0, x, c
        elseif h < 5 then
            r, g, b = x, 0, c
        else
            r, g, b = c, 0, x
        end

        return r + m, g + m, b + m
    end

    orig_update(self, dt)

    local ante = G.GAME.round_resets.ante
    local blind = get_blind_amount((ante >= 1 and ante <= 8) and math.floor(ante) or ante)
    G.P_BLINDS["bl_jane_wee"].mult = 22 / blind
    G.P_BLINDS["bl_jane_descending"].mult = math.floor(8 * math.sqrt(blind)) / blind

    if not Jane.bans_done then
        delete_hardbans()
        Jane.bans_done = true
    end

    if (G.GAME or {}).banned_keys then
        for _, v in pairs(Jane.config.bans) do
            G.GAME.banned_keys[v] = true
        end
    end

    if G.ARGS.LOC_COLOURS then
        local r, g, b = hsv(self.C.jane_RGB_HUE / 360, .5, 1)
        self.C.jane_RGB[1] = r
        self.C.jane_RGB[3] = g
        self.C.jane_RGB[2] = b
        self.C.jane_RGB_HUE = (self.C.jane_RGB_HUE + 0.5) % 360
        G.ARGS.LOC_COLOURS.jane_RGB = self.C.jane_RGB
    end

    rebalance_orrery()
end

local orig_card = SMODS.find_card

---@param key string
---@param count_debuffed true?
---@return Card[]|table[]
--- Returns all cards matching provided `key`.
function SMODS.find_card(key, count_debuffed)
    return orig_card(key == "j_jen_saint" and "j_jane_saint" or key, count_debuffed)
end

SMODS.Atlas {
    px = 34,
    py = 34,
    key = "modicon",
    path = "almanac_avatar.png",
}

SMODS.Atlas {
    px = 71,
    py = 95,
    key = "janeacc",
    path = Jane.config.texture_pack .. "/c_jane_acc.png",
}

for _, v in ipairs({
    "back",
    "blind",
    "booster",
    "edition",
    "joker",
    "slugcat",
    "spectral",
    "tarot",
    "token",
    "voucher",
}) do
    assert(SMODS.load_file("src/" .. v .. ".lua"))()
end
