-- Rush Duel 操作
RushDuel = RushDuel or {}

-- 内部方法: 选择匹配卡片, 执行操作
function RushDuel._private_action_select_match(hint, filter, tp, s_range, o_range, min, max, expect, hint_selection, confirm, action, ...)
    if min < 2 or Duel.IsExistingMatchingCard(filter, tp, s_range, o_range, min, expect, ...) then
        Duel.Hint(HINT_SELECTMSG, tp, hint)
        local g = Duel.SelectMatchingCard(tp, filter, tp, s_range, o_range, min, max, expect, ...)
        if g:GetCount() > 0 then
            RushDuel.HintOrConfirm(g, hint_selection, confirm, 1 - tp)
            return action(g, ...)
        end
    end
end
-- 内部方法: 可以选择匹配卡片, 执行操作
function RushDuel._private_action_can_select_match(desc, hint, filter, tp, s_range, o_range, min, max, expect, hint_selection, confirm, action, ...)
    if Duel.IsExistingMatchingCard(filter, tp, s_range, o_range, min, expect, ...) and Duel.SelectYesNo(tp, desc) then
        Duel.Hint(HINT_SELECTMSG, tp, hint)
        local g = Duel.SelectMatchingCard(tp, filter, tp, s_range, o_range, min, max, expect, ...)
        if g:GetCount() > 0 then
            RushDuel.HintOrConfirm(g, hint_selection, confirm, 1 - tp)
            return action(g, ...)
        end
    end
end
-- 内部方法: 选择子卡片组, 执行操作
function RushDuel._private_action_select_group(hint, filter, check, tp, s_range, o_range, min, max, expect, hint_selection, confirm, action, ...)
    local g = Duel.GetMatchingGroup(filter, tp, s_range, o_range, expect, ...)
    if g:CheckSubGroup(check, min, max, ...) then
        Duel.Hint(HINT_SELECTMSG, tp, hint)
        local sg = g:SelectSubGroup(tp, check, false, min, max, ...)
        if sg:GetCount() > 0 then
            RushDuel.HintOrConfirm(sg, hint_selection, confirm, 1 - tp)
            return action(sg, ...)
        end
    end
end
-- 内部方法: 可以选择子卡片组, 执行操作
function RushDuel._private_action_can_select_group(desc, hint, filter, check, tp, s_range, o_range, min, max, expect, hint_selection, confirm, action, ...)
    local g = Duel.GetMatchingGroup(filter, tp, s_range, o_range, expect, ...)
    if g:CheckSubGroup(check, min, max, ...) and Duel.SelectYesNo(tp, desc) then
        Duel.Hint(HINT_SELECTMSG, tp, hint)
        local sg = g:SelectSubGroup(tp, check, false, min, max, ...)
        if sg:GetCount() > 0 then
            RushDuel.HintOrConfirm(sg, hint_selection, confirm, 1 - tp)
            return action(sg, ...)
        end
    end
end
-- 内部方法: 是否包含公开区域
function RushDuel._private_is_include_public(s_range, o_range)
    return (s_range | o_range) & (LOCATION_ONFIELD | LOCATION_GRAVE | LOCATION_REMOVED) ~= 0
end
-- 内部方法: 特殊召唤
function RushDuel._special_summon(target, effect, player, position, break_effect, target_player)
    if break_effect then
        Duel.BreakEffect()
    end
    local ct = Duel.SpecialSummon(target, 0, player, target_player or player, false, false, position)
    if (position & POS_FACEDOWN) ~= 0 then
        Duel.ConfirmCards(1 - player, target)
    end
    return ct
end
-- 内部方法: 盖放魔法陷阱
function RushDuel._set_spell_trap(target, effect, player, break_effect)
    if break_effect then
        Duel.BreakEffect()
    end
    return Duel.SSet(player, target)
end

-- 操作: 选择匹配卡片
function RushDuel.SelectAndDoAction(hint, filter, tp, s_range, o_range, min, max, expect, action)
    local hint_selection = RushDuel._private_is_include_public(s_range, o_range)
    return RushDuel._private_action_select_match(hint, filter, tp, s_range, o_range, min, max, expect, hint_selection, false, action)
end
-- 可选操作: 选择匹配卡片
function RushDuel.CanSelectAndDoAction(desc, hint, filter, tp, s_range, o_range, min, max, expect, action)
    local hint_selection = RushDuel._private_is_include_public(s_range, o_range)
    return RushDuel._private_action_can_select_match(desc, hint, filter, tp, s_range, o_range, min, max, expect, hint_selection, false, action)
end
-- 操作: 选择子卡片组
function RushDuel.SelectGroupAndDoAction(hint, filter, check, tp, s_range, o_range, min, max, expect, action)
    local hint_selection = RushDuel._private_is_include_public(s_range, o_range)
    return RushDuel._private_action_select_group(hint, filter, check, tp, s_range, o_range, min, max, expect, hint_selection, false, action)
end
-- 可选操作: 选择子卡片组
function RushDuel.CanSelectGroupAndDoAction(desc, hint, filter, check, tp, s_range, o_range, min, max, expect, action)
    local hint_selection = RushDuel._private_is_include_public(s_range, o_range)
    return RushDuel._private_action_can_select_group(desc, hint, filter, check, tp, s_range, o_range, min, max, expect, hint_selection, false, action)
end

-- 操作: 选择怪兽特殊召唤
function RushDuel.SelectAndSpecialSummon(filter, tp, s_range, o_range, min, max, expect, e, pos, break_effect, target_player)
    local ct = RushDuel.GetMZoneCount(target_player or tp, max)
    if ct >= min then
        return RushDuel._private_action_select_match(HINTMSG_SPSUMMON, filter, tp, s_range, o_range, min, ct, expect, false, false, RushDuel._special_summon, e, tp, pos, break_effect, target_player)
    end
    return 0
end
-- 可选操作: 选择怪兽特殊召唤
function RushDuel.CanSelectAndSpecialSummon(desc, filter, tp, s_range, o_range, min, max, expect, e, pos, break_effect, target_player)
    local ct = RushDuel.GetMZoneCount(target_player or tp, max)
    if ct >= min then
        return RushDuel._private_action_can_select_match(desc, HINTMSG_SPSUMMON, filter, tp, s_range, o_range, min, ct, expect, false, false, RushDuel._special_summon, e, tp, pos, break_effect,
            target_player)
    end
    return 0
end
-- 操作: 选择怪兽特殊召唤 (子卡片组)
function RushDuel.SelectGroupAndSpecialSummon(filter, check, tp, s_range, o_range, min, max, expect, e, pos, break_effect, target_player)
    local ct = RushDuel.GetMZoneCount(target_player or tp, max)
    if ct >= min then
        return RushDuel._private_action_select_group(HINTMSG_SPSUMMON, filter, check, tp, s_range, o_range, min, ct, expect, false, false, RushDuel._special_summon, e, tp, pos, break_effect,
            target_player)
    end
    return 0
end
-- 可选操作: 选择怪兽特殊召唤 (子卡片组)
function RushDuel.CanSelectGroupAndSpecialSummon(desc, filter, check, tp, s_range, o_range, min, max, expect, e, pos, break_effect, target_player)
    local ct = RushDuel.GetMZoneCount(target_player or tp, max)
    if ct >= min then
        return RushDuel._private_action_can_select_group(desc, HINTMSG_SPSUMMON, filter, check, tp, s_range, o_range, min, ct, expect, false, false, RushDuel._special_summon, e, tp, pos, break_effect,
            target_player)
    end
    return 0
end
-- 操作: 选择魔法，陷阱卡盖放
function RushDuel.SelectAndSet(filter, tp, s_range, o_range, min, max, expect, e, break_effect)
    local ct = RushDuel.GetSZoneCount(tp, max)
    if ct >= min then
        return RushDuel._private_action_select_match(HINTMSG_SET, filter, tp, s_range, o_range, min, ct, expect, false, false, RushDuel._set_spell_trap, e, tp, break_effect)
    end
    return 0
end
-- 可选操作: 选择魔法，陷阱卡盖放
function RushDuel.CanSelectAndSet(desc, filter, tp, s_range, o_range, min, max, expect, e, break_effect)
    local ct = RushDuel.GetSZoneCount(tp, max)
    if ct >= min then
        return RushDuel._private_action_can_select_match(desc, HINTMSG_SET, filter, tp, s_range, o_range, min, ct, expect, false, false, RushDuel._set_spell_trap, e, tp, break_effect)
    end
    return 0
end
-- 操作: 选择魔法，陷阱卡盖放 (子卡片组)
function RushDuel.SelectGroupAndSet(filter, check, tp, s_range, o_range, min, max, expect, e, break_effect)
    local ct = RushDuel.GetSZoneCount(tp, max)
    if ct >= min then
        return RushDuel._private_action_select_group(HINTMSG_SET, filter, check, tp, s_range, o_range, min, ct, expect, false, false, RushDuel._set_spell_trap, e, tp, break_effect)
    end
    return 0
end
-- 可选操作: 选择魔法，陷阱卡盖放 (子卡片组)
function RushDuel.CanSelectGroupAndSet(desc, filter, check, tp, s_range, o_range, min, max, expect, e, break_effect)
    local ct = RushDuel.GetSZoneCount(tp, max)
    if ct >= min then
        return RushDuel._private_action_can_select_group(desc, HINTMSG_SET, filter, check, tp, s_range, o_range, min, ct, expect, false, false, RushDuel._set_spell_trap, e, tp, break_effect)
    end
    return 0
end

-- 操作: 改变表示形式
function RushDuel.ChangePosition(target, pos)
    if pos == nil then
        return Duel.ChangePosition(target, POS_FACEUP_DEFENSE, POS_FACEUP_DEFENSE, POS_FACEUP_ATTACK, POS_FACEUP_ATTACK)
    else
        return Duel.ChangePosition(target, pos)
    end
end

-- 操作: 加入/返回手卡, 并给对方确认
function RushDuel.SendToHandAndExists(target, confirm_player, filter, count, expect)
    local g = RushDuel.ToMaximunGroup(target)
    if Duel.SendtoHand(g, nil, REASON_EFFECT) == 0 then
        return false
    end
    if confirm_player ~= nil then
        Duel.ConfirmCards(confirm_player, g)
    end
    return RushDuel.IsOperatedGroupExists(filter, count, expect)
end
-- 操作: 返回对方手卡, 不能确认那些卡
function RushDuel.SendToOpponentHand(target)
    local g = RushDuel.ToMaximunGroup(target)
    return Duel.SendtoHand(g, nil, REASON_EFFECT)
end

-- 操作: 送去墓地
function RushDuel.SendToGraveAndExists(target, filter, count, expect)
    local g = RushDuel.ToMaximunGroup(target)
    return Duel.SendtoGrave(g, REASON_EFFECT) ~= 0 and RushDuel.IsOperatedGroupExists(filter, count, expect)
end
-- 操作: 从卡组上面把卡送去墓地
function RushDuel.SendDeckTopToGraveAndExists(player, card_count, filter, count, expect)
    return Duel.DiscardDeck(player, card_count, REASON_EFFECT) ~= 0 and RushDuel.IsOperatedGroupExists(filter, count, expect)
end
-- 操作: 从卡组下面把卡送去墓地
function RushDuel.SendDeckBottomToGraveAndExists(player, card_count, filter, count, expect)
    local dg = RushDuel.GetDeckBottomGroup(player, card_count)
    if dg:GetCount() == 0 then
        return false
    end
    Duel.DisableShuffleCheck()
    return Duel.SendtoGrave(dg, REASON_EFFECT) ~= 0 and RushDuel.IsOperatedGroupExists(filter, count, expect)
end
-- 操作: 随机选对方的手卡送去墓地
function RushDuel.SendOpponentHandToGrave(tp, desc, min, max)
    local g = Duel.GetFieldGroup(tp, 0, LOCATION_HAND)
    local ct = g:GetCount()
    if ct < min then
        return 0
    end
    local ops = {}
    for i = min, math.min(max, ct), min do
        table.insert(ops, i)
    end
    local ac = 0
    if #ops == 1 then
        ac = table.remove(ops)
    elseif #ops > 1 then
        Duel.Hint(HINT_SELECTMSG, tp, desc)
        ac = Duel.AnnounceNumber(tp, table.unpack(ops))
    end
    if ac > 0 then
        local sg = g:RandomSelect(tp, ac)
        return Duel.SendtoGrave(sg, REASON_EFFECT)
    end
    return 0
end

-- 操作: 返回卡组
function RushDuel.SendToDeckAndExists(target, filter, count, expect)
    local g = RushDuel.ToMaximunGroup(target)
    return Duel.SendtoDeck(g, nil, 2, REASON_EFFECT) ~= 0 and RushDuel.IsOperatedGroupExists(filter, count, expect)
end
-- 操作: 返回卡组上面 (排序)
function RushDuel.SendToDeckTop(target, sort_player, target_player, sort)
    local g = RushDuel.ToMaximunGroup(target)
    if sort then
        local og, ct = RushDuel.SendToDeckSort(g, 0, REASON_EFFECT, sort_player, target_player)
        return ct
    else
        return Duel.SendtoDeck(g, nil, 0, REASON_EFFECT)
    end
end
-- 操作: 返回卡组下面 (排序)
function RushDuel.SendToDeckBottom(target, sort_player, target_player, sort)
    local g = RushDuel.ToMaximunGroup(target)
    if sort then
        local og, ct = RushDuel.SendToDeckSort(g, 1, REASON_EFFECT, sort_player, target_player)
        return ct
    else
        return Duel.SendtoDeck(g, nil, 1, REASON_EFFECT)
    end
end
-- 操作: 返回对方卡组上面 (排序)
function RushDuel.SendToOpponentDeckTop(target, player)
    local g = RushDuel.ToMaximunGroup(target)
    if g:GetCount() == 1 then
        return Duel.SendtoDeck(g, nil, 0, REASON_EFFECT)
    else
        local og, ct = RushDuel.SendToDeckSort(g, 0, REASON_EFFECT, player, 1 - player)
        return ct
    end
end
-- 操作: 返回对方卡组下面 (排序)
function RushDuel.SendToOpponentDeckBottom(target, player)
    local g = RushDuel.ToMaximunGroup(target)
    if g:GetCount() == 1 then
        return Duel.SendtoDeck(g, nil, 1, REASON_EFFECT)
    else
        local og, ct = RushDuel.SendToDeckSort(g, 1, REASON_EFFECT, player, 1 - player)
        return ct
    end
end

-- 操作: 翻开卡组并选择卡
function RushDuel.RevealDeckTopAndSelect(player, count, hint, filter, min, max)
    Duel.ConfirmDecktop(player, count)
    local g = Duel.GetDecktopGroup(player, count)
    if g:GetCount() > 0 then
        Duel.Hint(HINT_SELECTMSG, player, hint)
        local sg = g:FilterSelect(player, filter, min, max, nil)
        g:Sub(sg)
        return sg, g
    else
        return g, g
    end
end
-- 操作: 翻开卡组并可以选择卡
function RushDuel.RevealDeckTopAndCanSelect(player, count, desc, hint, filter, min, max, ...)
    Duel.ConfirmDecktop(player, count)
    local g = Duel.GetDecktopGroup(player, count)
    if g:GetCount() > 0 then
        if g:IsExists(filter, min, nil, ...) and Duel.SelectYesNo(player, desc) then
            Duel.Hint(HINT_SELECTMSG, player, hint)
            local sg = g:FilterSelect(player, filter, min, max, nil, ...)
            g:Sub(sg)
            return sg, g
        else
            return Group.CreateGroup(), g
        end
    else
        return g, g
    end
end

-- 可选操作: 抽卡
function RushDuel.CanDraw(desc, player, count, break_effect)
    if Duel.IsPlayerCanDraw(player, count) and Duel.SelectYesNo(player, desc) then
        if break_effect then
            Duel.BreakEffect()
        end
        return Duel.Draw(player, count, REASON_EFFECT)
    end
    return 0
end
-- 可选操作: 盲堆
function RushDuel.CanDiscardDeck(desc, player, count, break_effect)
    if Duel.IsPlayerCanDiscardDeck(player, count) and Duel.SelectYesNo(player, desc) then
        if break_effect then
            Duel.BreakEffect()
        end
        return Duel.DiscardDeck(player, count, REASON_EFFECT)
    end
    return 0
end
