跳转到内容

模块:Db:修订间差异

来自DJMAX中文资料库
无编辑摘要
无编辑摘要
 
(未显示同一用户的52个中间版本)
第1行: 第1行:
local p = {}
local p = {}


-- 1. 国际化和常量
-- [[------------------------------------------------------------------]]
-- [[ 1. 国际化和常量 ]]
-- [[------------------------------------------------------------------]]
local i18n = {
local i18n = {
['true'] = '是',      -- 布尔值:真
['true'] = '是',       
['false'] = '否',    -- 布尔值:假
['false'] = '否',     
['none'] = '无',
['none'] = '无',
['category_missing_value'] = '缺失%s',
['category_missing_value'] = '缺失%s',
['error_no_type'] = '<span class="error">Db:调用时必须提供type参数。</span>',
['error_no_type'] = '<span class="error">Db:调用时必须提供type参数。</span>',
    ['error_find_keys_args'] = '<span class="error">Db:findKeysByValue必须提供field和value参数。</span>',
    ['error_generate_table_args'] = '<span class="error">Db:generateListTable调用缺少field或columns参数。</span>',
}
}


-- 2. 辅助函数
-- [[------------------------------------------------------------------]]
-- [[ 2. 表格标题映射 ]]
-- [[------------------------------------------------------------------]]
local displayNames = {
    ['title'] = '歌曲标题', ['composer'] = '艺术家', ['bpm'] = 'BPM', ['singer'] = '歌手', ['genre'] = '流派',
    ['id'] = '歌曲ID', ['key'] = 'KEY音', ['bga'] = 'BGA作者', ['duration'] = '时长',
    ['pack'] = '曲包',
    ['4b_nm'] = '4B NM', ['4b_nm_notes'] = '4B NM', ['4b_hd'] = '4B HD', ['4b_hd_notes'] = '4B HD',
    ['4b_mx'] = '4B MX', ['4b_mx_notes'] = '4B MX', ['4b_sc'] = '4B SC', ['4b_sc_notes'] = '4B SC',
    ['5b_nm'] = '5B NM', ['5b_nm_notes'] = '5B NM', ['5b_hd'] = '5B HD', ['5b_hd_notes'] = '5B HD',
    ['5b_mx'] = '5B MX', ['5b_mx_notes'] = '5B MX', ['5b_sc'] = '5B SC', ['5b_sc_notes'] = '5B SC',
    ['6b_nm'] = '6B NM', ['6b_nm_notes'] = '6B NM', ['6b_hd'] = '6B HD', ['6b_hd_notes'] = '6B HD',
    ['6b_mx'] = '6B MX', ['6b_mx_notes'] = '6B MX', ['6b_sc'] = '6B SC', ['6b_sc_notes'] = '6B SC',
    ['8b_nm'] = '8B NM', ['8b_nm_notes'] = '8B NM', ['8b_hd'] = '8B HD', ['8b_hd_notes'] = '8B HD',
    ['8b_mx'] = '8B MX', ['8b_mx_notes'] = '8B MX', ['8b_sc'] = '8B SC', ['8b_sc_notes'] = '8B SC',
}


-- 导入歌曲数据,从 Module:db_song 中获取数据表
-- **Pack 缩写映射表(用户输入缩写 -> 完整包名,键使用大写以便不区分大小写查询)**
local packAbbreviations = {
    ['VL'] = 'V LIBERTY',
    ['VL2'] = 'V LIBERTY 2',
    ['VL3'] = 'V LIBERTY 3',
    ['VE'] = 'V EXTENSION',
    ['VE2'] = 'V EXTENSION 2',
    ['VE3'] = 'V EXTENSION 3',
    ['VE4'] = 'V EXTENSION 4',
    ['VE5'] = 'V EXTENSION 5',
    ['P1'] = 'PORTABLE 1',
    ['P2'] = 'PORTABLE 2',
    ['P3'] = 'PORTABLE 3',
    ['CE'] = 'CLAZZIQUAI',
    ['BS'] = 'BLACK SQUARE',
    ['TR'] = 'TRILOGY',
    ['T1'] = 'TECHNIKA',
    ['T2'] = 'TECHNIKA 2',
    ['T3'] = 'TECHNIKA 3',
    ['R'] = 'RESPECT',
    ['RV'] = 'RESPECT V',
    ['ARC'] = 'ARCAEA',
    ['BA'] = 'BLUE ARCHIVE',
    ['CY'] = 'CYTUS',
    ['CHU'] = 'CHUNITHM',
    ['DE'] = 'DEEMO',
    ['EZ'] = 'EZ2ON',
    ['GC'] = 'GROOVE COASTER',
    ['MD'] = 'MUSE DASH',
    ['ESTI'] = 'ESTIMATE',
    ['FC'] = 'FALCOM',
    ['GF'] = 'GIRL\'S FRONTLINE',
    ['GG'] = 'GUILTY GEAR',
    ['MAP'] = 'MAPLESTORY',
    ['NXN'] = 'NEXON',
    ['TK'] = 'TEKKEN',
    ['TB1'] = 'PLI: TRIBUTE VOL.1',
    ['645141'] = 'PLI: 64514 VOL.1',
}
 
-- [[------------------------------------------------------------------]]
-- [[ 3. 本地辅助函数定义 ]]
-- [[------------------------------------------------------------------]]
local function GetValuesTable()
local function GetValuesTable()
-- 从 Module:db_song 中获取数据。
     return mw.loadData('Module:db_song')
     return mw.loadData('Module:db_song')
end
end  


-- 从值列表按照键值获得值,找不到则返回nil。
local function ValueFromValuesByKey(values, key)
local function ValueFromValuesByKey(values, key)
if values and key then
if values and key then
第24行: 第83行:
end
end
return nil
return nil
end
-- **Pack 缩写解析**
local function resolvePackValue(input)
    if type(input) ~= 'string' then
        return input
    end
    -- 尝试查找缩写(不区分大小写,因为 packAbbreviations 的键是大写)
    local upperInput = string.upper(input)
    if packAbbreviations[upperInput] then
        return packAbbreviations[upperInput]
    end
    -- 找不到缩写,返回原始输入
    return input
end
local function formatSingleValue(value, fieldType)
    if value == nil then
        return i18n['none']
    elseif type(value) == 'boolean' then
        return value and i18n['true'] or i18n['false']
    elseif type(p.valueMappingMethod[fieldType]) == 'function' then
        return p.valueMappingMethod[fieldType](value)
    else
        return tostring(value)
    end
end
local function formatBPMValue(valueString)
    if string.find(valueString, '-') then
        return '<span style="color:red;">' .. valueString .. '</span>'
    end
    return valueString
end
-- **格式化 key 值 (仅在表格中使用)**
local function formatKeyValue(value)
    if type(value) == 'boolean' then
        local color = value and 'green' or 'red'
        local text = value and i18n['true'] or i18n['false']
        return '<span style="color:' .. color .. ';">' .. text .. '</span>'
    end
    return tostring(value)
end
local function getDifficultyDetails(field)
    local checkField = field and string.lower(mw.text.trim(field)) or ''
   
    local difficultyFields = {
        ['4b_nm'] = true, ['4b_hd'] = true, ['4b_mx'] = true, ['4b_sc'] = true,
        ['5b_nm'] = true, ['5b_hd'] = true, ['5b_mx'] = true, ['5b_sc'] = true,
        ['6b_nm'] = true, ['6b_hd'] = true, ['6b_mx'] = true, ['6b_sc'] = true,
        ['8b_nm'] = true, ['8b_hd'] = true, ['8b_mx'] = true, ['8b_sc'] = true,
    }
    if difficultyFields[checkField] == true then
        local mode = checkField:sub(-2)
        return checkField:match("^(%d+)b"), mode
    end
    return nil, nil
end
-- **核心:难度样式应用 (仅用于表格,并处理可能存在的 'sc' 前缀)**
local function formatDifficultyStyles(valueString, mode)
   
    if valueString == '-' or valueString == i18n['none'] then
        return valueString
    end
   
    local displayString = valueString
    local numValue = tonumber(valueString)
   
    if mode == 'sc' and numValue == nil then
        local stripped = valueString:match("^[Ss][Cc](%d+)$")
        if stripped then
            numValue = tonumber(stripped)
            displayString = stripped     
        end
    end
   
    if numValue == nil then
        return valueString
    end
    local style = ''
   
    if mode == 'sc' then
        local blueGlow = ' text-shadow: 0 0 5px #3D66FF, 0 0 8px #3D66FF;'
        style = 'font-weight: bold;'
       
        if numValue >= 1 and numValue <= 5 then
            style = style .. ' color: #E00075;'
        elseif numValue >= 6 and numValue <= 10 then
            style = style .. ' color: #C604E3;'
        elseif numValue >= 11 and numValue <= 12 then
            style = style .. ' color: #3D66FF;'
        elseif numValue == 13 then
            style = style .. ' color: orange;' .. blueGlow
        elseif numValue == 14 then
            style = style .. ' color: red;' .. blueGlow
        elseif numValue == 15 then
            style = style .. ' color: purple;' .. blueGlow
        end
    elseif (mode == 'nm' or mode == 'hd' or mode == 'mx') then
        if numValue == 14 then
            style = 'font-weight: bold; color: orange;'
        elseif numValue == 15 then
            style = 'font-weight: bold; color: red;'
        end
    end
   
    if style ~= '' then
        return '<span style="' .. style .. '">' .. displayString .. '</span>'
    end
   
    return displayString
end
end


-- 3. 值格式化方法 (valueMappingMethod)
-- **NOTES 字段加粗/变色逻辑**
local valueMappingMethod = {
local function formatNotesValue(valueString, context)
     -- 格式化函数:将时长(秒)格式化为 'M:SS' 格式
    local numValue = tonumber(valueString)
   
    if numValue and numValue >= 2000 then
        local content = valueString
       
        if context == 'table' then
            -- --- 表格环境 (高优先级颜色 + 加粗) ---
            if numValue >= 3000 then
                -- Table, >= 3000: 紫色加粗
                return '<span style="font-weight: bold; color: purple;">' .. content .. '</span>'
            elseif numValue >= 2000 then
                -- Table, 2000 <= X < 3000: 红色加粗
                return '<span style="font-weight: bold; color: red;">' .. content .. '</span>'
            end
        else
            -- --- 非表格环境 (仅加粗) ---
            return '<b>' .. content .. '</b>'
        end
    end
   
    -- 如果小于 2000,返回原始字符串
    return valueString
end
 
 
-- **SC 前缀应用 (仅用于 p.value)**
local function applySCPrefix(valueString, fieldType)
    local isSC = string.match(fieldType, '_sc$')
   
     if isSC and valueString ~= i18n['none'] and valueString ~= '-' then
        if not valueString:lower():find('sc') then
            return 'sc' .. valueString
        end
    end
   
    return valueString
end
 
 
-- 4. 值格式化方法 (valueMappingMethod)
p.valueMappingMethod = {
     ['duration'] = (function (value)
     ['duration'] = (function (value)
         if type(value) == 'number' then
         if type(value) == 'number' then
第37行: 第252行:
         return value
         return value
     end),
     end),
     -- 格式化函数:将艺术家名称自动链接
     -- key 字段在非表格环境中的默认处理 (例如 p.value 调用)
     ['artist'] = (function (value)
     ['key'] = (function(value)
         if type(value) == 'string' and value ~= '' then
         if type(value) == 'boolean' then
             return '[[' .. value .. ']]'
             return value and i18n['true'] or i18n['false']
         end
         end
         return value
         return tostring(value)
     end),
     end)
}
}


-- 4. 外部调用函数 (p.value)
-- [[------------------------------------------------------------------]]
-- [[ 5. 外部调用函数 (p.*) ]]
-- [[------------------------------------------------------------------]]
 
function p.value(f)
function p.value(f)
local args = f
    local args = f
local frame = mw.getCurrentFrame()
local frame = mw.getCurrentFrame()
if f == frame then
if f == frame then
第54行: 第272行:
end
end
      
      
local argTargetName = mw.text.trim(args[1] or '') -- 歌曲ID (键名)
local argTargetName = mw.text.trim(args[1] or '')  
local argType = args.type                       -- 字段名 (type)
local argTypeString = args.type                
local argNocat = args.nocat                    -- 是否禁止分类追踪
local argNocat = args.nocat                     
      
      
     if not argType then
     if not argTypeString then
         return i18n['error_no_type']
         return i18n['error_no_type']
     end
     end
      
      
    -- 1. 加载歌曲数据
     local values = GetValuesTable()  
     local values = GetValuesTable()  
   
    -- 2. 获取目标歌曲条目
     local songEntry = ValueFromValuesByKey(values, argTargetName)
     local songEntry = ValueFromValuesByKey(values, argTargetName)


     if not songEntry then
     if not songEntry then
         -- [处理歌曲ID缺失...]
         if not argNocat then
        -- ... (代码省略,与之前版本相同)
            local title = mw.title.getCurrentTitle()
            if title.namespace == 0 and not title.isSubpage then
                return '?' .. '[[Category:' .. string.format(i18n['category_missing_value'], '歌曲ID ' .. argTargetName) .. ']]'
            end
        end
         return i18n['none']
         return i18n['none']
     end
     end
      
      
     -- 3. 从歌曲条目中获取特定字段的值
     local fieldNames = mw.text.split(argTypeString, ',')
     local value = ValueFromValuesByKey(songEntry, argType)
     local resultTable = {}
   
 
    if value == nil then
    for _, argType in ipairs(fieldNames) do
         -- [处理特定字段的值缺失...]
        argType = mw.text.trim(argType)
         -- ... (代码省略,与之前版本相同)
       
         return i18n['none']
        local value = ValueFromValuesByKey(songEntry, argType)
    end
       
   
         -- 使用 formatSingleValue 处理 BPM/Duration/Key(boolean->text) 等映射
    -- 4. 应用格式化
         local formattedValue = formatSingleValue(value, argType)  
   
          
    -- **新增:优先级最高的布尔值转换**
        formattedValue = applySCPrefix(formattedValue, argType)
    if type(value) == 'boolean' then
       
        if value then
        -- 非表格环境下应用 NOTES 格式化(仅加粗)
             return i18n['true']
        if string.match(argType, '_notes$') then
        else
             formattedValue = formatNotesValue(formattedValue, 'text')
            return i18n['false']
         end
         end
       
        table.insert(resultTable, formattedValue)
     end
     end
      
      
    -- 接下来应用字段特定的格式化
return table.concat(resultTable, ' / ')
if type(valueMappingMethod[argType]) == 'function' then
return valueMappingMethod[argType](value)
else
-- 转换为字符串输出
return tostring(value)
end
end
end
-- 5. 反向查找
 
function p.findKeysByValue(f)
 
     local args = f
function p.generateListTable(f)
    local frame = mw.getCurrentFrame()
     local args = require('Module:ProcessArgs').merge(true)  
    if f == frame then
        args = require('Module:ProcessArgs').merge(true)
    end
      
      
     local targetField = mw.text.trim(args.field or '')
     local conditionField = mw.text.trim(args.field or '')  
    -- 如果目标值是字符串,清除其两侧空格,否则保持原类型 (如数字)
     local conditionValue = args.value                      
     local targetValue = args.value  
     local displayFieldsString = mw.text.trim(args.columns or '')  
     if type(targetValue) == 'string' then
        targetValue = mw.text.trim(targetValue)
    end
      
      
     -- 检查必要参数
     local isPackFilter = (conditionField == 'pack') -- 定义 pack 筛选标记
    if targetField == '' or targetValue == nil then
        return i18n['error_find_keys_args']
    end


     -- 获取所有歌曲数据
     -- ** 预处理 conditionValue **
     local songData = GetValuesTable()
     if type(conditionValue) == 'string' then
   
        local trimmedValue = mw.text.trim(conditionValue)
    local matchingKeys = {}
       
   
         local lowerValue = string.lower(trimmedValue)
    -- 遍历数据,查找匹配的键
    for songId, songEntry in pairs(songData) do
         local currentValue = ValueFromValuesByKey(songEntry, targetField)
          
          
         -- 直接比较当前值和目标值
         if lowerValue == 'true' or trimmedValue == '是' then
         if currentValue == targetValue then
            conditionValue = true
             table.insert(matchingKeys, songId)
         elseif lowerValue == 'false' or trimmedValue == '否' then
             conditionValue = false
        else
            local num = tonumber(trimmedValue)
            -- 核心修复:如果是 Pack 筛选,即使能转为数字也不转,保持字符串以便后续缩写解析和不区分大小写比较
            if num ~= nil and not isPackFilter then
                conditionValue = num
            else
                conditionValue = trimmedValue
            end
         end
         end
     end
     end
      
      
    -- 对找到的键进行排序 (可选,但有助于规范输出)
     if conditionField == '' or displayFieldsString == '' then
    table.sort(matchingKeys)
   
    -- 返回以逗号和空格分隔的歌曲ID列表
    return table.concat(matchingKeys, ', ')
end
--6. 表格生成函数
function p.generateListTable(f)
    local args = require('Module:ProcessArgs').merge(true)
   
    local conditionField = mw.text.trim(args.field or '')  -- 用于筛选的字段
    local conditionValue = args.value                      -- 用于筛选的值
    if type(conditionValue) == 'string' then
        conditionValue = mw.text.trim(conditionValue)
    end
    local displayFieldsString = mw.text.trim(args.columns or '') -- 要显示的字段列表
   
     if conditionField == '' or conditionValue == nil or displayFieldsString == '' then
         return i18n['error_generate_table_args']
         return i18n['error_generate_table_args']
     end
     end
第162行: 第356行:
     local matchingKeys = {}
     local matchingKeys = {}
      
      
     -- 查找匹配的键
    local shouldFilter = conditionValue ~= nil
   
     -- 如果是 pack 筛选,提前解析 conditionValue 以支持缩写
    local resolvedConditionValue = nil
    if isPackFilter and type(conditionValue) == 'string' then
        resolvedConditionValue = resolvePackValue(conditionValue)
    end
 
 
     for songId, songEntry in pairs(songData) do
     for songId, songEntry in pairs(songData) do
         local currentValue = ValueFromValuesByKey(songEntry, conditionField)
         if not shouldFilter then
        local checkValue = currentValue
            table.insert(matchingKeys, songId)
       
        else
        -- 针对布尔值的特殊处理,允许用户输入 'true'/'false'/'是'/'否' 来匹配
            local currentValue = ValueFromValuesByKey(songEntry, conditionField)
        if type(currentValue) == 'boolean' then
            local match = false
            local lowerTarget = type(conditionValue) == 'string' and string.lower(conditionValue) or conditionValue
           
            if lowerTarget == 'true' or lowerTarget == '是' then
            -- 1. 数字和布尔值的精确匹配
                 checkValue = true
            if currentValue == conditionValue then
            elseif lowerTarget == 'false' or lowerTarget == '否' then
                match = true
                 checkValue = false
           
            -- 2. 字符串匹配
            elseif type(currentValue) == 'string' and type(conditionValue) == 'string' then
               
                -- 2a. Pack 字段: 缩写解析后的精确匹配 (Case-insensitive Exact Match)
                if isPackFilter then
                    local targetValue = resolvedConditionValue -- 这会是完整的包名或原始输入
                   
                    if string.upper(currentValue) == string.upper(targetValue) then
                        match = true
                    end
               
                -- 2b. 其他字符串字段: 模糊匹配 (Case-insensitive Fuzzy Match)
                 else
                    local searchString = conditionValue
                   
                    -- 模糊匹配:只要 currentValue 包含 searchString(不区分大小写)
                    if string.find(string.upper(currentValue), string.upper(searchString), 1, true) then
                        match = true
                    end
                 end
             end
             end
        end


        if checkValue == conditionValue then
            if match then
            table.insert(matchingKeys, songId)
                table.insert(matchingKeys, songId)
            end
         end
         end
     end
     end
第190行: 第412行:
     local output = {}
     local output = {}
      
      
     -- 构造表格头部
     -- 检查是否需要禁用宽度设置
     table.insert(output, '{| class="wikitable sortable"')
    local disableWidths = (#displayFields <= 8)
   
    -- 表格样式
     table.insert(output, '{| class="wikitable sortable" style="width: 100%; margin: 0 auto; text-align: center;"')
   
    -- 构造表格头部:添加宽度样式控制
     table.insert(output, '|-')
     table.insert(output, '|-')
   
     for _, field in ipairs(displayFields) do
     for _, field in ipairs(displayFields) do
         field = mw.text.trim(field)
         field = mw.text.trim(field)
         table.insert(output, '! ' .. (field))
       
        local _, mode = getDifficultyDetails(field)
        local headerContent = displayNames[field] or field
        local headerStyle = ''
        local separator = ' ' -- 默认分隔符是空格
       
        if not disableWidths then -- 宽度要求失效检查
            -- BPM/Key/Duration 字段,固定宽度 6%
            if field == 'bpm' or field == 'key' or field == 'duration' then
                headerStyle = ' style="width: 6%;"'
                separator = ' | '
            -- Pack 字段,固定宽度 10%
            elseif field == 'pack' then
                headerStyle = ' style="width: 10%;"'
                separator = ' | '
            elseif mode then -- 谱面难度字段 (3.2% 固定宽度)
                headerStyle = ' style="width: 3.2%;"'
                separator = ' | '
            elseif string.match(field, '_notes$') then -- NOTES 字段 (4% 固定宽度)
                headerStyle = ' style="width: 4%;"'
                separator = ' | '
            end
        end
       
        -- MediaWiki 表格头单元格: ! style | Content
         table.insert(output, '!' .. headerStyle .. separator .. headerContent)
     end
     end
      
      
第204行: 第455行:
         table.insert(output, '|-')
         table.insert(output, '|-')
          
          
        -- 第一列: Song ID
        table.insert(output, '| ' .. songId)
       
        -- 后续列: 数据字段
         for _, field in ipairs(displayFields) do
         for _, field in ipairs(displayFields) do
             field = mw.text.trim(field)
             field = mw.text.trim(field)
           
             local rawValue = ValueFromValuesByKey(songEntry, field)
             local rawValue = ValueFromValuesByKey(songEntry, field)
           
            -- 获取格式化的值
             local formattedValue = formatSingleValue(rawValue, field)
             local formattedValue = formatSingleValue(rawValue, field)
             table.insert(output, '| ' .. formattedValue)
           
            -- 应用内容样式 (BPM/Key)
            if field == 'bpm' then
                formattedValue = formatBPMValue(formattedValue)
            elseif field == 'key' then
                formattedValue = formatKeyValue(rawValue) -- 对 key 字段应用颜色逻辑,使用原始值 (rawValue)
            end
 
            local _, mode = getDifficultyDetails(field)
            if mode then
                formattedValue = formatDifficultyStyles(formattedValue, mode)
            end
           
            -- *** NOTES 字段样式逻辑 (包含 _sc_notes 默认颜色) ***
            if string.match(field, '_notes$') then
                local numValue = tonumber(rawValue)
               
                -- 1. 检查是否为高优先级(>= 2000/3000)
                if numValue and numValue >= 2000 then
                    -- 高优先级:应用红/紫色的 formatNotesValue
                    formattedValue = formatNotesValue(formattedValue, 'table')
                -- 2. 检查是否为所有 _sc_notes 字段且为低优先级(< 2000)
                elseif string.match(field, '_sc_notes$') then
                    -- sc_notes 默认颜色:3d66ff (如果值不是高优先级)
                    formattedValue = '<span style="color: #3d66ff;">' .. formattedValue .. '</span>'
                end
            end
           
            local cellContent = formattedValue
           
            -- CRITICAL FIX: 如果内容是单个 '-', 增加空格以避免被解析为行分隔符 '|-'.
            if cellContent == '-' then
                cellContent = ' -'
            end
 
             table.insert(output, '|' .. cellContent)
         end
         end
     end
     end
      
      
    -- 闭合表格
     table.insert(output, '|}')
     table.insert(output, '|}')
      
      
     return table.concat(output, '\n')
     return table.concat(output, '\n')
end
end
return p
return p

2025年12月1日 (一) 01:00的最新版本

本模块为自动表核心功能程序,根据输入的信息自动筛选数据。

数据库储存在Module:db_song,通过Template:db可以调用数据库中的信息,也可以通过Template:songlist生成自动表格。


local p = {}

-- [[------------------------------------------------------------------]]
-- [[ 1. 国际化和常量 ]]
-- [[------------------------------------------------------------------]]
local i18n = {
	['true'] = '是',      
	['false'] = '否',     
	['none'] = '无',
	['category_missing_value'] = '缺失%s',
	['error_no_type'] = '<span class="error">Db:调用时必须提供type参数。</span>',
    ['error_find_keys_args'] = '<span class="error">Db:findKeysByValue必须提供field和value参数。</span>', 
    ['error_generate_table_args'] = '<span class="error">Db:generateListTable调用缺少field或columns参数。</span>', 
}

-- [[------------------------------------------------------------------]]
-- [[ 2. 表格标题映射 ]]
-- [[------------------------------------------------------------------]]
local displayNames = {
    ['title'] = '歌曲标题', ['composer'] = '艺术家', ['bpm'] = 'BPM', ['singer'] = '歌手', ['genre'] = '流派',
    ['id'] = '歌曲ID', ['key'] = 'KEY音', ['bga'] = 'BGA作者', ['duration'] = '时长',
    ['pack'] = '曲包', 
    ['4b_nm'] = '4B NM', ['4b_nm_notes'] = '4B NM', ['4b_hd'] = '4B HD', ['4b_hd_notes'] = '4B HD', 
    ['4b_mx'] = '4B MX', ['4b_mx_notes'] = '4B MX', ['4b_sc'] = '4B SC', ['4b_sc_notes'] = '4B SC',
    ['5b_nm'] = '5B NM', ['5b_nm_notes'] = '5B NM', ['5b_hd'] = '5B HD', ['5b_hd_notes'] = '5B HD',
    ['5b_mx'] = '5B MX', ['5b_mx_notes'] = '5B MX', ['5b_sc'] = '5B SC', ['5b_sc_notes'] = '5B SC',
    ['6b_nm'] = '6B NM', ['6b_nm_notes'] = '6B NM', ['6b_hd'] = '6B HD', ['6b_hd_notes'] = '6B HD',
    ['6b_mx'] = '6B MX', ['6b_mx_notes'] = '6B MX', ['6b_sc'] = '6B SC', ['6b_sc_notes'] = '6B SC',
    ['8b_nm'] = '8B NM', ['8b_nm_notes'] = '8B NM', ['8b_hd'] = '8B HD', ['8b_hd_notes'] = '8B HD',
    ['8b_mx'] = '8B MX', ['8b_mx_notes'] = '8B MX', ['8b_sc'] = '8B SC', ['8b_sc_notes'] = '8B SC',
}

-- **Pack 缩写映射表(用户输入缩写 -> 完整包名,键使用大写以便不区分大小写查询)**
local packAbbreviations = {
    ['VL'] = 'V LIBERTY',
    ['VL2'] = 'V LIBERTY 2',
    ['VL3'] = 'V LIBERTY 3',
    ['VE'] = 'V EXTENSION',
    ['VE2'] = 'V EXTENSION 2',
    ['VE3'] = 'V EXTENSION 3',
    ['VE4'] = 'V EXTENSION 4',
    ['VE5'] = 'V EXTENSION 5',
    ['P1'] = 'PORTABLE 1',
    ['P2'] = 'PORTABLE 2',
    ['P3'] = 'PORTABLE 3',
    ['CE'] = 'CLAZZIQUAI',
    ['BS'] = 'BLACK SQUARE',
    ['TR'] = 'TRILOGY',
    ['T1'] = 'TECHNIKA',
    ['T2'] = 'TECHNIKA 2',
    ['T3'] = 'TECHNIKA 3',
    ['R'] = 'RESPECT',
    ['RV'] = 'RESPECT V',
    ['ARC'] = 'ARCAEA',
    ['BA'] = 'BLUE ARCHIVE',
    ['CY'] = 'CYTUS',
    ['CHU'] = 'CHUNITHM',
    ['DE'] = 'DEEMO',
    ['EZ'] = 'EZ2ON',
    ['GC'] = 'GROOVE COASTER',
    ['MD'] = 'MUSE DASH',
    ['ESTI'] = 'ESTIMATE',
    ['FC'] = 'FALCOM',
    ['GF'] = 'GIRL\'S FRONTLINE',
    ['GG'] = 'GUILTY GEAR',
    ['MAP'] = 'MAPLESTORY',
    ['NXN'] = 'NEXON',
    ['TK'] = 'TEKKEN',
    ['TB1'] = 'PLI: TRIBUTE VOL.1',
    ['645141'] = 'PLI: 64514 VOL.1',
}

-- [[------------------------------------------------------------------]]
-- [[ 3. 本地辅助函数定义 ]]
-- [[------------------------------------------------------------------]]
local function GetValuesTable()
    return mw.loadData('Module:db_song')
end 

local function ValueFromValuesByKey(values, key)
	if values and key then
		return values[key]
	end
	return nil
end 

-- **Pack 缩写解析**
local function resolvePackValue(input)
    if type(input) ~= 'string' then
        return input
    end
    -- 尝试查找缩写(不区分大小写,因为 packAbbreviations 的键是大写)
    local upperInput = string.upper(input)
    if packAbbreviations[upperInput] then
        return packAbbreviations[upperInput]
    end
    -- 找不到缩写,返回原始输入
    return input
end

local function formatSingleValue(value, fieldType)
    if value == nil then
        return i18n['none']
    elseif type(value) == 'boolean' then
        return value and i18n['true'] or i18n['false']
    elseif type(p.valueMappingMethod[fieldType]) == 'function' then
        return p.valueMappingMethod[fieldType](value)
    else
        return tostring(value)
    end
end 

local function formatBPMValue(valueString)
    if string.find(valueString, '-') then
        return '<span style="color:red;">' .. valueString .. '</span>'
    end
    return valueString
end

-- **格式化 key 值 (仅在表格中使用)**
local function formatKeyValue(value)
    if type(value) == 'boolean' then
        local color = value and 'green' or 'red'
        local text = value and i18n['true'] or i18n['false']
        return '<span style="color:' .. color .. ';">' .. text .. '</span>'
    end
    return tostring(value)
end

local function getDifficultyDetails(field)
    local checkField = field and string.lower(mw.text.trim(field)) or ''
    
    local difficultyFields = {
        ['4b_nm'] = true, ['4b_hd'] = true, ['4b_mx'] = true, ['4b_sc'] = true,
        ['5b_nm'] = true, ['5b_hd'] = true, ['5b_mx'] = true, ['5b_sc'] = true,
        ['6b_nm'] = true, ['6b_hd'] = true, ['6b_mx'] = true, ['6b_sc'] = true,
        ['8b_nm'] = true, ['8b_hd'] = true, ['8b_mx'] = true, ['8b_sc'] = true,
    }

    if difficultyFields[checkField] == true then
        local mode = checkField:sub(-2)
        return checkField:match("^(%d+)b"), mode
    end
    return nil, nil
end

-- **核心:难度样式应用 (仅用于表格,并处理可能存在的 'sc' 前缀)**
local function formatDifficultyStyles(valueString, mode)
    
    if valueString == '-' or valueString == i18n['none'] then
        return valueString 
    end
    
    local displayString = valueString 
    local numValue = tonumber(valueString)
    
    if mode == 'sc' and numValue == nil then
        local stripped = valueString:match("^[Ss][Cc](%d+)$")
        if stripped then
            numValue = tonumber(stripped) 
            displayString = stripped       
        end
    end
    
    if numValue == nil then
        return valueString 
    end

    local style = ''
    
    if mode == 'sc' then
        local blueGlow = ' text-shadow: 0 0 5px #3D66FF, 0 0 8px #3D66FF;'
        style = 'font-weight: bold;'
        
        if numValue >= 1 and numValue <= 5 then
            style = style .. ' color: #E00075;'
        elseif numValue >= 6 and numValue <= 10 then
            style = style .. ' color: #C604E3;'
        elseif numValue >= 11 and numValue <= 12 then
            style = style .. ' color: #3D66FF;' 
        elseif numValue == 13 then
            style = style .. ' color: orange;' .. blueGlow
        elseif numValue == 14 then
            style = style .. ' color: red;' .. blueGlow
        elseif numValue == 15 then
            style = style .. ' color: purple;' .. blueGlow
        end
    elseif (mode == 'nm' or mode == 'hd' or mode == 'mx') then
        if numValue == 14 then
            style = 'font-weight: bold; color: orange;'
        elseif numValue == 15 then
            style = 'font-weight: bold; color: red;'
        end
    end
    
    if style ~= '' then
        return '<span style="' .. style .. '">' .. displayString .. '</span>'
    end
    
    return displayString 
end

-- **NOTES 字段加粗/变色逻辑**
local function formatNotesValue(valueString, context)
    local numValue = tonumber(valueString)
    
    if numValue and numValue >= 2000 then
        local content = valueString
        
        if context == 'table' then
            -- --- 表格环境 (高优先级颜色 + 加粗) ---
            if numValue >= 3000 then
                -- Table, >= 3000: 紫色加粗
                return '<span style="font-weight: bold; color: purple;">' .. content .. '</span>'
            elseif numValue >= 2000 then
                -- Table, 2000 <= X < 3000: 红色加粗
                return '<span style="font-weight: bold; color: red;">' .. content .. '</span>'
            end
        else
            -- --- 非表格环境 (仅加粗) ---
            return '<b>' .. content .. '</b>'
        end
    end
    
    -- 如果小于 2000,返回原始字符串
    return valueString
end


-- **SC 前缀应用 (仅用于 p.value)**
local function applySCPrefix(valueString, fieldType)
    local isSC = string.match(fieldType, '_sc$')
    
    if isSC and valueString ~= i18n['none'] and valueString ~= '-' then
        if not valueString:lower():find('sc') then
            return 'sc' .. valueString
        end
    end
    
    return valueString
end


-- 4. 值格式化方法 (valueMappingMethod)
p.valueMappingMethod = {
    ['duration'] = (function (value)
        if type(value) == 'number' then
            local minutes = math.floor(value / 60)
            local seconds = value % 60
            return string.format('%d:%02d', minutes, seconds)
        end
        return value
    end),
    -- key 字段在非表格环境中的默认处理 (例如 p.value 调用)
    ['key'] = (function(value)
        if type(value) == 'boolean' then
            return value and i18n['true'] or i18n['false']
        end
        return tostring(value)
    end)
}

-- [[------------------------------------------------------------------]]
-- [[ 5. 外部调用函数 (p.*) ]]
-- [[------------------------------------------------------------------]]

function p.value(f)
    local args = f
	local frame = mw.getCurrentFrame()
	if f == frame then
		args = require('Module:ProcessArgs').merge(true)
	end
    
	local argTargetName = mw.text.trim(args[1] or '') 
	local argTypeString = args.type                 
	local argNocat = args.nocat                     
    
    if not argTypeString then
        return i18n['error_no_type']
    end
    
    local values = GetValuesTable() 
    local songEntry = ValueFromValuesByKey(values, argTargetName)

    if not songEntry then
        if not argNocat then
            local title = mw.title.getCurrentTitle()
            if title.namespace == 0 and not title.isSubpage then
                return '?' .. '[[Category:' .. string.format(i18n['category_missing_value'], '歌曲ID ' .. argTargetName) .. ']]'
            end
        end
        return i18n['none']
    end
    
    local fieldNames = mw.text.split(argTypeString, ',')
    local resultTable = {}

    for _, argType in ipairs(fieldNames) do
        argType = mw.text.trim(argType)
        
        local value = ValueFromValuesByKey(songEntry, argType)
        
        -- 使用 formatSingleValue 处理 BPM/Duration/Key(boolean->text) 等映射
        local formattedValue = formatSingleValue(value, argType) 
        
        formattedValue = applySCPrefix(formattedValue, argType)
        
        -- 非表格环境下应用 NOTES 格式化(仅加粗)
        if string.match(argType, '_notes$') then
            formattedValue = formatNotesValue(formattedValue, 'text') 
        end
        
        table.insert(resultTable, formattedValue)
    end
    
	return table.concat(resultTable, ' / ')
end


function p.generateListTable(f)
    local args = require('Module:ProcessArgs').merge(true) 
    
    local conditionField = mw.text.trim(args.field or '')   
    local conditionValue = args.value                       
    local displayFieldsString = mw.text.trim(args.columns or '') 
    
    local isPackFilter = (conditionField == 'pack') -- 定义 pack 筛选标记

    -- ** 预处理 conditionValue **
    if type(conditionValue) == 'string' then
        local trimmedValue = mw.text.trim(conditionValue)
        
        local lowerValue = string.lower(trimmedValue)
        
        if lowerValue == 'true' or trimmedValue == '是' then
            conditionValue = true
        elseif lowerValue == 'false' or trimmedValue == '否' then
            conditionValue = false
        else
            local num = tonumber(trimmedValue)
            -- 核心修复:如果是 Pack 筛选,即使能转为数字也不转,保持字符串以便后续缩写解析和不区分大小写比较
            if num ~= nil and not isPackFilter then 
                conditionValue = num
            else
                conditionValue = trimmedValue
            end
        end
    end
    
    if conditionField == '' or displayFieldsString == '' then
        return i18n['error_generate_table_args']
    end

    local songData = GetValuesTable()
    local displayFields = mw.text.split(displayFieldsString, ',')
    local matchingKeys = {}
    
    local shouldFilter = conditionValue ~= nil
    
    -- 如果是 pack 筛选,提前解析 conditionValue 以支持缩写
    local resolvedConditionValue = nil
    if isPackFilter and type(conditionValue) == 'string' then
        resolvedConditionValue = resolvePackValue(conditionValue)
    end


    for songId, songEntry in pairs(songData) do
        if not shouldFilter then
            table.insert(matchingKeys, songId)
        else
            local currentValue = ValueFromValuesByKey(songEntry, conditionField)
            local match = false
            
            -- 1. 数字和布尔值的精确匹配
            if currentValue == conditionValue then
                match = true
            
            -- 2. 字符串匹配
            elseif type(currentValue) == 'string' and type(conditionValue) == 'string' then
                
                -- 2a. Pack 字段: 缩写解析后的精确匹配 (Case-insensitive Exact Match)
                if isPackFilter then
                    local targetValue = resolvedConditionValue -- 这会是完整的包名或原始输入
                    
                    if string.upper(currentValue) == string.upper(targetValue) then
                        match = true
                    end
                
                -- 2b. 其他字符串字段: 模糊匹配 (Case-insensitive Fuzzy Match)
                else
                    local searchString = conditionValue
                    
                    -- 模糊匹配:只要 currentValue 包含 searchString(不区分大小写)
                    if string.find(string.upper(currentValue), string.upper(searchString), 1, true) then 
                        match = true
                    end
                end
            end

            if match then
                table.insert(matchingKeys, songId)
            end
        end
    end
    
    table.sort(matchingKeys)

    if #matchingKeys == 0 then
        return '未找到符合条件的歌曲。'
    end

    local output = {}
    
    -- 检查是否需要禁用宽度设置
    local disableWidths = (#displayFields <= 8)
    
    -- 表格样式
    table.insert(output, '{| class="wikitable sortable" style="width: 100%; margin: 0 auto; text-align: center;"')
    
    -- 构造表格头部:添加宽度样式控制
    table.insert(output, '|-')
    for _, field in ipairs(displayFields) do
        field = mw.text.trim(field)
        
        local _, mode = getDifficultyDetails(field)
        local headerContent = displayNames[field] or field
        local headerStyle = ''
        local separator = ' ' -- 默认分隔符是空格
        
        if not disableWidths then -- 宽度要求失效检查
            -- BPM/Key/Duration 字段,固定宽度 6%
            if field == 'bpm' or field == 'key' or field == 'duration' then 
                headerStyle = ' style="width: 6%;"'
                separator = ' | ' 
            -- Pack 字段,固定宽度 10%
            elseif field == 'pack' then
                headerStyle = ' style="width: 10%;"'
                separator = ' | ' 
            elseif mode then -- 谱面难度字段 (3.2% 固定宽度)
                headerStyle = ' style="width: 3.2%;"'
                separator = ' | ' 
            elseif string.match(field, '_notes$') then -- NOTES 字段 (4% 固定宽度)
                headerStyle = ' style="width: 4%;"'
                separator = ' | ' 
            end
        end
        
        -- MediaWiki 表格头单元格: ! style | Content
        table.insert(output, '!' .. headerStyle .. separator .. headerContent)
    end
    
    -- 构造表格行
    for _, songId in ipairs(matchingKeys) do
        local songEntry = songData[songId]
        table.insert(output, '|-')
        
        for _, field in ipairs(displayFields) do
            field = mw.text.trim(field)
            
            local rawValue = ValueFromValuesByKey(songEntry, field)
            
            -- 获取格式化的值
            local formattedValue = formatSingleValue(rawValue, field)
            
            -- 应用内容样式 (BPM/Key)
            if field == 'bpm' then
                formattedValue = formatBPMValue(formattedValue)
            elseif field == 'key' then
                formattedValue = formatKeyValue(rawValue) -- 对 key 字段应用颜色逻辑,使用原始值 (rawValue)
            end

            local _, mode = getDifficultyDetails(field)
            if mode then
                formattedValue = formatDifficultyStyles(formattedValue, mode)
            end
            
            -- *** NOTES 字段样式逻辑 (包含 _sc_notes 默认颜色) ***
            if string.match(field, '_notes$') then 
                local numValue = tonumber(rawValue)
                
                -- 1. 检查是否为高优先级(>= 2000/3000)
                if numValue and numValue >= 2000 then
                    -- 高优先级:应用红/紫色的 formatNotesValue 
                    formattedValue = formatNotesValue(formattedValue, 'table') 
                -- 2. 检查是否为所有 _sc_notes 字段且为低优先级(< 2000)
                elseif string.match(field, '_sc_notes$') then
                    -- sc_notes 默认颜色:3d66ff (如果值不是高优先级)
                    formattedValue = '<span style="color: #3d66ff;">' .. formattedValue .. '</span>'
                end
            end
            
            local cellContent = formattedValue
            
            -- CRITICAL FIX: 如果内容是单个 '-', 增加空格以避免被解析为行分隔符 '|-'.
            if cellContent == '-' then
                cellContent = ' -'
            end

            table.insert(output, '|' .. cellContent)
        end
    end
    
    table.insert(output, '|}')
    
    return table.concat(output, '\n')
end

return p