모듈:Taxonomy

-- [[ 생물 분류 정보 표시용 틀 ]]
local taxo = {}

local datafile = mw.loadData('Module:Taxonomy/data')
local korean_rank_name = datafile.korean_rank_name
local taxobox_colormap = datafile.taxobox_color

local major_rank = {regnum = true, phylum = true,  divisio = true, classis = true, ordo = true, familia = true, genus = true, species = true}
local minor_rank = {regio = true, subregnum = true, superdivisio = true, superphylum = ture, subdivisio = true, subphylum = true,
                    superclassis = true, subclassis = true, superordo = true, subordo = true, superfamilia = true, subfamilia = true }
local italic_rank = {genus = true, subgenus = true, sectio = true, species = true, subspecies = true}
local infraspecific_rank = {species = true, subspecies = true, varietas = true, subvarietas = true, forma = true, subforma = true}

local function loadtaxon(taxon)
    local taxoninfo = {}
    taxoninfo.key = taxon
    local page = mw.title.makeTitle('틀', '분류군/' .. taxon)
    if page == nil then
        return nil
    end
    local text = page:getContent()
    if text == nil then
        return nil
    end

    for line in mw.text.gsplit(text, '\n', true) do
        local key, value = mw.ustring.match(line, '|([^=]+)=(.+)')
        if key and value then
            key = mw.text.trim(key)                             -- remove white space
            key = key:gsub(' ', '_')
            value = value:gsub('<!--.*-->', '')
            value = mw.text.trim(value)    -- remove comment and white space
            if key ~= '' and value ~= '' then
                taxoninfo[key] = value
            end
        end
    end
    
    if taxoninfo.same_as then
        local taxoninfo2 = loadtaxon(taxoninfo.same_as)
        if taxoninfo2 then
            for k, v in pairs(taxoninfo2) do
                taxoninfo[k] = taxoninfo[k] or v    -- overloading info2
            end
        end
    end

    -- if taxon is not given, load from info data
    taxoninfo.taxon = taxoninfo.taxon or taxoninfo.key

    -- split link target and display name
    if taxoninfo.link then
        local v1, v2 = taxoninfo.link:match('(.*)|(.*)')
        if v1 and v2 then
            taxoninfo.link = mw.text.trim(v1)
            taxoninfo.name = mw.text.trim(v2)
        end
    end

    if taxoninfo.always_display or major_rank[taxoninfo.rank] then
        taxoninfo.always_display = 'yes'
    end

    if taxoninfo.extinct then taxoninfo.extinct = 'yes' end

    if taxoninfo.parent then
        return taxoninfo
    else
        return nil
    end
end

local function loadtaxainfo(taxon)
    taxon = mw.text.trim(taxon)
    local taxainfo = {}
    taxainfo.key = {}
    taxainfo.list = {}
    taxainfo.rank = {}
    while true do
        local taxoninfo = loadtaxon(taxon)
        if taxoninfo then
            taxainfo.key[taxoninfo.key] = taxoninfo
            table.insert(taxainfo.list, taxoninfo)
            if not taxainfo.rank[taxoninfo.rank] then
                taxainfo.rank[taxoninfo.rank] = taxoninfo.key
            end

            taxon = taxoninfo.parent
        else
            return taxainfo
        end
    end
end

local function koreanrank(rank)
    if korean_rank_name[rank] then
        return korean_rank_name[rank]
    elseif rank:sub(1,8) == 'unranked' then
        return '(미분류)'
    elseif rank:sub(1,8) == 'informal' then
        return '(비공식)'
    else
        return "''" .. rank .. "''"
    end
end

local function taxobox_colour(taxainfo)
    if taxobox_colormap[taxainfo.rank.regnum] then
        return taxobox_colormap[taxainfo.rank.regnum]
    elseif taxobox_colormap[taxainfo.rank.unranked_regnum] then
        return taxobox_colormap[taxainfo.rank.unranked_regnum]
    elseif taxobox_colormap[taxainfo.rank.regio] then
        return taxobox_colormap[taxainfo.rank.regio]
    elseif taxainfo.rank.regio then
        return taxobox_colormap.default
    else
        return 'transparent; text-align:center; border: 1px solid red;'
    end
end

local function get_link(taxoninfo)
    local extinct = ''
    
    if taxoninfo.extinct then
        extinct = '<span style="font-style:normal;font-weight:normal;">†</span>'
    end

    if taxoninfo.link then
        if taxoninfo.name or taxoninfo.extinct then
            local displayname = taxoninfo.name or taxoninfo.link
            return string.format('[[%s|%s%s]]', taxoninfo.link, extinct, displayname)
        else
            return string.format('[[%s]]', taxoninfo.link)
        end
    else
        return extinct .. taxoninfo.taxon
    end
end

local function italic_name(taxoninfo)
    if taxoninfo.rank == 'genus' or taxoninfo.rank == 'subgenus' or taxoninfo.rank == 'sectio' then
        return '<i>' .. taxoninfo.taxon .. '</i>'
    elseif taxoninfo.rank == 'species' then
        local genus, species = taxoninfo.taxon:match('(.*) (.*)')
        if genus and species then
            return string.format('<i>%s. %s</i>', genus:sub(1,1), species)
        end
    elseif infraspecific_rank[taxoninfo.rank] then
        local genus, species, connecting, infraspecific = taxoninfo.taxon:match('(.*) (.*) (.*) (.*)') -- 식물 분류명 시도
        if genus and species and connecting and infraspecific then
            return string.format('<i>%s. %s.</i> %s <i>%s</i>', genus:sub(1,1), species:sub(1,1), connecting, infraspecific)
        end
        local genus, species, subspecies = taxoninfo.taxon:match('(.*) (.*) (.*)')
        if genus and species and subspecies then
            return string.format('<i>%s. %s. %s</i>', genus:sub(1,1), species:sub(1,1), subspecies)
        end
    end
    return taxoninfo.taxon
end

-- emulate [[en:Template:Taxonomy links]]

local function taxonomy_links(taxoninfo)
    if taxoninfo.rank then
        local display_taxon = italic_name(taxoninfo)

        local edit_taxon = string.format('<span class="plainlinks" style="font-size:small; float:right">&#91;[[틀:분류군/%s|%s]]&#93;</span>', taxoninfo.key, display_taxon)
        
        return string.format([==[
|%s:
|<span class="%s" style="white-space:nowrap;">%s</span>%s
|-
]==], koreanrank(taxoninfo.rank), taxoninfo.rank, get_link(taxoninfo), edit_taxon)
    end
end

-- emulate [[en:Template:Taxonomy lists]]
local function taxonomy_lists(taxainfo)
    
    local t = {}
    for i = #taxainfo.list, 1, -1 do
        table.insert(t, taxonomy_links(taxainfo.list[i]))
    end
    return table.concat(t)
end

-- emulate [[en:Template:Taxonomy key]]
local function taxonomy_key(taxainfo)
    local taxoninfo = taxainfo.list[1]

    local taxa_header = string.format([==[
{| class="infobox biota"
|-
! colspan=2 style="text-align: center; background-color: %s;" | 조상 분류군
|-
]==], taxobox_colour(taxainfo))
    local taxa_footer = '|}\n'
    local taxa_list = taxa_header .. taxonomy_lists(taxainfo) .. taxa_footer
    
    -- child taxa 기능 추가
    local display_link
    if taxoninfo.link then
        if taxoninfo.name or taxoninfo.extinct then
            display_link = '[[' .. taxoninfo.link .. '|' .. taxoninfo.link .. '|'
            if taxoninfo.extinct then
                display_link = display_link .. '&dagger;'
            end
            display_link = display_link .. (taxoninfo.name or taxoninfo.link)
            if taxoninfo.uncertain then
                display_link = display_link .. ' (?)'
            end
            display_link = display_link .. ']]'
        else
            display_link = '[[' .. taxoninfo.link .. ']]'
        end
    else
        display_link = taxoninfo.taxon or '??'
    end
    
    local taxa_body = string.format([==[
{| class="wikitable"
| 부모(parent):
|<code>%s</code> ([[틀:분류군/%s|분류군]])
|-
|계급(rank):
|<code>%s</code> (<code>%s</code>)
|-
|링크(link):
|'''<code>%s</code>'''
|-
|절멸(extinct):
|%s
|-
|명명(authority):
|%s
|-
|항상 표시(always_display):
|%s %s
|-
|참조(refs):
|%s
|-
| colspan=2 style="font-size:smaller" | 이 정보는 [[틀:분류군 자료]] 틀에 의해 자동으로 생성되었습니다.
|}
]==],
        taxoninfo.parent, taxoninfo.parent,
        taxoninfo.rank, koreanrank(taxoninfo.rank),
        display_link,
        taxoninfo.extinct and 'Yes' or 'no',
        taxoninfo.authority or '',
        taxoninfo.always_display and 'Yes'  or 'no',
        major_rank[taxoninfo.rank] and '(기본 계급)' or (taxoninfo.always_display and '(수동 설정)'  or ''),
        taxoninfo.refs or '')
    return taxa_list .. taxa_body
end

-- emulate [[en:Taxobox/taxonomy]] 
local function taxobox_taxonomy(taxainfo, display_taxa, display_authority, display_latin)
    local t = {}
    for i = #taxainfo.list, 1, -1 do
        local taxoninfo = taxainfo.list[i]
        local authority, latinname
        if (i <= display_taxa._depth) or taxoninfo.always_display
                or (i <= 5 and minor_rank[taxoninfo.rank] and taxoninfo.link)
                or display_taxa[taxoninfo.rank] or display_taxa[taxoninfo.taxon] then
            if i <= display_authority._depth or display_authority[taxoninfo.rank] or display_authority[taxoninfo.taxon] then
                authority = taxoninfo.authority
            end
            if taxoninfo.link and (i <= display_latin._depth or display_latin[taxoninfo.rank] or display_latin[taxoninfo.taxon]) then
                latinname = '(' .. italic_name(taxoninfo) .. ')'
            end

            if authority then
                authority = '<br /><small>' .. authority .. '</small>'
            end

            local display_link

            if taxoninfo.link then
                if taxoninfo.name or taxoninfo.extinct then
                    display_link = '[[' .. taxoninfo.link .. '|'
                    if taxoninfo.extinct then
                        display_link = display_link .. '&dagger;'
                    end
                    display_link = display_link .. (taxoninfo.name or taxoninfo.link)
                    if taxoninfo.uncertain then
                        display_link = display_link .. ' (?)'
                    end
                    display_link = display_link .. ']]'
                else
                    display_link = '[[' .. taxoninfo.link .. ']]'
                end
            else
                display_link = taxoninfo.taxon
            end
            table.insert(t, string.format([==[
|%s:
|<span class="%s" style="white-space:nowrap;">%s</span>%s%s
|-
]==],
        koreanrank(taxoninfo.rank),
        taxoninfo.rank,
        display_link,
        latinname or '',
        authority or ''
    ))
        end -- if taxoninfo.always_display
    end -- for
    return table.concat(t)
end

--[[
function taxo.taxonomy_list(frame)
    local taxon = frame.args[1]
    local taxa = loadtaxainfo(taxon)
    return taxonomy_lists(taxa)
end
]]

function split_comma(str)
    local result = {}
    
    result._depth = 0
    if type(str) ~= 'string' then
        return result
    end

    for segment in mw.text.gsplit(str, ',') do
        if tonumber(segment) then
            result._depth = tonumber(segment)
        else
            result[mw.text.trim(segment)] = true
        end
    end
    return result
end
        
function taxo.taxobox_taxonomy(frame)
    local args = frame.args
    local taxon = args.taxon
    local display_taxa = split_comma(args.display_taxa)
    local display_authority = split_comma(args.display_authority)
    local display_latin = split_comma(args.display_latin)
    local taxa = loadtaxainfo(taxon)
    
    if display_taxa._depth < 1 then
        display_taxa._depth = 1
    end

    return taxobox_taxonomy(taxa, display_taxa, display_authority, display_latin)
end

function taxo.taxobox_colour(frame)
    local taxon = frame.args[1]
    local taxa = loadtaxainfo(taxon)
    return taxobox_colour(taxa)
end

function taxo.taxonomy_key(frame)
    local taxon = frame.args[1]
    local taxa = loadtaxainfo(taxon)
    
    local refs = frame.args.refs    -- quick hack
    if refs then
        taxa.list[1].refs = refs
    end

    return taxonomy_key(taxa)
end

return taxo