-- Rush Duel 融合
RushDuel = RushDuel or {}

-- 内部方法: 生成融合素材的函数
function RushDuel._private_make_fusion_material(card, sub, insf, mat, ...)
    local fun = {}
    local unspecified = false
    for _, val in ipairs {...} do
        local val_type = type(val)
        if val_type == "number" then
            table.insert(fun, RushDuel._private_make_fusion_material_code(val))
            mat[val] = true
        elseif val_type == "function" then
            table.insert(fun, RushDuel._private_make_fusion_material_func(val))
            unspecified = true
        elseif val_type == "table" then
            local funs = RushDuel._private_make_fusion_material(card, sub, insf, mat, table.unpack(val))
            table.insert(fun, RushDuel._private_make_fusion_material_mix(funs))
        end
    end
    return fun, unspecified
end

-- 内部方法: 融合素材 - 卡名
function RushDuel._private_make_fusion_material_code(code)
    return function(c, fc, sub, mg, sg)
        return c:IsFusionCode(code) or (sub and c:CheckFusionSubstitute(fc))
    end
end

-- 内部方法: 融合素材 - 条件
function RushDuel._private_make_fusion_material_func(func)
    return function(c, fc, sub, mg, sg)
        return func(c, fc, sub, mg, sg) and not c:IsHasEffect(6205579)
    end
end

-- 内部方法: 融合素材 - 混合
function RushDuel._private_make_fusion_material_mix(funs)
    return function(c, fc, sub, mg, sg)
        for _, fun in ipairs(funs) do
            if fun(c, fc, sub, mg, sg) then
                return true
            end
        end
        return false
    end
end

-- 内部方法: 添加融合素材数据
function RushDuel._private_set_fusion_material_data(card, mat, min, max, unspecified)
    local mt = getmetatable(card)
    -- 卡名记述的素材
    if mt.material == nil then
        mt.material = mat
    end
    -- 素材的数量
    if mt.material_count == nil then
        mt.material_count = {min, max}
    end
    -- 包含不指定卡名的融合素材
    if unspecified ~= nil then
        mt.unspecified_funsion = unspecified
    end
end

-- 内部方法: 创建融合效果
function RushDuel._private_create_fusion_effect(card, insf, sub, checker, min, max, ...)
    local e = Effect.CreateEffect(card)
    e:SetType(EFFECT_TYPE_SINGLE)
    e:SetCode(EFFECT_FUSION_MATERIAL)
    e:SetProperty(EFFECT_FLAG_CANNOT_DISABLE + EFFECT_FLAG_UNCOPYABLE)
    e:SetCondition(RushDuel.FusionProcedureCondition(insf, sub, checker, min, max, ...))
    e:SetOperation(RushDuel.FusionProcedureOperation(insf, sub, checker, min, max, ...))
    card:RegisterEffect(e)
    return e
end

-- 添加融合手续: 指定卡名/条件, 固定数量
function RushDuel.AddFusionProcedure(card, sub, insf, ...)
    if card:IsStatus(STATUS_COPYING_EFFECT) then
        return
    end
    local vals = {...}
    -- 简易融合
    if type(insf) ~= "boolean" then
        table.insert(vals, 1, insf)
        insf = true
    end
    -- 融合素材替代
    if type(sub) ~= "boolean" then
        table.insert(vals, 1, sub)
        sub = true
    end
    -- 有卡名记述的融合素材
    local mat = {}
    local funs, unspecified = RushDuel._private_make_fusion_material(card, sub, insf, mat, table.unpack(vals))
    RushDuel._private_set_fusion_material_data(card, mat, #funs, #funs, unspecified)
    local checker = RushDuel.FusionProcedureChecker(RushDuel.FusionProcedureCheckStart, nil)
    return RushDuel._private_create_fusion_effect(card, insf, sub, checker, #funs, #funs, table.unpack(funs))
end
-- 融合手续 - 检测: 指定卡名/条件, 固定数量
function RushDuel.FusionProcedureCheckStart(mg, fc, sub, ...)
    local sg = Group.CreateGroup()
    return mg:IsExists(RushDuel.FusionProcedureCheckStep, 1, nil, mg, sg, fc, sub, ...)
end
function RushDuel.FusionProcedureCheckStep(c, mg, sg, fc, sub, fun1, fun2, ...)
    if fun2 then
        sg:AddCard(c)
        local res = false
        if fun1(c, fc, false, mg, sg) then
            res = mg:IsExists(RushDuel.FusionProcedureCheckStep, 1, sg, mg, sg, fc, sub, fun2, ...)
        elseif sub and fun1(c, fc, true, mg, sg) then
            res = mg:IsExists(RushDuel.FusionProcedureCheckStep, 1, sg, mg, sg, fc, false, fun2, ...)
        end
        sg:RemoveCard(c)
        return res
    else
        return fun1(c, fc, sub, mg, sg)
    end
end

-- 添加融合手续: 指定条件, 不固定数量
function RushDuel.AddFusionProcedureSP(card, matfilter, matcheck, min, max, sub, insf)
    if card:IsStatus(STATUS_COPYING_EFFECT) then
        return
    end
    local insf = (insf ~= false)
    local sub = (sub ~= false)
    local checker = RushDuel.FusionProcedureChecker(RushDuel.FusionProcedureCheckSP, matcheck)
    return RushDuel._private_create_fusion_effect(card, insf, sub, checker, min, max, matfilter)
end
-- 融合手续 - 检测: 指定条件, 不固定数量
function RushDuel.FusionProcedureCheckSP(mg, fc, sub, matfilter)
    local ct = mg:FilterCount(matfilter, nil, fc, false)
    if not sub then
        return ct == mg:GetCount()
    else
        return ct >= mg:GetCount() - 1
    end
end

-- 手动添加融合素材列表
function RushDuel.SetFusionMaterial(card, codes, min, max)
    if card:IsStatus(STATUS_COPYING_EFFECT) then
        return
    end
    local mat = {}
    for _, code in ipairs(codes) do
        mat[code] = true
    end
    RushDuel._private_set_fusion_material_data(card, mat, min, max)
end

-- 融合手续 - 检测
function RushDuel.FusionProcedureChecker(condition, checker)
    return function(sg, tp, fc, sub, chkfnf, ...)
        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 condition(sg, fc, sub, ...)
            and (chkf == PLAYER_NONE or Duel.GetLocationCountFromEx(tp, tp, sg, fc) > 0)
            and (not checker or checker(sg, tp, fc, chkf))
            and (not Auxiliary.FCheckAdditional or Auxiliary.FCheckAdditional(tp, sg, fc))
            and (not Auxiliary.FGoalCheckAdditional or Auxiliary.FGoalCheckAdditional(tp, sg, fc))
    end
end
-- 融合手续 - 条件
function RushDuel.FusionProcedureCondition(insf, sub, checker, min, max, ...)
    local funs = {...}
    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, table.unpack(funs))
        if gc then
            if not mg:IsContains(gc) then
                return false
            end
            Duel.SetSelectedCard(gc)
        end
        return mg:CheckSubGroup(checker, min, max, tp, c, sub, chkfnf, table.unpack(funs))
    end
end
-- 融合手续 - 操作
function RushDuel.FusionProcedureOperation(insf, sub, checker, min, max, ...)
    local funs = {...}
    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, table.unpack(funs))
        if gc then
            Duel.SetSelectedCard(gc)
        end
        Duel.Hint(HINT_SELECTMSG, tp, HINTMSG_FMATERIAL)
        local sg = mg:SelectSubGroup(tp, checker, true, min, max, tp, c, sub, chkfnf, table.unpack(funs))
        if sg == nil then
            sg = Group.CreateGroup()
        end
        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.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.GetFusionSummonData(e, tp, matfilter, spfilter, exfilter, s_range, o_range, including_self, except, effect)
    local chkf = tp
    local mg1 = Duel.GetFusionMaterial(tp):Filter(RushDuel.FusionMaterialFilter, except, matfilter, effect)
    if s_range ~= 0 or o_range ~= 0 then
        local ext = Duel.GetMatchingGroup(aux.NecroValleyFilter(exfilter), tp, s_range, o_range, except, effect)
        mg1:Merge(ext)
    end
    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 mg2 = nil
    local sg2 = nil
    local ce = Duel.GetChainMaterial(tp)
    if ce ~= nil then
        local fgroup = ce:GetTarget()
        mg2 = fgroup(ce, e, tp)
        local mf = ce:GetValue()
        sg2 = Duel.GetMatchingGroup(RushDuel.FusionSpecialSummonFilter, tp, LOCATION_EXTRA, 0, nil, e, tp, mg2, mf, gc, chkf, spfilter)
    end
    return ce, mg1, mg2, sg1, sg2, chkf, gc
end
-- 融合效果 - 进行融合召唤
function RushDuel.ExecuteFusionSummon(e, tp, ce, mg1, mg2, sg1, sg2, gc, chkf, mat_move)
    local sg = sg1:Clone()
    if sg2 then
        sg:Merge(sg2)
    end
    ::cancel::
    Duel.Hint(HINT_SELECTMSG, tp, HINTMSG_SPSUMMON)
    local fc = sg:Select(tp, 1, 1, nil):GetFirst()
    local mat = nil
    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)
        if #mat < 2 then
            goto cancel
        end
        local cg = mat:Filter(RushDuel.ConfirmCardFilter, nil)
        if #cg > 0 then
            Duel.ConfirmCards(1 - tp, cg)
        end
        fc:SetMaterial(mat)
        mat_move(mat)
        Duel.BreakEffect()
        Duel.SpecialSummon(fc, SUMMON_TYPE_FUSION, tp, tp, false, false, POS_FACEUP)
    else
        mat = Duel.SelectFusionMaterial(tp, fc, mg2, gc, chkf)
        if #mat < 2 then
            goto cancel
        end
        local fop = ce:GetOperation()
        fop(ce, e, tp, fc, mat)
    end
    fc:CompleteProcedure()
    return fc, mat
end
-- 判断条件: 是否可以进行融合召唤
function RushDuel.IsCanFusionSummon(e, tp, matfilter, spfilter, exfilter, s_range, o_range, mat_check, including_self, except)
    Auxiliary.FGoalCheckAdditional = mat_check
    local ce, mg1, mg2, sg1, sg2 = RushDuel.GetFusionSummonData(e, tp, matfilter, spfilter, exfilter, s_range, o_range, including_self, except)
    local res = #sg1 > 0
    if not res and ce ~= nil then
        res = #sg2 > 0
    end
    Auxiliary.FGoalCheckAdditional = nil
    return res
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)
        Auxiliary.FGoalCheckAdditional = mat_check
        local ce, mg1, mg2, sg1, sg2, chkf, gc = RushDuel.GetFusionSummonData(e, tp, matfilter, spfilter, exfilter, s_range, o_range, including_self, nil, e)
        if sg1:GetCount() > 0 or (sg2 ~= nil and sg2:GetCount() > 0) then
            local fc, mat = RushDuel.ExecuteFusionSummon(e, tp, ce, mg1, mg2, sg1, sg2, gc, chkf, mat_move)
            if action ~= nil then
                action(e, tp, eg, ep, ev, re, r, rp, mat, fc)
            end
        end
        Auxiliary.FGoalCheckAdditional = nil
    end
end

-- 可以进行融合术召唤
function RushDuel.CanFusionSummon(desc, matfilter, spfilter, exfilter, s_range, o_range, mat_check, mat_move, e, tp, break_effect, including_self)
    Auxiliary.FGoalCheckAdditional = mat_check
    local ce, mg1, mg2, sg1, sg2, chkf, gc = RushDuel.GetFusionSummonData(e, tp, matfilter, spfilter, exfilter, s_range, o_range, including_self, nil, e)
    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
        fc = RushDuel.ExecuteFusionSummon(e, tp, ce, mg1, mg2, sg1, sg2, gc, chkf, mat_move)
    end
    aux.FGoalCheckAdditional = nil
    return fc
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
