--- STEAMODDED HEADER

--- MOD_NAME: Incantation
--- MOD_ID: incantation
--- MOD_AUTHOR: [jenwalter666, MathIsFun_]
--- MOD_DESCRIPTION: Enables the ability to stack identical consumables.
--- PRIORITY: 89999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999
--- BADGE_COLOR: 000000
--- PREFIX: inc
--- VERSION: 0.5.10
--- LOADER_VERSION_GEQ: 1.0.0

Incantation = {consumable_in_use = false, accelerate = false} --will port more things over to this global later, but for now it's going to be mostly empty

local CFG = SMODS.current_mod.config

local MaxStack = 9999
local BulkUseLimit = 9999
local NaiveBulkUseCancel = 100
local AccelerateThreshold = 3

local HardLimit = 9007199254740992

SMODS.current_mod.config_tab = function()
    return {n = G.UIT.ROOT, config = {r = 0.1, align = "cm", padding = 0.1, colour = G.C.BLACK, minw = 8, minh = 4}, nodes = {
        {n = G.UIT.R, config = {align = "cl", padding = 0}, nodes = {
            {n = G.UIT.C, config = { align = "cl", padding = 0.05 }, nodes = {
                create_toggle{ col = true, label = "", scale = 0.85, w = 0, shadow = true, ref_table = CFG, ref_value = "NegativesOnly" },
            }},
            {n = G.UIT.C, config = { align = "c", padding = 0 }, nodes = {
                { n = G.UIT.T, config = { text = localize('incant_negatives_only'), scale = 0.35, colour = G.C.UI.TEXT_LIGHT }},
            }},
        }},
		{n = G.UIT.R, config = {align = "cl", padding = 0}, nodes = {
            {n = G.UIT.C, config = { align = "cl", padding = 0.05 }, nodes = {
                create_toggle{ col = true, label = "", scale = 0.85, w = 0, shadow = true, ref_table = CFG, ref_value = "StackAnything" },
            }},
            {n = G.UIT.C, config = { align = "c", padding = 0 }, nodes = {
                { n = G.UIT.T, config = { text = localize('incant_stack_anything'), scale = 0.35, colour = G.C.RED }},
            }},
        }},
        {n = G.UIT.R, config = {align = "cl", padding = 0}, nodes = {
            {n = G.UIT.C, config = { align = "cl", padding = 0.05 }, nodes = {
                create_toggle{ col = true, label = "", scale = 0.85, w = 0, shadow = true, ref_table = CFG, ref_value = "UnsafeMode" },
            }},
            {n = G.UIT.C, config = { align = "c", padding = 0 }, nodes = {
                { n = G.UIT.T, config = { text = localize('incant_unsafe_mode'), scale = 0.35, colour = G.C.RED }},
            }},
        }}
    }}
end

local function tablecontains(haystack, needle)
	for k, v in pairs(haystack) do
		if v == needle then
			return true
		end
	end
	return false
end

local Stackable = {
	'Planet',
	'Tarot',
	'Spectral'
}

local StackableIndividual = {
	'c_black_hole',
	'c_cry_white_hole'
}

local Divisible = {
	'Planet',
	'Tarot',
	'Spectral'
}

local DivisibleIndividual = {
	'c_black_hole',
	'c_cry_white_hole'
}

local BulkUsable = {
	'Planet'
}

local BulkUsableIndividual = {
	'c_black_hole',
	'c_cry_white_hole'
}

--Allow mods to add/remove their own card types to the list

function AllowStacking(set)
	if not tablecontains(Stackable, set) then
		table.insert(Stackable, set)
	end
end

function AllowStackingIndividual(key)
	if not tablecontains(StackableIndividual, key) then
		table.insert(StackableIndividual, key)
	end
end

function AllowDividing(set)
	AllowStacking(set)
	if not tablecontains(Divisible, set) then
		table.insert(Divisible, set)
	end
end

function AllowDividingIndividual(key)
	AllowStackingIndividual(key)
	if not tablecontains(DivisibleIndividual, key) then
		table.insert(DivisibleIndividual, key)
	end
end

function AllowBulkUse(set)
	AllowStacking(set)
	AllowDividing(set)
	if not tablecontains(BulkUsable, set) then
		table.insert(BulkUsable, set)
	end
end

function AllowBulkUseIndividual(key)
	AllowStackingIndividual(key)
	AllowDividingIndividual(key)
	if not tablecontains(BulkUsableIndividual, key) then
		table.insert(BulkUsableIndividual, key)
	end
end

--[[

+++ MOD SUPPORT SKELETON : ALWAYS INCLUDE THIS IN YOUR MOD'S INITIALISATION SOMEWHERE +++

if not IncantationAddons then
	IncantationAddons = {
		Stacking = {},
		Dividing = {},
		BulkUse = {},
		StackingIndividual = {},
		DividingIndividual = {},
		BulkUseIndividual = {}
	}
end

then you can use table.insert() to insert sets/keys into the subtables contained within IncantationAddons

]]

if IncantationAddons then
	if #IncantationAddons.Stacking > 0 then
		for _, v in pairs(IncantationAddons.Stacking) do
			AllowStacking(v)
		end
	end
	if #IncantationAddons.StackingIndividual > 0 then
		for _, v in pairs(IncantationAddons.StackingIndividual) do
			AllowStackingIndividual(v)
		end
	end
	if #IncantationAddons.Dividing > 0 then
		for _, v in pairs(IncantationAddons.Dividing) do
			AllowDividing(v)
		end
	end
	if #IncantationAddons.DividingIndividual > 0 then
		for _, v in pairs(IncantationAddons.DividingIndividual) do
			AllowDividingIndividual(v)
		end
	end
	if #IncantationAddons.BulkUse > 0 then
		for _, v in pairs(IncantationAddons.BulkUse) do
			AllowBulkUse(v)
		end
	end
	if #IncantationAddons.BulkUseIndividual > 0 then
		for _, v in pairs(IncantationAddons.BulkUseIndividual) do
			AllowBulkUseIndividual(v)
		end
	end
end

function Card:getQty()
	return (self.ability or {}).qty or 1
end

function Card:setQty(quantity, dontupdatecost)
	if not quantity then quantity = 1 end
	if self:CanStack() then
		if self.ability then
			self.ability.qty = math.min(HardLimit, math.floor(quantity))
			self:create_stack_display()
			if not dontupdatecost then self:set_cost() end
		end
	end
end

function Card:addQty(quantity, dontupdatecost)
	self:setQty(self:getQty() + math.floor(quantity), dontupdatecost)
end

function Card:subQty(quantity, dont_dissolve, dontupdatecost)
	if quantity >= self:getQty() and not dont_dissolve then
		self:setQty(0)
		self.ignorestacking = true
		self:start_dissolve()
	else
		self:setQty(math.max(0, self:getQty() - math.ceil(quantity)), dontupdatecost)
	end
end

function Card:CanStack()
	if self.area and G.consumeables and self.area ~= G.consumeables then
		return false
	elseif self.ability and (self.ability.set == 'Booster' or self.ability.set == 'Voucher') then
		return false
	elseif CFG.NegativesOnly and not (self.edition and self.edition.negative) then
		return false
	elseif CFG.StackAnything then
		return true
	end

	return (self.config.center and (type(self.config.center.can_stack) == 'function' and self.config.center:can_stack() or self.config.center.can_stack)) or tablecontains(Stackable, self.ability.set) or tablecontains(StackableIndividual, self.config.center_key)
end

function Card:CanDivide()
	if CFG.StackAnything then
		return true
	end
	return (self.config.center and (type(self.config.center.can_divide) == 'function' and self.config.center:can_divide() or self.config.center.can_divide)) or tablecontains(Divisible, self.ability.set) or tablecontains(DivisibleIndividual, self.config.center_key)
end

function Card:CanBulkUse(ignoreunsafe)
	return (not ignoreunsafe and CFG.UnsafeMode) or not self.config.center.no_bulkuse and ((self.config.center and (type(self.config.center.can_bulk_use) == 'function' and self.config.center:can_bulk_use() or (self.config.center.can_bulk_use or (self.config.center.bulk_use and (type(self.config.center.bulk_use) == 'function'))))) or tablecontains(BulkUsable, self.ability.set) or tablecontains(BulkUsableIndividual, self.config.center_key))
end

function Card:getmaxuse()
	--let modders define their own bulk-use limit in case of concerns with performance
	return (self.config.center.bulk_use_limit or UseBulkCap) and math.min((self.config.center.bulk_use_limit or BulkUseLimit), self:getQty()) or (self:getQty())
end

function set_consumeable_usage(card, qty)
	qty = math.floor(qty or 1)
    if card.config.center_key and card.ability.consumeable then
      if G.PROFILES[G.SETTINGS.profile].consumeable_usage[card.config.center_key] then
        G.PROFILES[G.SETTINGS.profile].consumeable_usage[card.config.center_key].count = G.PROFILES[G.SETTINGS.profile].consumeable_usage[card.config.center_key].count + qty
      else
        G.PROFILES[G.SETTINGS.profile].consumeable_usage[card.config.center_key] = {count = 1, order = card.config.center.order}
      end
      if G.GAME.consumeable_usage[card.config.center_key] then
        G.GAME.consumeable_usage[card.config.center_key].count = G.GAME.consumeable_usage[card.config.center_key].count + qty
      else
        G.GAME.consumeable_usage[card.config.center_key] = {count = 1, order = card.config.center.order, set = card.ability.set}
      end
      G.GAME.consumeable_usage_total = G.GAME.consumeable_usage_total or {tarot = 0, planet = 0, spectral = 0, tarot_planet = 0, all = 0}
      if card.config.center.set == 'Tarot' then
        G.GAME.consumeable_usage_total.tarot = G.GAME.consumeable_usage_total.tarot + qty  
        G.GAME.consumeable_usage_total.tarot_planet = G.GAME.consumeable_usage_total.tarot_planet + qty
      elseif card.config.center.set == 'Planet' then
        G.GAME.consumeable_usage_total.planet = G.GAME.consumeable_usage_total.planet + qty
        G.GAME.consumeable_usage_total.tarot_planet = G.GAME.consumeable_usage_total.tarot_planet + qty
      elseif card.config.center.set == 'Spectral' then  G.GAME.consumeable_usage_total.spectral = G.GAME.consumeable_usage_total.spectral + qty
      end

      G.GAME.consumeable_usage_total.all = G.GAME.consumeable_usage_total.all + qty

      if not card.config.center.discovered then
        discover_card(card)
      end

      if card.config.center.set == 'Tarot' or card.config.center.set == 'Planet' then 
        G.E_MANAGER:add_event(Event({
          trigger = 'immediate',
          func = function()
            G.E_MANAGER:add_event(Event({
              trigger = 'immediate',
              func = function()
                G.GAME.last_tarot_planet = card.config.center_key
                  return true
              end
            }))
              return true
          end
        }))
      end

    end
    G:save_settings()
end

function Card:split(amount, forced, fullmax)
	if not amount then amount = math.floor((self:getQty()) / 2) end
	amount = math.max(1, amount)
	if (self.ability.qty or 0) > (fullmax and 0 or 1) and (self:CanDivide() or forced) and not self.ignorestacking then
		local traysize = G.consumeables.config.card_limit
		if (self.edition or {}).negative then
			traysize = traysize + 1
		end
		local split = copy_card(self)
		local qty2 = math.min(self.ability.qty - (fullmax and 0 or 1), amount)
		G.consumeables.config.card_limit = #G.consumeables.cards + 1
		split.ignorestacking = true
		split.created_from_split = true
		split:add_to_deck()
		G.consumeables:emplace(split, nil, nil, true)
		split.ability.qty = qty2
		self.ability.qty = self.ability.qty - qty2
		G.consumeables.config.card_limit = traysize
		if qty2 > 1 then
			split:create_stack_display()
		end
		split:set_cost()
		self:set_cost()
		play_sound('card1')
		return split
	end
end

function Card:try_merge()
	if self:CanStack() and not self.ignorestacking then
		for _, v in pairs(G.consumeables.cards) do

			if CFG.NegativesOnly and not (v.edition or {}).negative then
				-- Ignore this
			elseif v ~= self and not v.nomerging and not v.ignorestacking and v.config.center_key == self.config.center_key and (((v.edition or {}).type or '') == ((self.edition or {}).type or '')) and (v:getQty() < (UseStackCap and MaxStack or HardLimit)) then
				local space = (UseStackCap and MaxStack or HardLimit) - (v:getQty())
				v.ability.qty = (v:getQty()) + math.min((self:getQty()), space)
				v:create_stack_display()
				v:juice_up(0.5, 0.5)
				play_sound('card1')
				v:set_cost()
				if (self:getQty()) - space < 1 then
					self.ignorestacking = true
					self.area:remove_card(self)
					self:remove()
					return true
				else
					self.ability.qty = (self:getQty()) - space
					self:set_cost()
				end
			end
		end
	end
end

function Card:getEvalQty()
	return self.cardinuse and math.min(self:getQty(), (self.bulkuse and not self.naivebulkuse) and self:getQty() or (self.OverrideBulkUseLimit or NaiveBulkUseCancel)) or self:getQty()
end

local useconsumeref = Card.use_consumeable

function Card:use_consumeable(area, copier)
	local obj = self.config.center
	local uselim = self.OverrideBulkUseLimit or NaiveBulkUseCancel
	local qty = self:getQty()
	if qty <= 0 then
		self:setQty(1, true)
		qty = 1
	end
	if self.ability then
		self.ability.qty_initial = qty
	end
	if not self.naivebulkuse and self.bulkuse and obj.bulk_use and type(obj.bulk_use) == 'function' then
		set_consumeable_usage(self, qty)
		return obj:bulk_use(self, area, copier, qty)
	elseif not self.naivebulkuse and self.ability.consumeable.hand_type then
		update_hand_text({sound = 'button', volume = 0.7, pitch = 0.8, delay = 0.3}, {handname=localize(self.ability.consumeable.hand_type, 'poker_hands'),chips = G.GAME.hands[self.ability.consumeable.hand_type].chips, mult = G.GAME.hands[self.ability.consumeable.hand_type].mult, level=G.GAME.hands[self.ability.consumeable.hand_type].level})
        level_up_hand(copier or self, self.ability.consumeable.hand_type, nil, qty)
        update_hand_text({sound = 'button', volume = 0.7, pitch = 1.1, delay = 0}, {mult = 0, chips = 0, handname = '', level = ''})
		set_consumeable_usage(self, qty)
	elseif not self.naivebulkuse and self.ability.name == 'Black Hole' then
        update_hand_text({sound = 'button', volume = 0.7, pitch = 0.8, delay = 0.3}, {handname=localize('k_all_hands'),chips = '...', mult = '...', level=''})
        G.E_MANAGER:add_event(Event({trigger = 'after', delay = 0.2, func = function()
            play_sound('tarot1')
            self:juice_up(0.8, 0.5)
            G.TAROT_INTERRUPT_PULSE = true
            return true end }))
        update_hand_text({delay = 0}, {mult = '+', StatusText = true})
        G.E_MANAGER:add_event(Event({trigger = 'after', delay = 0.9, func = function()
            play_sound('tarot1')
            self:juice_up(0.8, 0.5)
            return true end }))
        update_hand_text({delay = 0}, {chips = '+', StatusText = true})
        G.E_MANAGER:add_event(Event({trigger = 'after', delay = 0.9, func = function()
            play_sound('tarot1')
            self:juice_up(0.8, 0.5)
            G.TAROT_INTERRUPT_PULSE = nil
            return true end }))
        update_hand_text({sound = 'button', volume = 0.7, pitch = 0.9, delay = 0}, {level='+' .. qty})
        delay(1.3)
        for k, v in pairs(G.GAME.hands) do
            level_up_hand(self, k, true, qty)
        end
        update_hand_text({sound = 'button', volume = 0.7, pitch = 1.1, delay = 0}, {mult = 0, chips = 0, handname = '', level = ''})
		set_consumeable_usage(self, qty)
	else
		Incantation.accelerate = qty > AccelerateThreshold or self.force_incantation_acceleration
		self.cardinuse = true
		Incantation.consumable_in_use = true
		local lim = math.min(qty, uselim)
		local newqty = math.max(0, qty - lim)
		if not self.ability.qty then self.ability.qty = 1 end
		for i = 1, lim do
			useconsumeref(self,area,copier)
			G.E_MANAGER:add_event(Event({
				trigger = 'immediate',
				delay = 0.1,
				blockable = true,
				func = function()
					self.ability.qty = self.ability.qty - 1
					play_sound('button', self.ability.qty <= 0 and 1 or 0.85, 0.7)
					return true
				end
			}))
		end
		G.E_MANAGER:add_event(Event({
			trigger = 'after',
			delay = 0.1,
			blockable = true,
			func = function()
				Incantation.consumable_in_use = false
				Incantation.accelerate = false
				self.cardinuse = nil
				self.bulkuse = nil
				self.naivebulkuse = nil
				self.OverrideBulkUseLimit = nil
				if obj.keep_on_use and obj:keep_on_use(self) then
					self.ignorestacking = false
					self.ability.qty = obj.keep_on_use_retain_stack and qty or (newqty + 1)
				else
					if newqty > 0 then
						self:split(newqty, true, true)
					end
				end
				return true
			end
		}))
	end
end

local startdissolveref = Card.start_dissolve
function Card:start_dissolve(a,b,c,d)
	if self.ability.qty and self.ability.qty > 1 and Incantation.consumable_in_use and self.cardinuse and not self.ignore_incantation_consumable_in_use then return end
	self.ignorestacking = true
	return startdissolveref(self,a,b,c,d)
end

local usecardref = G.FUNCS.use_card

function CanUseStackButtons()
	if ((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 then
		return false
	end
	return true
end

G.FUNCS.use_card = function(e, mute, nosave)
    local card = e.config.ref_table
	local useamount = card.bulkuse and card:getmaxuse() or 1
	if ((card.ability or {}).qty or 1) > useamount then
		card.highlighted = false
		card.bulkuse = false
		card.OverrideBulkUseLimit = useamount
	end
	usecardref(e, mute, nosave)
end

G.FUNCS.can_split_card = function(e)
	local card = e.config.ref_table
	local splitone = e.config.issplitone
	if (card:getQty()) > 1 and card.highlighted and CanUseStackButtons() and not card.ignorestacking then
        e.config.colour = splitone and G.C.GREEN or G.C.PURPLE
        e.config.button = splitone and 'split_one' or 'split_half'
		e.states.visible = true
	else
        e.config.colour = G.C.UI.BACKGROUND_INACTIVE
        e.config.button = nil
		e.states.visible = false
	end
end

function Card:MergeAvailable()
	if ((self.config or {}).center or {}).set ~= 'Joker' and ((self.config or {}).center or {}).set ~= 'Booster' then
		for k, v in pairs(G.consumeables.cards) do
			if v then
				if v ~= self and (v.config or {}).center_key == (self.config or {}).center_key and ((v.edition or {}).type or '') == ((self.edition or {}).type or '') then
					return true
				end
			end
		end
	end
end

G.FUNCS.can_merge_card = function(e)
	local card = e.config.ref_table
	if card:CanStack() and card.highlighted and not card.ignorestacking and CanUseStackButtons() and card:MergeAvailable() then
        e.config.colour = G.C.BLUE
        e.config.button = 'merge_card'
		e.states.visible = true
	else
        e.config.colour = G.C.UI.BACKGROUND_INACTIVE
        e.config.button = nil
		e.states.visible = false
	end
end

G.FUNCS.can_use_all = function(e)
	local card = e.config.ref_table
	local obj = card.config.center
	if card:CanBulkUse() and ((tablecontains(BulkUsable, card.ability.set) or tablecontains(BulkUsableIndividual, card.config.center_key)) or (obj.bulk_use and type(obj.bulk_use) == 'function')) and (card:getQty()) > 1 and card.highlighted and CanUseStackButtons() and not card.ignorestacking then
        e.config.colour = G.C.DARK_EDITION
        e.config.button = 'use_all'
		e.states.visible = true
	else
        e.config.colour = G.C.UI.BACKGROUND_INACTIVE
        e.config.button = nil
		e.states.visible = false
	end
end

G.FUNCS.can_use_every_planet = function(e)
	local card = e.config.ref_table
	local obj = card.config.center
	if (((card.config or {}).center or {}).set or '') == 'Planet' and card:CanBulkUse() and CanUseStackButtons() and not card.ignorestacking and not obj.ignore_allplanets then
        e.config.colour = G.C.SECONDARY_SET.Planet
        e.config.button = 'use_every_planet'
		e.states.visible = true
	else
        e.config.colour = G.C.UI.BACKGROUND_INACTIVE
        e.config.button = nil
		e.states.visible = false
	end
end

G.FUNCS.can_use_naivebulk = function(e)
	local card = e.config.ref_table
	local obj = card.config.center
	if (card:CanBulkUse() or CFG.UnsafeMode) and (CFG.UnsafeMode or not obj.bulk_use or type(obj.bulk_use) ~= 'function') and (card:getQty()) > 1 and card.highlighted and CanUseStackButtons() and not card.ignorestacking then
        e.config.colour = G.C.BLACK
        e.config.button = 'use_naivebulk'
		e.states.visible = true
	else
        e.config.colour = G.C.UI.BACKGROUND_INACTIVE
        e.config.button = nil
		e.states.visible = false
	end
end

G.FUNCS.split_half = function(e)
	local card = e.config.ref_table
	card:split(math.floor(card.ability.qty / 2))
end

G.FUNCS.split_one = function(e)
	local card = e.config.ref_table
	card:split(1)
end

G.FUNCS.merge_card = function(e)
	local card = e.config.ref_table
	card:try_merge()
end

G.FUNCS.use_all = function(e)
	local card = e.config.ref_table
	local obj = card.config.center
	if card:CanBulkUse() and (not obj.can_use or obj:can_use(card)) and (card:getQty()) > 1 and card.highlighted then
		card.bulkuse = true
		G.FUNCS.use_card(e, false, true)
	end
end

function runthrough_planets()
	for i = 1, #G.play.cards do
		local card = G.play.cards[i]
		if card then
			local obj = card.config.center
			if (((card.config or {}).center or {}).set or '') == 'Planet' then
				card.bulkuse = card:CanBulkUse() and math.max(1, card:getQty()) > 1
				card.force_incantation_acceleration = true
				card:use_consumeable(G.consumeables)
				if G.betmma_abilities then
					for i = 1, #G.betmma_abilities.cards do
						G.betmma_abilities.cards[i]:calculate_joker({using_consumeable = true, consumeable = card})
					end
				end
				for i = 1, #G.jokers.cards do
					local effects = G.jokers.cards[i]:calculate_joker({using_consumeable = true, consumeable = card})
					if effects and effects.joker_repetitions then
						rep_list = effects.joker_repetitions
						for z=1, #rep_list do
							if type(rep_list[z]) == 'table' and rep_list[z].repetitions then
								for r=1, rep_list[z].repetitions do
									card_eval_status_text(rep_list[z].card, 'jokers', nil, nil, nil, rep_list[z])
									G.jokers.cards[i]:calculate_joker({using_consumeable = true, consumeable = card, retrigger_joker = true})
								end
							end
						end
					end
				end
				G.E_MANAGER:add_event(Event({func = function()
					card.ignore_incantation_consumable_in_use = true
					card:start_dissolve()
					return true
				end}))
			end
		end
		G.E_MANAGER:add_event(Event({
			trigger = 'after',
			delay = 0.1,
			blockable = true,
			func = function()
				Incantation.consumable_in_use = false
				Incantation.accelerate = false
				return true
			end
		}))
	end
end

G.FUNCS.use_every_planet = function(e)
	local targets = {}
	for i = 1, #G.consumeables.cards do
		local card = G.consumeables.cards[i]
		if card then
			local obj = card.config.center
			if (((card.config or {}).center or {}).set or '') == 'Planet' and (not obj.can_use or obj:can_use(card)) and not obj.ignore_allplanets then
				table.insert(targets, card)
			end
		end
	end
	for i = 1, #targets do
		local card = targets[i]
		G.E_MANAGER:add_event(Event({func = function()
			if math.ceil(i/2) == i/2 then play_sound('card1') end
			card.area:remove_card(card)
			G.play:emplace(card)
			return true
		end}))
	end
	G.E_MANAGER:add_event(Event({func = function()
		runthrough_planets()
		return true
	end}))
end

G.FUNCS.use_naivebulk = function(e)
	local card = e.config.ref_table
	local obj = card.config.center
	if card:CanBulkUse() and (not obj.can_use or obj:can_use(card)) and (card:getQty()) > 1 and card.highlighted and CFG.UnsafeMode then
		card.naivebulkuse = true
		card.bulkuse = true
		G.FUNCS.use_card(e, false, true)
	end
end

G.FUNCS.disablestackdisplay = function(e)
	local card = e.config.ref_table
	e.states.visible = ((card:getQty()) > 1 and not card.ignorestacking) or card.cardinuse
end

function Card:create_stack_display()
	if not self.children.stackdisplay and self:CanStack() and not self.playing_card then
		self.children.stackdisplay = UIBox {
			definition = {
				n = G.UIT.ROOT,
				config = {
					minh = 0.6,
					maxh = 1.2,
					minw = 0.5,
					maxw = 2,
					r = 0.001,
					padding = 0.1,
					align = 'cm',
					colour = adjust_alpha(darken(G.C.BLACK, 0.2), 0.6),
					shadow = false,
					func = 'disablestackdisplay',
					ref_table = self
				},
				nodes = {
					{
						n = G.UIT.T,
						config = {
							text = 'x',
							scale = 0.4,
							colour = G.C.MULT
						}
					},
					{
						n = G.UIT.T,
						config = {
							ref_table = self.ability,
							ref_value = 'qty',
							scale = 0.4,
							colour = G.C.UI.TEXT_LIGHT
						}
					}
				}
			},
			config = {
				align = 'cm',
				bond = 'Strong',
				parent = self
			},
			states = {
				collide = { can = false },
				drag = { can = true }
			}
		}
	end
end

local card_load_ref = Card.load
function Card:load(cardTable, other_card)
	card_load_ref(self, cardTable, other_card)
	if self.ability then
		if self.ability.qty then
			self:create_stack_display()
		end
	end
end

local deckadd = Card.add_to_deck
function Card:add_to_deck(from_debuff)
	deckadd(self, from_debuff)
	if G.consumeables then
		if self:CanStack() then
			if not self.ignorestacking then
				G.E_MANAGER:add_event(Event({
					trigger = 'after',
					delay = 0.1,
					blocking = false,
					func = function()
						if self and self.area and self.area ~= 'shop' and self.area ~= 'pack_cards' then
							self:try_merge()
						end
						return true
					end
				}))
			end
			self.ignorestacking = nil
		end
	end
end

local hlref = Card.highlight

function Card:highlight(is_highlighted)
	local isplanet = ((self.ability or {}).set or '') == 'Planet'
	if self:CanStack() and self.added_to_deck and not self.ignorestacking then
		if is_highlighted then
			if isplanet then
				self.children.everyplanetbutton = UIBox {
					definition = {
						n = G.UIT.ROOT,
						config = {
							minh = 0.3,
							maxh = 0.6,
							minw = 0.3,
							maxw = 4,
							r = 0.08,
							padding = 0.1,
							align = 'cm',
							colour = G.C.SECONDARY_SET.Planet,
							shadow = true,
							button = 'use_every_planet',
							func = 'can_use_every_planet',
							ref_table = self
						},
						nodes = {
							{
								n = G.UIT.T,
								config = {
									text = 'MASS-USE PLANETS',
									scale = 0.3,
									colour = G.C.UI.TEXT_LIGHT
								}
							}
						}
					},
					config = {
						align = 'bmi',
						offset = {
							x = 0,
							y = 1
						},
						bond = 'Strong',
						parent = self
					}
				}
			end
			self.children.useallbutton = UIBox {
				definition = {
					n = G.UIT.ROOT,
					config = {
						minh = 0.3,
						maxh = 0.6,
						minw = 0.3,
						maxw = 4,
						r = 0.08,
						padding = 0.1,
						align = 'cm',
						colour = G.C.DARK_EDITION,
						shadow = true,
						button = 'use_all',
						func = 'can_use_all',
						ref_table = self
					},
					nodes = {
						{
							n = G.UIT.T,
							config = {
								text = 'BULK USE' .. (CFG.UnsafeMode and ' (NORMAL)' or ''),
								scale = 0.3,
								colour = G.C.UI.TEXT_LIGHT
							}
						}
					}
				},
				config = {
					align = 'bmi',
					offset = {
						x = 0,
						y = 0.5
					},
					bond = 'Strong',
					parent = self
				}
			}
			self.children.mergebutton = UIBox {
				definition = {
					n = G.UIT.ROOT,
					config = {
						minh = 0.3,
						maxh = 0.6,
						minw = 0.3,
						maxw = 4,
						r = 0.08,
						padding = 0.1,
						align = 'cm',
						colour = G.C.BLUE,
						shadow = true,
						button = 'merge_card',
						func = 'can_merge_card',
						ref_table = self
					},
					nodes = {
						{
							n = G.UIT.T,
							config = {
								text = 'MERGE',
								scale = 0.3,
								colour = G.C.UI.TEXT_LIGHT
							}
						}
					}
				},
				config = {
					align = 'bmi',
					offset = {
						x = 0,
						y = 1 + (isplanet and 0.5 or 0)
					},
					bond = 'Strong',
					parent = self
				}
			}
			self.children.splithalfbutton = UIBox {
				definition = {
					n = G.UIT.ROOT,
					config = {
						minh = 0.3,
						maxh = 0.6,
						minw = 0.3,
						maxw = 4,
						r = 0.08,
						padding = 0.1,
						align = 'cm',
						colour = G.C.PURPLE,
						shadow = true,
						button = 'split_half',
						func = 'can_split_card',
						ref_table = self
					},
					nodes = {
						{
							n = G.UIT.T,
							config = {
								text = 'SPLIT HALF',
								scale = 0.3,
								colour = G.C.UI.TEXT_LIGHT
							}
						}
					}
				},
				config = {
					align = 'bmi',
					offset = {
						x = 0,
						y = 1.5 + (isplanet and 0.5 or 0)
					},
					bond = 'Strong',
					parent = self
				}
			}
			self.children.splitonebutton = UIBox {
				definition = {
					n = G.UIT.ROOT,
					config = {
						minh = 0.3,
						maxh = 0.6,
						minw = 0.3,
						maxw = 4,
						r = 0.08,
						padding = 0.1,
						align = 'cm',
						issplitone = true,
						colour = G.C.GREEN,
						shadow = true,
						button = 'split_one',
						func = 'can_split_card',
						ref_table = self
					},
					nodes = {
						{
							n = G.UIT.T,
							config = {
								text = 'SPLIT ONE',
								scale = 0.3,
								colour = G.C.UI.TEXT_LIGHT
							}
						}
					}
				},
				config = {
					align = 'bmi',
					offset = {
						x = 0,
						y = 2 + (isplanet and 0.5 or 0)
					},
					bond = 'Strong',
					parent = self
				}
			}
			if CFG.UnsafeMode then
				self.children.useallnaivebutton = UIBox {
					definition = {
						n = G.UIT.ROOT,
						config = {
							minh = 0.3,
							maxh = 0.6,
							minw = 0.3,
							maxw = 4,
							r = 0.08,
							padding = 0.1,
							align = 'cm',
							colour = G.C.BLACK,
							shadow = true,
							button = 'use_naivebulk',
							func = 'can_use_naivebulk',
							ref_table = self
						},
						nodes = {
							{
								n = G.UIT.T,
								config = {
									text = 'BULK USE (ONE-AT-A-TIME)',
									scale = 0.3,
									colour = G.C.RED
								}
							}
						}
					},
					config = {
						align = 'bmi',
						offset = {
							x = 0,
							y = 2.5 + (isplanet and 0.5 or 0)
						},
						bond = 'Strong',
						parent = self
					}
				}
			end
		else
			if isplanet then
				if self.children.everyplanetbutton then self.children.everyplanetbutton:remove();self.children.everyplanetbutton = nil end
			end
			if self.children.splithalfbutton then self.children.splithalfbutton:remove();self.children.splithalfbutton = nil end
			if self.children.splitonebutton then self.children.splitonebutton:remove();self.children.splitonebutton = nil end
			if self.children.mergebutton then self.children.mergebutton:remove();self.children.mergebutton = nil end
			if self.children.useallbutton then self.children.useallbutton:remove();self.children.useallbutton = nil end
			if CFG.UnsafeMode then
				if self.children.useallnaivebutton then self.children.useallnaivebutton:remove();self.children.useallnaivebutton = nil end
			end
		end
	end
	return hlref(self,is_highlighted)
end

local ccr = copy_card

function copy_card(other, new_card, card_scale, playing_card, strip_edition)
	local card = ccr(other, new_card, card_scale, playing_card, strip_edition)
	if card then
		if card:getQty() <= 0 then card:setQty(1) end
	end
	card.OverrideBulkUseLimit = nil
	card.bulkuse = nil
	card.naivebulkuse = nil
	card.cardinuse = nil
	card.eval_qty = nil
	return card
end

local costref = Card.set_cost
function Card:set_cost()
	costref(self)
	self.sell_cost = self.sell_cost * math.max(1, (self.ability or {}).qty or 1)
    self.sell_cost_label = self.facing == 'back' and '?' or self.sell_cost
end

if not (SMODS.Mods['jen'] or {}).can_load then
	SMODS.Joker:take_ownership('perkeo', {
		name = "Perkeo (Incantation)",
		loc_vars = function(self, info_queue, center)
			info_queue[#info_queue+1] = {key = 'e_negative_consumable', set = 'Edition', config = {extra = 1}}
			return {vars = {center.ability.extra}}
		end,
		calculate = function(self, card, context)
			if context.ending_shop then
				if G.consumeables.cards[1] then
					G.E_MANAGER:add_event(Event({
						func = function() 
							local total, checked, center = 0, 0, nil
							for i = 1, #G.consumeables.cards do
								total = total + (G.consumeables.cards[i]:getQty())
							end
							local poll = pseudorandom(pseudoseed('perkeo'))*total
							for i = 1, #G.consumeables.cards do
								checked = checked + (G.consumeables.cards[i]:getQty())
								if checked >= poll then
									center = G.consumeables.cards[i]
									break
								end
							end
							local card = copy_card(center, nil)
							card.ability.qty = 1
							card:set_edition({negative = true}, true)
							card:add_to_deck()
							G.consumeables:emplace(card) 
							return true
						end}))
					card_eval_status_text(context.blueprint_card or card, 'extra', nil, nil, nil, {message = localize('k_duplicated_ex')})
					return {calculated = true}
				end
			end
		end
	})
end
