-- Module to create sister project link box
local getArgs = require('Module:Arguments').getArgs
local sideBox = require('Module:Side box')._main
local p = {}

local logo = {
	wikt="Wiktionary-logo-v2.svg",
	c="Commons-logo.svg",
	n="Wikinews-logo.svg",
	q="Wikiquote-logo.svg",
	s="Wikisource-logo.svg",
	b="Wikibooks-logo.svg",
	voy="Wikivoyage-Logo-v3-icon.svg",
	v="Wikiversity logo 2017.svg",
	species="Wikispecies-logo.svg",
	iw="Wikipedia-logo-v2.svg",
	iw1="Wikipedia-logo-v2.svg",
	iw2="Wikipedia-logo-v2.svg",
	d="Wikidata-logo.svg",
	m="Wikimedia Community Logo.svg",
	mw="MediaWiki-2020-icon.svg",
	f="Wikifunctions-logo-en.svg"}

local prefixList = {'wikt', 'c', 'n', 'q', 's', 'b', 'v', 'voy',
	'species', 'species_author', 'iw', 'iw1', 'iw2', 'd', 'm', 'mw', 'f'}

local sisterName = {
	wikt="위키낱말사전",
	c="위키공용",
	n="위키뉴스",
	q="위키인용집",
	s="위키문헌",
	b="위키책",
	voy="위키여행",
	v="위키배움터",
	species="위키생물종",
	iw="위키백과",
	iw1="위키백과",
	iw2="위키백과",
	d="위키데이터",
	m="메타위키",
	mw="미디어위키",
 f="위키함수"}

local sisterInfo = {
	wikt="사전적 정의",
	c="미디어",
	n="뉴스",
	q="인용구",
	s="원문",
	b="교과서",
	voy="여행 가이드",
	v="리소스",
	species="생물 분류 도감",
	species_author="저자 정보",
	iw="에디션",
	iw1="에디션",
	iw2="에디션",
	d="데이터",
	m="토론",
	mw="문서",
 f="함수"}

local defaultSisters = {
	wikt=true,
	c=true,
	n=true,
	q=true,
	s=true,
	b=true,
	voy='auto',
	v=true,
	species='auto',
	species_author=false,
	iw=false,
	iw1=false,
	iw2=false,
	d=false,
	m=false,
	mw=false,
 f=false

local sisterDb = {	
	wikt="kowiktionary",
	n="kowikinews",
	q="kowikiquote",
	s="kowikisource",
	b="kowikibooks",
	voy="kowikivoyage",
	v="kowikiversity",
	species="specieswiki"}

local trackingType = {
	wdMismatch="Pages using Sister project links with wikidata mismatch",
	wdNamespace="Pages using Sister project links with wikidata namespace mismatch",
	wdHidden="Pages using Sister project links with hidden wikidata",
	defaultSearch="Pages using Sister project links with default search"}

local inSandbox = mw.getCurrentFrame:getTitle:find 

-- Function to add "-sand" to classes when called from sandbox
local function sandbox(s)
	return inSandbox and s.."-sand" or s
end

-- Function to canonicalize string
-- search for variants of "yes", and "no", and transform
-- them into a standard form (like Template:YesNo)
-- Argument:
-- s --- input string
-- Result:
-- {x,y} list of length 2
-- x = nil if s is canonicalized, otherwise has trimmed s
-- y = canonical form of s 
local function canonicalize(s)
	if s == nil then
		return {nil, nil}
	end
	-- if s is table/list, then assume already canonicalized and return unchanged
	if tostring(type(s)) == "table" then
		return s
	end
	s = mw.text.trim(tostring(s))
	if s == "" then
		return {nil, nil}
	end
	local lowerS = s:lower
	-- Check for various forms of "yes"
	if lowerS == 'yes' or lowerS == 'y' or lowerS == 't' 
	 or lowerS == '1' or lowerS == 'true' or lowerS == 'on' then
		return {nil, true}
	end
 -- Check for various forms of "no"
	if lowerS == 'no' or lowerS == 'n' or lowerS == 'f' 
	 or lowerS == '0' or lowerS == 'false' or lowerS == 'off'then
		return {nil, false}
	end
 -- Neither yes nor no recognized, leave string trimmed
	return {s, true}
end

-- Merge two or more canonicalized argument lists
-- Arguments:
-- argList = list of canonicalized arguments
-- noAll = if true, return no when all argList is no.
-- otherwise, return blank when all argList is blank
local function mergeArgs
	local test = nil -- default, return blank if all blank
	if noAll then
		test = false -- return no if all no
	end
	local allSame = true
	-- Search through string for first non-no or non-blank
	for _, arg in ipairs(argList) do
		if arg[2] then
			return arg -- found non-no and non-blank, return it
		end
		-- test to see if argList is all blank / no
		allSame = allSame and (arg[2] == test)
	end
	-- if all blank / no, return blank / no
	if allSame then
		return {nil, test} -- all match no/blank, return it
	end
	-- otherwise, return no / blank
	if noAll then
		return {nil, nil}
	end
	return {nil, false}
end
		
-- Function to get sitelink for a wiki
-- Arguments:
-- wiki = db name of wiki to lookup
-- qid = QID of entity to search for, current page entity by default
local function getSitelink
	-- return nil if some sort of lookup failure
	return qid and mw.wikibase.getSitelink
end

-- Function to get sitelink for a wiki
-- Arguments:
-- prefix = prefix string for wiki to lookup
-- qid = QID of entity to search for, current page entity by default
local function fetchWikidata
	local sisterDbName = sisterDb[prefix]
	return sisterDbName and getSitelink
end

-- Function to generate the sister link itself
-- Arguments:
-- args = argument table for function
-- args[1] = page to fetch
-- args.default = link when blank
-- args.auto = new auto mode (don't fall back to search)
-- args.sitelink = wikidata sitelink (if available)
-- args.qid = QID of entity
-- args.search = fallback string to search for
-- args.sisterPrefix = wikitext prefix for sister site
-- args.information = type of info sister site contains
-- tracking = tracking table
local function genSisterLink
	if args[1][2] == false or (not args.default and args[1][2] == nil) then
		return nil --- either editor specified "no", or "blank" (and default=no), then skip this sister
	end
	local sitelink = args.sitelink or fetchWikidata
	if args.auto and not sitelink and args[1][2] == nil then
		return nil --- in auto mode, if link is blank and no sitelink, then skip
	end
	-- fallback order of sister link: first specified page, then wikidata, then search
	local link = args[1][1] or sitelink or (args.search and "Special:"..args.search)
	if not link then
		return nil --- no link found, just skip
	end
	if tracking then
		-- update state for tracking categories
		if args[1][1] and sitelink then
			-- transform supplied page name to be in wiki-format
			local page = mw.ustring.gsub
			page = mw.ustring.sub:upper..mw.ustring.sub
			local pageNS = mw.ustring.match(page,"^([^:]+):")
			local sitelinkNS = mw.ustring.match(sitelink,"^([^:]+):")
			if page == sitelink then
				tracking.wdHidden = args.sisterPrefix
			elseif pageNS ~= sitelinkNS then
				tracking.wdNamespace = args.sisterPrefix
			else
				tracking.wdMismatch = args.sisterPrefix
			end
		-- if no page link, nor a wikidata entry, and search is on, then warn
		elseif not (args[1][2] or sitelink) and args.search then
			tracking.defaultSearch = args.sisterPrefix
		end
	end
	return {prefix=args.sisterPrefix, link=link, logo=args.logo, name=args.name,
		 information=args.information, prep=args.prep}
end

-- Function to handle special case of commons link
local function commonsLinks
	-- use Module:Commons link to determine best commons link
	local commonsLink = require('Module:Commons link')
	local cLink = (not args.commonscat) and commonsLink._hasGallery(args.qid)
	 or commonsLink._hasCategory(args.qid)
	if commonsPage[1] and (
		not mw.ustring.match
	) and (
		not mw.ustring.match
	) then
		commonsPage[1] = (args.commonscat and "Category:" or "")..commonsPage[1]
	end
	local commonsSearch = "Search/"..(args.commonscat and "Category:" or "")..args[1]
	return {link=cLink, search=commonsSearch}
end

-- Function to handle special case for "author" and "cookbook"
local function handleSubtype(args)
	local ns = args.ns
	local ns_len = mw.ustring.len(ns)
	local result = {}
	result.sitelink = fetchWikidata
	local subtype = false
	if args.page then
		if mw.ustring.sub == ns then
 		subtype = true
 	elseif args.subtype then
 		result.page = ns..args.page
 		subtype = true
 	end
	elseif result.sitelink then
		subtype = mw.ustring.sub == ns
	elseif args.subtype then
		result.search = "Search/"..ns..args.default
		subtype = true
	end
	if subtype then
		result.info = args.info
	end
	return result
end

-- Function to create a sister link, by prefix
-- Arguments:
-- prefix = sister prefix 
-- args = arguments for this sister (see p._sisterLink above)
-- tracking = tracking table
local function sisterLink
	-- determine arguments to genSisterLink according to prefix
	if prefix == 'species_author' and not args.species[1] and args.species[2] and not args.species_author[1] and args.species_author[2] then
		return nil
	end
	local default = defaultSisters[prefix]
	if default == 'auto' then
		default = args.auto
	end
	-- Handle exceptions by prefix
	local search = ((prefix == 'd' and "ItemByTitle/kowiki/") or "Search/")..args[1]
	local sitelink = prefix == 'd' and args.qid
 local page = args[prefix]
 local info = sisterInfo[prefix]
 -- special case handling of author and cookbook
 local subtype = nil
 if prefix == 's' then
 	subtype = handleSubtype
 elseif prefix == 'b' then
 	subtype = handleSubtype
 end
 if subtype then
 page[1] = subtype.page or page[1]
		search = subtype.search or search
		sitelink = subtype.sitelink or sitelink
		info = subtype.info or info
	end
 if prefix == 'voy' then
 	if not args.bar then
 		info = "여행 정보"
 	end
 	if page[1] then
 		if mw.ustring.match then
 			info = "상용 회화집"
 		end
 	elseif page[2] or args.auto then
 		sitelink = sitelink or fetchWikidata
 		if sitelink and mw.ustring.match then
 			info = "상용 회화집"
 		end
		end
 end
 info = args.information or info
 if prefix == 'c' then
 	local commons = commonsLinks
 	search = commons.search
 	sitelink = commons.link
 end
 prefix = (prefix == 'species_author' and 'species') or prefix
 local logo = logo[prefix]
 local name = sisterName[prefix]
 local prep = "—"
 if mw.ustring.sub == 'iw' then
 	local lang = nil
 	local iw_arg = args[prefix]
 	if iw_arg[1] then
 		lang = iw_arg[1]
 	elseif iw_arg[2] then
 		local P424 = mw.wikibase.getBestStatements[1]
	 if P424 and P424.mainsnak.datavalue then
	 	lang = P424.mainsnak.datavalue.value
	 end
	 end
		if lang == nil then
			return nil
		end
	 prefix = ':'..lang
	 page[1] = ""
	 page[2] = true
	 local langname = mw.language.fetchLanguageName
	 if not langname or #langname == 0 then
	 	return nil
	 end
	 info = langname..' '..info
	 prep = "—"
 end
 return genSisterLink
end

local function templatestyles_page(is_bar)
	local sandbox = inSandbox and '연습장/' or ''
	if is_bar then
		return mw.ustring.format
	end
	return mw.ustring.format
end

-- Function to create html containers for sister project link list
-- Arguments:
-- args = table of arguments
-- args.position: if 'left', position links to left
-- args.collapsible: if non-empty, make box collapsible. If 'collapse', start box hidden
-- args.style: CSS style string appended to end of default CSS
-- args.display: boldface name to display
local function createSisterBox

	local list = mw.html.create('ul')
 for i, link in ipairs(sisterList) do
	 local li = list:tag('li')
	 -- html element for 27px-high logo
	 local logoSpan = li:tag('span')
	 logoSpan:addClass(sandbox("sister-logo"))
	 logoSpan:wikitext("27x27px|middle|link=|alt=")
	 -- html element for link
	 local linkspan = li:tag('span')
	 linkspan:addClass(sandbox("sister-link"))
	 local linkText = ""..link.information .." "..link.prep.." "..link.name
	 linkspan:wikitext(linkText)
 end
 list:allDone
 
 return sideBox({
		role = 'navigation',
		labelledby = 'sister-projects',
		class = sandbox("sister-box") .. ' sistersitebox plainlinks',
		position = args.position,
		style = args.style,
		abovestyle = args.collapsible and 'clear: both' or nil,
		above = mw.ustring.format,
		text = tostring(list),
		collapsible = args.collapsible,
		templatestyles = templatestyles_page
end

local function createSisterBar
	local nav = mw.html.create( 'div' )
	nav:addClass( 'noprint')
	nav:addClass( 'metadata')
	nav:addClass( sandbox('sister-bar'))
	nav:attr
	nav:attr
	local header = nav:tag('div')
	header:addClass(sandbox('sister-bar-header'))
	local pagename = header:tag('b')
	pagename:wikitext(args.display or args[1])
	local headerText = " — 위키백과의"
	headerText = headerText..':'
	header:wikitext(headerText)
	if #sisterList == 1 and args.trackSingle then
		header:wikitext("")
	end
	local container = nav:tag('ul')
	container:addClass(sandbox('sister-bar-content'))
	for _, link in ipairs(sisterList) do
		local item = container:tag('li')
		item:addClass(sandbox('sister-bar-item'))
		local logoSpan = item:tag('span')
		logoSpan:addClass(sandbox('sister-bar-logo'))
		logoSpan:wikitext("21x19px|link=|alt=")
		local linkSpan = item:tag('span')
		linkSpan:addClass(sandbox('sister-bar-link'))
		linkSpan:wikitext(" "..link.prep.." "..link.name)
	end
	return nav
end

function p._main(args)
	local titleObject = mw.title.getCurrentTitle
	local ns = titleObject.namespace
	-- find qid, either supplied with args, from search string, or from current page
	args.qid = args.qid or mw.wikibase.getEntityIdForTitle(args[1] or "") or mw.wikibase.getEntityIdForCurrentPage
	args.qid = args.qid and args.qid:upper
	-- search string defaults to PAGENAME
 args[1] = args[1] or mw.wikibase.getSitelink(args.qid or "") or titleObject.text
 -- handle redundant "commons"/"c" prefix
 args.c = args.c or args.commons
	-- Canonicalize all sister links (handle yes/no/empty)
	for _, k in ipairs(prefixList) do
		args[k] = canonicalize(args[k])
	end
	-- Canonicalize cookbook
	args.cookbook = canonicalize(args.cookbook)
	args.b = mergeArgs
	args.cookbook = args.cookbook[2]
	-- handle trackSingle parameter
	if args.trackSingle == nil then
 	args.trackSingle = true
	end
 if ns ~= 0 and ns ~= 14 then
 	args.trackSingle = false
 end
 -- Canonicalize general parameters
 for _,k in pairs do
 	args[k] = canonicalize(args[k])[2]
 end
	-- Initialize tracking categories if main namespace
	local tracking = (args.tracking or ns == 0) and {}
 local sisterList = {}
 local prefix
 -- Loop through all sister projects, generate possible links
 for _, prefix in ipairs(prefixList) do
 	local link = sisterLink
 	if link then
			table.insert
		end
	end
 local box = mw.html.create
 if args.bar and #sisterList > 0 then
 	box:wikitext(mw.getCurrentFrame:extensionTag{
			name = 'templatestyles', args = { src = templatestyles_page(true) }
 	box:node(createSisterBar)
 elseif #sisterList == 1 then
 	-- Use single sister box instead of multi-sister box
 	local sister = sisterList[1]
 	local link = "[["..sister.prefix..":"..sister.link.."|]]"
 	if sister.name == '위키공용' then
 		sister.name = '위키미디어 공용' -- make single sister commons box look like 
 	end
 	local text = sister.name.."에서 "..link.."에 관련된 "..mw.ustring.lower(sister.information).." 문서가 있습니다."
 	if sister.name == '위키백과' then -- make single sister interwiki box look like 
 		text = " "..sister.prep.." 위키백과, 우리 모두의 백과사전"
 	end
 	box:wikitext(sideBox({
 		role = 'navigation',
 		position=args.position,
 		image="40x40px|class=noviewer|alt=|link=",
 		metadata='no',
 		class='plainlinks sistersitebox',
 	 text=text,
			templatestyles = templatestyles_page
 elseif #sisterList > 0 then
 	-- else use sister box if non-empty
 	box:wikitext(createSisterBox)
 end
 if #sisterList == 0 and args.auto then
 	local generateWarning = require('Module:If preview')._warning
 	box:wikitext(generateWarning({"위키데이터에 자매 프로젝트 링크가 없습니다. auto=0을 시도하십시오."}))
 end
	-- Append tracking categories to container div
	-- Alpha ordering is by sister prefix
	if tracking then
		for k, v in pairs(tracking) do
			box:wikitext("")
		end
 	if #sisterList == 0 then
 		box:wikitext("")
 	end
	end
	return tostring(box)
end

-- Main entry point for generating sister project links box
function p.main(frame)
	local args = getArgs
	return p._main(args)
end

-- Lua entry point for generate one sister link
function p._sisterlink(args)
 local prefix = args.prefix
	-- Canonicalize all sister links (handle yes/no/empty)
	for _, k in ipairs(prefixList) do
		args[k] = canonicalize(args[k])
	end
	-- Canonicalize cookbook
	args.cookbook = canonicalize(args.cookbook)
	args.b = mergeArgs
	args.cookbook = args.cookbook[2]
 -- Canonicalize general parameters
 for _,k in pairs do
 	args[k] = canonicalize(args[k])[2]
 end
 args[1] = args[1] or mw.title.getCurrentTitle.text
	args.qid = args.qid or mw.wikibase.getEntityIdForCurrentPage
	args.qid = args.qid and args.qid:upper
	local link = sisterLink
	if not link then
		return ""
	end
	return ""..link.information .." "..link.prep.." "..link.name
end

-- Template entry point for generating one sister link
function p.link(frame)
	local args = getArgs(frame)
	return p._sisterlink(args)
end