SMODS.Sound({key = "enlightened", path = "enlightened.ogg"})
SMODS.Sound({key = "warning_heartbeat", path = "warning_heartbeat.ogg"})

for i = 1, 8 do
    SMODS.Sound({key = "gore" .. i, path = "gore" .. i .. ".ogg"})
end

for _, v in pairs({
    "artificer",
    "hunter",
    "gourmand",
    "monk",
    "rivulet",
    "rot",
    "saint",
    "spearmaster",
    "survivor",
}) do
    SMODS.Atlas {
        key = "jane" .. v,
        px = 71,
        py = 95,
        path = Jane.config.texture_pack .. "/j_jane_" .. v .. ".png"
    }
end

local epic = Cryptid and "cry_epic" or 3
local exotic = Cryptid and "cry_exotic" or 4

SMODS.Rarity {
    key = "junk",
    loc_txt = {name = "Junk"},
    badge_colour = G.C.JOKER_GREY,
}

SMODS.Joker {
    key = "monk",
    atlas = "janemonk",
    loc_txt = {
        name = "The Monk",
        text = {
            "{C:attention}Retrigger {}scored",
            "cards {C:attention}#1# time#2# {}if",
            "hand contains {C:attention}#3#",
            "or fewer card#4#",
        }
    },
    config = {extra = {retriggers = 2, requirement = 4}},
    pos = {x = 0, y = 0},
    soul_pos = {x = 1, y = 0},
    cost = 20,
    rarity = 4,
    blueprint_compat = true,
    loc_vars = function(_, _, card)
        local extra = card.ability.extra
        local retriggers = extra.retriggers
        local requirement = extra.requirement

        return {vars = {
            retriggers,
            retriggers == 1 and "" or "s",
            requirement,
            requirement == 1 and "" or "s"
        }}
    end,
    calculate = function(_, card, context)
        if not context.repetition or
            context.cardarea ~= G.play or not
            context.other_card or
            #G.play.cards > card.ability.extra.requirement then
            return
        end

        return {
            message = localize("k_again_ex"),
            repetitions = card.ability.extra.retriggers,
            colour = G.C.ORANGE,
            card = card
        }, true
    end
}

SMODS.Joker {
    key = "survivor",
    atlas = "janesurvivor",
    loc_txt = {
        name = "The Survivor",
        text = {
            "All cards held in hand",
            "{C:attention}contribute to scoring {}and",
            "are all considered as",
            "the {C:attention}first played card",
        }
    },
    pos = {x = 0, y = 0},
    soul_pos = {x = 1, y = 0},
    cost = 20,
    rarity = 4,
}

local hunter = {7, 5, 3, 2, 1}

SMODS.Joker {
    key = "hunter",
    atlas = "janehunter",
    loc_txt = {
        name = "The Hunter",
        text = {
            "{C:blue}Provides infinite hands",
            "{C:red,s,E:1}Succumbs to the Rot {}and creates",
            (Cryptid and "an {C:spectral}Empowered Tag" or "a {C:dark_edition}Negative {C:spectral}Soul") .. " {}after #1#",
            "When {C:attention}sold#2#{}, turns#3#",
            "#4#{C:red}The Rot{} without rewards",
        }
    },
    config = {extra = {rounds_left = hunter[1]}},
    pos = {x = 0, y = 0},
    eternal_compat = false,
    soul_pos = {x = 1, y = 0},
    cost = Cryptid and 20 or 8,
    rarity = Cryptid and 4 or 3,
    loc_vars = function(_, _, card)
        local function rounds(amount)
            return " round" .. ((math.abs(amount) > 1 or math.abs(amount) == 0) and "s" or "")
        end

        local rounds_left = card.ability.extra.rounds_left
        local sold = rounds_left - hunter[2]

        return {vars = {
            rounds_left .. rounds(rounds_left) .. (rounds_left <= 0 and "...?" or ""),
            sold <= 0 and "" or " after " .. sold .. rounds(sold),
            sold <= 0 and " into" or "",
            sold <= 0 and "" or "into ",
        }}
    end,
    update = function(_, card, _)
        if card.added_to_deck and card.children.center and card.children.floating_sprite then
            for k, v in ipairs(hunter) do
                if card.ability.extra.rounds_left <= v then
                    card.children.center:set_sprite_pos({x = 0, y = k - 1})
                    card.children.floating_sprite:set_sprite_pos({x = 1, y = k - 1})
                else
                    break
                end
            end
        end
    end,
    calculate = function(_, card, context)
        local function spawn_rot()
            card:flip()
            card:juice_up(2, 0.8)
            Jane.card_status_text(card, "Dead!", nil, 0.05 * card.T.h, G.C.BLACK, 2, 0, 0, nil, "bm", "jane_gore6")

            Jane.q(function()
                local card2 = create_card("Joker", G.jokers, nil, nil, nil, nil, "j_jane_rot", "hunter_rot_death")
                card2:add_to_deck()
                G.jokers:emplace(card2)
                card:set_eternal(nil)
                card2:set_eternal(true)
                play_sound("jane_gore5")
                return true
            end)
        end

        local function die()
            spawn_rot()

            Jane.q(function()
                Jane.empowered()
                return true
            end, 0.1)

            Jane.q(function()
                card:start_dissolve()
                return true
            end, 1)

            return true
        end

        if context.blueprint then
            return
        end

        if context.selling_self and card.ability.extra.rounds_left <= hunter[2] then
            spawn_rot()
        elseif not context.individual and not context.repetition and not context.retrigger_joker then
            if G.GAME.round_resets.hands <= 0 then
                G.GAME.round_resets.hands = 1
            end

            if not card.hunter_prep then
                card.hunter_prep = true
                Jane.q(function()
                    Jane.q(function()
                        card.hunter_prep = nil
                        if G.GAME.current_round.hands_left < G.GAME.round_resets.hands then
                            ease_hands_played(G.GAME.round_resets.hands - G.GAME.current_round.hands_left)
                        end

                        return true
                    end)

                    return true
                end)
            end

            if not context.end_of_round then
                return
            end

            card.hunter_prep = nil
            card.ability.extra.rounds_left = card.ability.extra.rounds_left - 1
            local rl = card.ability.extra.rounds_left
            Jane.card_status_text(card, tostring(card.ability.extra.rounds_left), nil, nil, G.C.RED, nil, nil, nil, nil, nil, "generic1")

            if rl > hunter[2] then
                card:juice_up(0.6, 0.1)
            elseif rl > hunter[3] then
                if rl == hunter[2] then
                    Jane.play_sound("jane_gore1")
                end

                card:juice_up(0.6, 0.1)
            elseif rl > hunter[4] then
                if rl == hunter[3] then
                    Jane.play_sound("jane_gore3")
                end

                card:juice_up(0.6, 0.1)
            elseif rl > hunter[5] then
                if rl == hunter[4] then
                    Jane.play_sound("jane_gore8")
                end

                card:juice_up(0.6, 0.1)
                Jane.play_sound("jane_warning_heartbeat")
            elseif rl > 0 then
                if rl == hunter[5] then
                    Jane.play_sound("jane_gore4")
                end

                card:juice_up(1.8, 0.3)
                Jane.play_sound("jane_warning_heartbeat")
            else
                card:juice_up(2, 0.8)
                Jane.play_sound("jane_warning_heartbeat")
                G.E_MANAGER:add_event(Event({trigger = "after", func = die}))
            end
        end
    end
}

SMODS.Joker {
    key = "gourmand",
    atlas = "janegourmand",
    loc_txt = {
        name = "The Gourmand",
        text = {
            "Values on {C:attention}consumables",
            "are {C:attention}multiplied{} by {C:attention}#1#",
            "when they are created",
            "{C:inactive}(If possible)",
        }
    },
    loc_vars = function(_, _, center)
        return {vars = {center.ability.modifier}}
    end,
    config = {modifier = 2},
    pos = {x = 0, y = 0},
    soul_pos = {x = 1, y = 0},
    cost = 8,
    rarity = 3,
}

local orig_set_ability = Card.set_ability

---@diagnostic disable-next-line: duplicate-set-field
function Card:set_ability(center, initial, delay_sprites)
    orig_set_ability(self, center, initial, delay_sprites)

    if next(SMODS.find_card("j_jane_gourmand")) and
        self.gc and
        self:gc().key ~= "c_base" and
        string.sub(self:gc().key, 1, 2) == "c_" then
        local mod = 1

        for _, v in pairs(SMODS.find_card("j_jane_gourmand")) do
            mod = mod * v.ability.modifier
        end

        Jane.misprintize(self, {min = mod, max = mod}, nil, true)
    end
end

SMODS.Joker {
    key = "artificer",
    atlas = "janeartificer",
    loc_txt = {
        name = "The Artificer",
        text = {
            "Grants the {C:green}ability{} to {C:red}destroy",
            "selected {C:attention}playing cards",
        }
    },
    pos = {x = 0, y = 0},
    soul_pos = {x = 1, y = 0},
    cost = Cryptid and 50 or 20,
    rarity = exotic,
    Bakery_use_button_text = function(_, _) return "DESTROY" end,
    Bakery_can_use = function(_, card)
        return not card.debuff and Jane.can_use() and next(G.hand.highlighted)
    end,
    Bakery_use_joker = function(_, _)
        for _, v in pairs(G.hand.highlighted) do
            v:start_dissolve()
        end
    end,
}

SMODS.Joker {
    key = "spearmaster",
    atlas = "janespearmaster",
    loc_txt = {
        name = "The Spearmaster",
        text = {
            "You can choose {C:attention}any number of cards",
            "after opening {C:attention}any Booster Pack",
            "{C:attention}Booster Packs{} have {C:green}+#1#{} additional cards",
        }
    },
    loc_vars = function(_, _, center)
        return {vars = {center.ability.extra.extrachoices}}
    end,
    config = {extra = {extrachoices = 1}},
    pos = {x = 0, y = 0},
    soul_pos = {x = 1, y = 0},
    cost = Cryptid and 12 or 20,
    rarity = Cryptid and epic or 4,
}

local orig_open = Card.open

---@diagnostic disable-next-line: duplicate-set-field
function Card:open()
    local orig = self.ability.extra or 1
    local spearmasters = SMODS.find_card("j_jane_spearmaster")

    if next(spearmasters) then
        for _, v in pairs(spearmasters) do
            orig = orig + v.ability.extra.extrachoices
        end

        self.config.choose = math.floor(orig)
        self.ability.extra = math.floor(orig)
    end

    orig_open(self)

    G.E_MANAGER:add_event(Event({
        delay = 0.5,
        timer = "REAL",
        func = function()
            if next(spearmasters) then
                G.GAME.pack_choices = math.floor(self.ability.extra)
            end

            return true
        end
    }))
end

SMODS.Joker {
    key = "rivulet",
    atlas = "janerivulet",
    loc_txt = {
        name = "The Rivulet",
        text = {
            "Non-{C:dark_edition}editioned{} cards are",
            "{C:attention}given a random {C:dark_edition}Edition",
        }
    },
    pos = {x = 0, y = 0},
    soul_pos = {x = 1, y = 0},
    cost = 20,
    rarity = 4,
}

local orig_draw = Card.draw

function Card:draw(layer)
    local cen = self.gc and self:gc()

    if self.children.floating_sprite and cen.sinis then
        if Jane.sinister and not self.shows_sinister then
            self.shows_sinister = true
            self.children.floating_sprite:set_sprite_pos(cen.sinis)
        elseif not Jane.sinister and self.shows_sinister then
            self.shows_sinister = nil
            self.children.floating_sprite:set_sprite_pos(cen.soul_pos)
        end

        if self.shows_sinister then
            self:juice_up(0, math.random())
        end
    end

    if cen and
        self.facing == "front" and
        self.config and
        (self.added_to_deck or
        (self.area and self.area == G.hand)) and not
        self.edition and
        (self.area == G.consumeables or cen.set ~= "Booster") and
        next(SMODS.find_card("j_jane_rivulet")) then
        self:set_edition(poll_edition("rivulet_edition", nil, nil, true))
    end

    orig_draw(self, layer)
end

local function attunement()
    local function polygloss(tbl)
        local sum = 0

        for _, v in pairs(tbl) do
            if (v.edition or {}).key == "e_jane_polygloss" then
                sum = sum + 1
            end
        end

        return sum
    end

    if Cryptid then
        return 1.001
    end

    local cards = polygloss(G.playing_cards) + polygloss(G.jokers.cards) + polygloss(G.consumeables.cards)
    local base = 1.01
    local round = 100
    local expo = cards * 2
    local error_correction = 1e-10
    return math.floor(base ^ expo * round + error_correction) / round
end

SMODS.Joker {
    key = "saint",
    atlas = "janesaint",
    loc_txt = {
        name = "The Saint{C:jane_RGB}#1#",
        text = {
            "{C:spectral}#2# {}will {C:attention}not destroy Jokers",
            "{C:jane_RGB}#3#{}#4#{X:black,C:jane_RGB,s:1.5}#5#{C:spectral}#6#{C:chips}#7#{}#8#{C:mult}#9#",
            "{C:inactive,s:1.25}#10#{C:attention,s:1.25}#11#{C:inactive,s:1.25}#12#{C:inactive}#13#{C:jane_RGB}#14#" ..
            (Cryptid and "{C:inactive}" or "{C:inactive,s:0.75}") ..
            "#15#",
        }
    },
    config = {extra = {karma = 0, max_karma = Cryptid and 10 or 3}},
    pos = {x = 0, y = 0},
    soul_pos = {x = 1, y = 0},
    cost = 20,
    rarity = 4,
    blueprint_compat = true,
    loc_vars = function(_, _, card)
        local extra = card.ability.extra
        local karma = extra.karma
        local max_karma = extra.max_karma
        local attuned = karma >= max_karma

        return {vars = {
            attuned and " (Attuned)" or "",
            Cryptid and "Ankh and Gateway" or "Ankh",
            attuned and "" or "Attune ",
            attuned and "" or "after using ",
            attuned and (Cryptid and "^^" or "^") .. attunement() or max_karma,
            attuned and "" or (Cryptid and " Gateways" or " Ankh or Soul Cards"),
            attuned and " Chips " or "",
            attuned and "& " or "",
            attuned and "Mult" or "",
            attuned and "" or "[",
            attuned and "" or karma,
            attuned and "" or " / " .. max_karma .. "]",
            attuned and "(Cannot be debuffed" .. (Cryptid and "" or ", scales with ") or "",
            attuned and (Cryptid and "" or "polygloss") or "",
            attuned and ")" or "",
        }}
    end,
    update = function(_, card, _)
        if card.added_to_deck and card.children.center and card.children.floating_sprite then
            local extra = card.ability.extra
            card.children.floating_sprite:set_sprite_pos({x = extra.is_attuned and 2 or 1, y = 0})
        end
    end,
    calculate = function(_, card, context)
        local extra = card.ability.extra
        local max_karma = extra.max_karma
        extra.is_attuned = extra.karma >= max_karma

        if extra.karma >= max_karma then
            if card.ability then
                card.ability.perishable = false
                card.ability.perish_tally = 1e9
            end

            card.debuff = false

            if not context.joker_main then
                return
            end

            local attune = attunement()

            if attune == 1 then
                return
            end

            return {
                card = card,
                colour = G.C.jane_RGB,
                sound = "talisman_eeechip",
                message = "^^" .. attune .. " Chips & Mult",
                [Cryptid and "EEchip_mod" or "Echip_mod"] = attune,
                [Cryptid and "EEmult_mod" or "Emult_mod"] = attune,
            }, true
        end

        if not (not context.blueprint and
            (not context.retrigger_joker_check and not context.retrigger_joker) and
            context.using_consumeable and
            context.consumeable) then
            return
        end

        local key = context.consumeable:gc().key

        -- TODO: Make ankh not destroy jokers
        if Cryptid and key ~= "c_cry_gateway" or not Cryptid and (key ~= "c_ankh" and key ~= "c_soul") then
            return
        end

        local quota = context.consumeable.getEvalQty and context.consumeable:getEvalQty() or 1
        extra.karma = extra.karma + quota

        card_eval_status_text(
            card,
            "extra",
            nil,
            nil,
            nil,
            {message = "+" .. quota .. " Karma", colour = G.C.PALE_GREEN}
        )

        card_eval_status_text(
            card,
            "extra",
            nil,
            nil,
            nil,
            {message = extra.karma .. " / " .. max_karma, colour = G.C.GREEN}
        )

        if extra.karma < max_karma then
            return
        end

        Jane.card_status_text(
            card,
            "!!!",
            nil,
            0.05 * card.T.h,
            G.C.DARK_EDITION,
            0.6,
            0.6,
            2,
            2,
            "bm",
            "jane_enlightened"
        )

        G.E_MANAGER:add_event(Event({
            delay = 0.1,
            func = function()
                card:flip()
                play_sound("card1")
                return true
            end
        }))

        G.E_MANAGER:add_event(Event({
            delay = 1,
            func = function()
                card:flip()
                card:juice_up(1, 1)
                play_sound("card1")
                extra.is_attuned = true
                return true
            end
        }))
    end
}

if Cryptid then
    Cryptid.aliases["saint"] = "j_jane_saint"
    Cryptid.aliases["the saint"] = "j_jane_saint"
end

SMODS.Joker {
    key = "rot",
    atlas = "janerot",
    loc_txt = {
        name = "The Rot",
        text = {
            "Clogs up your Joker slots",
            "{C:attention}Duplicates itself{} at the",
            "end of {C:attention}every ante",
        }
    },
    pos = {x = 0, y = 0},
    soul_pos = {x = 1, y = 0},
    cost = 1,
    rarity = "jane_junk",
    in_pool = function (_, _)
        return not not next(SMODS.find_card("j_jane_rot"))
    end,
    calculate = function(_, card, context)
        local function has_room()
            return G.jokers.config.card_count < G.jokers.config.card_limit
        end

        local function is_end_of_ante()
            return not context.individual and not
            context.repetition and not
            card.debuff and
            context.end_of_round and not
            context.blueprint and
            G.GAME.blind.boss and not
            (G.GAME.blind.config and G.GAME.blind.config.bonus)
        end

        local function spawn()
            local rot = copy_card(card)
            rot.cloned = true
            rot:add_to_deck()
            G.jokers:emplace(rot)
            Jane.card_status_text(rot, "...", nil, 0.05 * card.T.h, G.C.BLACK, 3, 0, 0, nil, "bm")
        end

        if has_room() and not card.cloned and is_end_of_ante() then
            spawn()
        else
            card.cloned = false
        end
    end
}
