-- Rush Duel 融合术/结合
RushDuel = RushDuel or {}

-- 添加 融合术/结合 素材
function RushDuel.AddFusionProcedure(card, ...)
    Auxiliary.AddFusionProcMix(card, true, true, ...)
end
-- 添加 融合术/结合 素材 (特殊的素材要求)
function RushDuel.AddFusionProcedureSP(card, matfilter, matcheck, min, max, insf, sub)
    local insf = insf ~= false
    local sub = sub ~= false
    if not card:IsStatus(STATUS_COPYING_EFFECT) then
        local e1 = Effect.CreateEffect(card)
        e1:SetType(EFFECT_TYPE_SINGLE)
        e1:SetCode(EFFECT_FUSION_MATERIAL)
        e1:SetProperty(EFFECT_FLAG_CANNOT_DISABLE + EFFECT_FLAG_UNCOPYABLE)
        e1:SetCondition(RushDuel.FusionProcCondition(matfilter, matcheck, min, max, insf, sub))
        e1:SetOperation(RushDuel.FusionProcOperation(matfilter, matcheck, min, max, insf, sub))
        card:RegisterEffect(e1)
    end
end
function RushDuel.FusionProcCheck(sg, tp, fc, sub, chkfnf, matfilter, matcheck)
    local chkf = chkfnf & 0xff
    local concat_fusion = chkfnf & 0x200 > 0
    if not concat_fusion and sg:IsExists(Auxiliary.TuneMagicianCheckX, 1, nil, sg, EFFECT_TUNE_MAGICIAN_F) then
        return false
    end
    if not Auxiliary.MustMaterialCheck(sg, tp, EFFECT_MUST_BE_FMATERIAL) then
        return false
    end
    return RushDuel.FusionSubCheck(sg, fc, sub, matfilter) and matcheck(sg, tp, fc, chkf)
        and (chkf == PLAYER_NONE or Duel.GetLocationCountFromEx(tp, tp, sg, fc) > 0)
        and (not Auxiliary.FCheckAdditional or Auxiliary.FCheckAdditional(tp, sg, fc))
        and (not Auxiliary.FGoalCheckAdditional or Auxiliary.FGoalCheckAdditional(tp, sg, fc))
end
function RushDuel.FusionSubCheck(sg, fc, sub, matfilter)
    local ct = sg:FilterCount(matfilter, nil, fc, false)
    if not sub then
        return ct == sg:GetCount()
    else
        return ct >= sg:GetCount() - 1
    end
end
function RushDuel.FusionProcCondition(matfilter, matcheck, min, max, insf, sub)
    return function(e, g, gc, chkfnf)
        if g == nil then
            return insf and Auxiliary.MustMaterialCheck(nil, e:GetHandlerPlayer(), EFFECT_MUST_BE_FMATERIAL)
        end
        local c = e:GetHandler()
        local tp = c:GetControler()
        local notfusion = chkfnf & 0x100 > 0
        local concat_fusion = chkfnf & 0x200 > 0
        local sub = (sub or notfusion) and not concat_fusion
        local mg = g:Filter(Auxiliary.FConditionFilterMix, c, c, sub, concat_fusion, matfilter)
        if gc then
            if not mg:IsContains(gc) then
                return false
            end
            Duel.SetSelectedCard(Group.FromCards(gc))
        end
        return mg:CheckSubGroup(RushDuel.FusionProcCheck, min, max, tp, c, sub, chkfnf, matfilter, matcheck)
    end
end
function RushDuel.FusionProcOperation(matfilter, matcheck, min, max, insf, sub)
    return function(e, tp, eg, ep, ev, re, r, rp, gc, chkfnf)
        local c = e:GetHandler()
        local tp = c:GetControler()
        local notfusion = chkfnf & 0x100 > 0
        local concat_fusion = chkfnf & 0x200 > 0
        local sub = (sub or notfusion) and not concat_fusion
        local mg = eg:Filter(Auxiliary.FConditionFilterMix, c, c, sub, concat_fusion, matfilter)
        if gc then
            Duel.SetSelectedCard(Group.FromCards(gc))
        end
        Duel.Hint(HINT_SELECTMSG, tp, HINTMSG_FMATERIAL)
        local sg = mg:SelectSubGroup(tp, RushDuel.FusionProcCheck, false, min, max, tp, c, sub, chkfnf, matfilter, matcheck)
        Duel.SetFusionMaterial(sg)
    end
end

-- 创建效果: 融合术/结合 召唤
function RushDuel.CreateFusionEffect(card, matfilter, spfilter, exfilter, s_range, o_range, mat_check, mat_move, target_action, operation_action, including_self)
    local self_range = s_range or 0
    local opponent_range = o_range or 0
    local move = mat_move or RushDuel.FusionToGrave
    local include = including_self or false
    local e = Effect.CreateEffect(card)
    e:SetTarget(RushDuel.FusionTarget(matfilter, spfilter, exfilter, self_range, opponent_range, mat_check, include, target_action))
    e:SetOperation(RushDuel.FusionOperation(matfilter, spfilter, exfilter, self_range, opponent_range, mat_check, move, include, operation_action))
    return e
end
-- 判断条件: 是否可以进行 融合术/结合 召唤
function RushDuel.IsCanFusionSummon(e, tp, matfilter, spfilter, exfilter, s_range, o_range, mat_check, including_self, except)
    local chkf = tp
    local mg1 = Duel.GetFusionMaterial(tp):Filter(RushDuel.FusionMaterialFilter, except, matfilter)
    if s_range ~= 0 or o_range ~= 0 then
        local mg2 = Duel.GetMatchingGroup(exfilter, tp, s_range, o_range, except)
        mg1:Merge(mg2)
    end
    aux.FGoalCheckAdditional = mat_check
    local gc = nil
    if including_self then
        gc = e:GetHandler()
    end
    local res = Duel.IsExistingMatchingCard(RushDuel.FusionSpecialSummonFilter, tp, LOCATION_EXTRA, 0, 1, nil, e, tp, mg1, nil, gc, chkf, spfilter)
    if not res then
        local ce = Duel.GetChainMaterial(tp)
        if ce ~= nil then
            local fgroup = ce:GetTarget()
            local mg3 = fgroup(ce, e, tp)
            local mf = ce:GetValue()
            res = Duel.IsExistingMatchingCard(RushDuel.FusionSpecialSummonFilter, tp, LOCATION_EXTRA, 0, 1, nil, e, tp, mg3, mf, gc, chkf, spfilter)
        end
    end
    aux.FGoalCheckAdditional = nil
    return res
end
function RushDuel.FusionMaterialFilter(c, filter, e)
    return (not filter or filter(c)) and (not e or not c:IsImmuneToEffect(e))
end
function RushDuel.FusionSpecialSummonFilter(c, e, tp, m, f, gc, chkf, filter)
    return c:IsType(TYPE_FUSION) and (not filter or filter(c, e, tp, m, f, chkf)) and (not f or f(c)) and c:IsCanBeSpecialSummoned(e, SUMMON_TYPE_FUSION, tp, false, false) and c:CheckFusionMaterial(m, gc, chkf)
end
function RushDuel.ConfirmCardFilter(c)
    return c:IsLocation(LOCATION_HAND) or (c:IsLocation(LOCATION_MZONE) and c:IsFacedown())
end
function RushDuel.FusionTarget(matfilter, spfilter, exfilter, s_range, o_range, mat_check, including_self, action)
    return function(e, tp, eg, ep, ev, re, r, rp, chk)
        if chk == 0 then
            return RushDuel.IsCanFusionSummon(e, tp, matfilter, spfilter, exfilter, s_range, o_range, mat_check, including_self, nil)
        end
        if action ~= nil then
            action(e, tp, eg, ep, ev, re, r, rp)
        end
        Duel.SetOperationInfo(0, CATEGORY_SPECIAL_SUMMON, nil, 1, tp, LOCATION_EXTRA)
    end
end
function RushDuel.FusionOperation(matfilter, spfilter, exfilter, s_range, o_range, mat_check, mat_move, including_self, action)
    return function(e, tp, eg, ep, ev, re, r, rp)
        local chkf = tp
        local mg1 = Duel.GetFusionMaterial(tp):Filter(RushDuel.FusionMaterialFilter, nil, matfilter, e)
        if s_range ~= 0 or o_range ~= 0 then
            local mg2 = Duel.GetMatchingGroup(aux.NecroValleyFilter(exfilter), tp, s_range, o_range, nil, e)
            mg1:Merge(mg2)
        end
        aux.FGoalCheckAdditional = mat_check
        local gc = nil
        if including_self then
            gc = e:GetHandler()
        end
        local sg1 = Duel.GetMatchingGroup(RushDuel.FusionSpecialSummonFilter, tp, LOCATION_EXTRA, 0, nil, e, tp, mg1, nil, gc, chkf, spfilter)
        local mg3 = nil
        local sg2 = nil
        local ce = Duel.GetChainMaterial(tp)
        if ce ~= nil then
            local fgroup = ce:GetTarget()
            mg3 = fgroup(ce, e, tp)
            local mf = ce:GetValue()
            sg2 = Duel.GetMatchingGroup(RushDuel.FusionSpecialSummonFilter, tp, LOCATION_EXTRA, 0, nil, e, tp, mg3, mf, gc, chkf, spfilter)
        end
        local mat = nil
        local fc = nil
        if sg1:GetCount() > 0 or (sg2 ~= nil and sg2:GetCount() > 0) then
            local sg = sg1:Clone()
            if sg2 then
                sg:Merge(sg2)
            end
            Duel.Hint(HINT_SELECTMSG, tp, HINTMSG_SPSUMMON)
            local tg = sg:Select(tp, 1, 1, nil)
            fc = tg:GetFirst()
            if sg1:IsContains(fc) and (sg2 == nil or not sg2:IsContains(fc) or not Duel.SelectYesNo(tp, ce:GetDescription())) then
                mat = Duel.SelectFusionMaterial(tp, fc, mg1, gc, chkf)
                fc:SetMaterial(mat)
                local cg = mat:Filter(RushDuel.ConfirmCardFilter, nil)
                if cg:GetCount() > 0 then
                    Duel.ConfirmCards(1 - tp, cg)
                end
                mat_move(mat)
                Duel.BreakEffect()
                Duel.SpecialSummon(fc, SUMMON_TYPE_FUSION, tp, tp, false, false, POS_FACEUP)
            else
                mat = Duel.SelectFusionMaterial(tp, fc, mg3, gc, chkf)
                local fop = ce:GetOperation()
                fop(ce, e, tp, fc, mat)
            end
            fc:CompleteProcedure()
        end
        if action ~= nil then
            action(e, tp, eg, ep, ev, re, r, rp, mat, fc)
        end
        aux.FGoalCheckAdditional = nil
    end
end

-- 素材去向: 墓地
function RushDuel.FusionToGrave(mat)
    Duel.SendtoGrave(mat, REASON_EFFECT + REASON_MATERIAL + REASON_FUSION)
end
-- 素材去向: 卡组
function RushDuel.FusionToDeck(mat)
    Duel.SendtoDeck(mat, nil, SEQ_DECKSHUFFLE, REASON_EFFECT + REASON_MATERIAL + REASON_FUSION)
end

-- 可以进行融合术召唤
function RushDuel.CanFusionSummon(desc, matfilter, spfilter, exfilter, s_range, o_range, mat_check, mat_move, e, tp, break_effect, including_self)
    local chkf = tp
    local mg1 = Duel.GetFusionMaterial(tp):Filter(RushDuel.FusionMaterialFilter, nil, matfilter, e)
    if s_range ~= 0 or o_range ~= 0 then
        local mg2 = Duel.GetMatchingGroup(aux.NecroValleyFilter(exfilter), tp, s_range, o_range, nil, e)
        mg1:Merge(mg2)
    end
    aux.FGoalCheckAdditional = mat_check
    local gc = nil
    if including_self then
        gc = e:GetHandler()
    end
    local sg1 = Duel.GetMatchingGroup(RushDuel.FusionSpecialSummonFilter, tp, LOCATION_EXTRA, 0, nil, e, tp, mg1, nil, gc, chkf, spfilter)
    local mg3 = nil
    local sg2 = nil
    local ce = Duel.GetChainMaterial(tp)
    if ce ~= nil then
        local fgroup = ce:GetTarget()
        mg3 = fgroup(ce, e, tp)
        local mf = ce:GetValue()
        sg2 = Duel.GetMatchingGroup(RushDuel.FusionSpecialSummonFilter, tp, LOCATION_EXTRA, 0, nil, e, tp, mg3, mf, gc, chkf, spfilter)
    end
    local mat = nil
    local fc = nil
    if (sg1:GetCount() > 0 or (sg2 ~= nil and sg2:GetCount() > 0)) and Duel.SelectYesNo(tp, desc) then
        if break_effect then
            Duel.BreakEffect()
        end
        local sg = sg1:Clone()
        if sg2 then
            sg:Merge(sg2)
        end
        Duel.Hint(HINT_SELECTMSG, tp, HINTMSG_SPSUMMON)
        local tg = sg:Select(tp, 1, 1, nil)
        fc = tg:GetFirst()
        if sg1:IsContains(fc) and (sg2 == nil or not sg2:IsContains(fc) or not Duel.SelectYesNo(tp, ce:GetDescription())) then
            mat = Duel.SelectFusionMaterial(tp, fc, mg1, gc, chkf)
            fc:SetMaterial(mat)
            local cg = mat:Filter(RushDuel.ConfirmCardFilter, nil)
            if cg:GetCount() > 0 then
                Duel.ConfirmCards(1 - tp, cg)
            end
            mat_move(mat)
            Duel.BreakEffect()
            Duel.SpecialSummon(fc, SUMMON_TYPE_FUSION, tp, tp, false, false, POS_FACEUP)
        else
            mat = Duel.SelectFusionMaterial(tp, fc, mg3, gc, chkf)
            local fop = ce:GetOperation()
            fop(ce, e, tp, fc, mat)
        end
        fc:CompleteProcedure()
    end
    aux.FGoalCheckAdditional = nil
    return fc
end
