local categoryHandler = require('Module:Category handler').main

local p = {}

-- Helper functions

local function checkNum
	-- Checks whether a value is a number greater than or equal to zero. If so,
	-- returns it as a number. If not, returns a default value.
	val = tonumber(val)
	if val and val >= 0 then
		return val
	else
		return default
	end
end

local function addSuffix
	-- Turns a number into a string and adds a suffix.
	if num then
		return tostring(num) .. suffix
	else
		return nil
	end
end

local function checkNumAndAddSuffix
	-- Checks a value with checkNum and adds a suffix.
	num = checkNum
	return addSuffix
end

local function makeCat
	-- Makes a category link.
	if sort then
		return mw.ustring.format
	else
		return mw.ustring.format
	end
end

-- Argument processing

local function makeInvokeFunc(funcName)
	return function (frame)
		local origArgs = require('Module:Arguments').getArgs(frame)
		local args = {}
		for k, v in pairs(origArgs) do
			args[k] = v
		end
		return p.main
	end
end

p.userbox = makeInvokeFunc('_userbox')
p['userbox-2'] = makeInvokeFunc('_userbox-2')
p['userbox-r'] = makeInvokeFunc('_userbox-r')

-- Main functions

function p.main
	local userboxData = p[funcName](args)
	local userbox = p.render(userboxData)
	local cats = p.categories(args)
	return userbox .. (cats or '')
end

function p._userbox(args)
	-- Does argument processing for .
	local data = {}

	-- Get div tag values.
	data.float = args ['정렬'] or args.float or 'left'
	local borderWidthNum = checkNum -- Used to calculate width.
	data.borderWidth = addSuffix
	data.borderColor = args['border-color'] or args[1] or args['border-c'] or args['테두리색'] or args['id-c'] or args['항목바탕색'] or '#999'
	data.width = addSuffix -- Also used in the table tag.
	data.bodyClass = args.bodyclass

	-- Get table tag values.
	data.backgroundColor = args['info-background'] or args[2] or args['info-c'] or args['내용바탕색'] or '#eee'

	-- Get info values.
	data.info = args.info or args['내용'] or args[4] or args.info or ""
	data.infoTextAlign = args['info-a'] or args['내용정렬'] or 'left'
	data.infoFontSize = checkNumAndAddSuffix
	data.infoHeight = checkNumAndAddSuffix
	data.infoPadding = args['내용여백'] or args['info-padding'] or args['info-p'] or '0 4px 0 4px'
	data.infoLineHeight = args['info-line-height'] or args['info-lh'] or '1.25em'
	data.infoColor = args['info-color'] or args['info-fc'] or args['내용글자색'] or 'black'
	data.infoOtherParams = args['info-other-param'] or args['info-op']
	data.infoClass = args['info-class']

	-- Get id values.
	local id = args.logo or args[3] or args['항목'] or args.id
	data.id = id
	data.showId = id and true or false
	data.idWidth = checkNumAndAddSuffix
	data.idHeight = checkNumAndAddSuffix
	data.idBackgroundColor = args['logo-background'] or args[1] or args['id-c'] or args['항목바탕색'] or '#ddd'
	data.idTextAlign = args['항목정렬'] or args['id-a'] or 'center'
	data.idFontSize = checkNumAndAddSuffix
	data.idColor = args['logo-color'] or args['id-fc'] or args['항목글자색'] or data.infoColor
	data.idPadding = args['항목여백'] or args['logo-padding'] or args['id-p'] or '0 1px 0 0'
	data.idLineHeight = args['logo-line-height'] or args['id-lh'] or '1.25em'
	data.idOtherParams = args['logo-other-param'] or args['id-op']
	data.idClass = args['id-class']

	return data
end

p['_userbox-2'] = function (args)
	-- Does argument processing for .
	local data = {}

	-- Get div tag values.
	data.float = args['정렬'] or args.float or 'left'
	local borderWidthNum = checkNum -- Used to calculate width.
	data.borderWidth = addSuffix
	data.borderColor = args[1] or args['border-c'] or args['테두리색'] or args['id1-c'] or '#999999'
	data.width = addSuffix -- Also used in the table tag.
	data.bodyClass = args.bodyclass

	-- Get table tag values.
	data.backgroundColor = args[2] or args['info-c'] or args['내용바탕색'] or '#eeeeee'

	-- Get info values.
	data.info = args[4] or args.info or args['내용'] or ""
	data.infoTextAlign = args['내용정렬'] or args['info-a'] or 'left'
	data.infoFontSize = checkNumAndAddSuffix
	data.infoColor = args[8] or args['info-fc'] or args['내용글자색'] or 'black'
	data.infoPadding = args['내용여백'] or args['info-p'] or '0 4px 0 4px'
	data.infoLineHeight = args['info-lh'] or '1.25em'
	data.infoOtherParams = args['info-op']

	-- Get id values.
	data.showId = true
	data.id = args.logo or args[3] or args['항목'] or args.id1 or 'id1'
	data.idWidth = checkNumAndAddSuffix
	data.idHeight = checkNumAndAddSuffix
	data.idBackgroundColor = args['항목바탕색'] or args[1] or args['id1-c'] or '#dddddd'
	data.idTextAlign = 'center'
	data.idFontSize = checkNumAndAddSuffix
	data.idLineHeight = args['id1-lh'] or '1.25em'
	data.idColor = args['항목글자색'] or args['id1-fc'] or data.infoColor
	data.idPadding = args['항목여백'] or args['id1-p'] or '0 1px 0 0'
	data.idOtherParams = args['id1-op']

	-- Get id2 values.
	data.showId2 = true
	data.id2 = args['항목2'] or args.logo or args[5] or args.id2 or 'id2'
	data.id2Width = checkNumAndAddSuffix
	data.id2Height = data.idHeight
	data.id2BackgroundColor = args['항목2바탕색'] or args[7] or args['id2-c'] or args[1] or '#dddddd'
	data.id2TextAlign = 'center'
	data.id2FontSize = checkNumAndAddSuffix
	data.id2LineHeight = args['id2-lh'] or '1.25em'
	data.id2Color = args['항목2글자색'] or args['id2-fc'] or data.infoColor
	data.id2Padding = args['항목2여백'] or args['id2-p'] or '0 0 0 1px'
	data.id2OtherParams = args['id2-op']

	return data
end

p['_userbox-r'] = function (args)
	-- Does argument processing for .
	local data = {}

	-- Get div tag values.
	data.float = args['정렬'] or args.float or 'left'
	local borderWidthNum = checkNum -- Used to calculate width.
	data.borderWidth = addSuffix
	data.borderColor = args['border-color'] or args[1] or args['border-c'] or args['테두리색'] or args['id-c'] or args['항목바탕색'] or '#999'
	data.width = addSuffix -- Also used in the table tag.
	data.bodyClass = args.bodyclass
	
	-- Get table tag values.
	data.backgroundColor = args['info-background'] or args[2] or args['info-c'] or args['내용바탕색'] or '#eee'

	-- Get id values.
	data.showId = false -- We only show id2 in userbox-r.

	-- Get info values.
	data.info = args.info or args['내용'] or args[4] or ""
	data.infoTextAlign = args['내용정렬'] or args['info-align'] or args['info-a'] or 'left'
	data.infoFontSize = checkNumAndAddSuffix
	data.infoPadding = args['내용여백'] or args['info-padding'] or args['info-p'] or '0 4px 0 4px'
	data.infoLineHeight = args['info-line-height'] or args['info-lh'] or '1.25em'
	data.infoColor = args['info-color'] or args['info-fc'] or args['내용글자색'] or 'black'
	data.infoOtherParams = args['info-other-param'] or args['info-op']
	
	-- Get id2 values.
	data.showId2 = true
	data.id2 = args.logo or args[3] or args['항목'] or args.id or 'id'
	data.id2Width = checkNumAndAddSuffix
	data.id2Height = checkNumAndAddSuffix
	data.id2BackgroundColor = args['logo-background'] or args[1] or args['id-c'] or args['항목바탕색'] or '#ddd'
	data.id2TextAlign = args['항목정렬'] or args['id-a'] or 'center'
	data.id2FontSize = checkNumAndAddSuffix
	data.id2Color = args['logo-color'] or args['id-fc'] or args['항목글자색'] or data.infoColor
	data.id2Padding = args['항목여백'] or args['logo-padding'] or args['id-p'] or '0 0 0 1px'
	data.id2LineHeight = args['logo-line-height'] or args['id-lh'] or '1.25em'
	data.id2OtherParams = args['logo-other-param'] or args['id-op']

	return data
end

function p.render(data)
	-- Renders the userbox html using the content of the data table. 
	-- Render the div tag html.
	local root = mw.html.create('div')
	root

	-- Render the table tag html.
	local tableroot = root:tag('table')
	tableroot
	
	-- Render the id html.
	local tablerow = tableroot:tag('tr')
	if data.showId then
		tablerow:tag('td')
	end

	-- Render the info html.
	tablerow:tag('td')
	
	-- Render the second id html.
	if data.showId2 then
		tablerow:tag('td')
	end

	local title = mw.title.getCurrentTitle
	if (title.namespace == 2) and not title.text:match("/") then
		return tostring(root) -- regular user page
	elseif title.namespace == 14 then
		return tostring(root) -- category
	elseif title.isTalkPage then
		return tostring(root) -- talk page
	end

	local legible = true
	local contrast = require('Module:Color contrast')._ratio

	local function has_text(wikitext)
		local function get_alt(text)
			return text:match("|alt=([^|]*)") or ""
		end
	
		wikitext = wikitext:gsub
		wikitext = wikitext:gsub("%[%[%s*[Mm][Ee][Dd][Ii][Aa]%s*:[^|]-(|.-)]]", get_alt)
		wikitext = wikitext:gsub("%[%[%s*[Ii][Mm][Aa][Gg][Ee]%s*:[^|]-(|.-)]]", get_alt)
		wikitext = wikitext:gsub("%[%[%s*[Ff][Ii][Ll][Ee]%s*:[^|]-(|.-)]]", get_alt)
		return mw.text.trim(wikitext) ~= ""
	end

	if contrast { data.infoColor, data.backgroundColor, error = 0 } < 4.5 then
		legible = false
	end

	if data.showId and contrast { data.idColor, data.idBackgroundColor, error = 0 } < 4.5 then
		if has_text(data.id or "") then
			legible = false
		end
	end

	if data.showId2 and contrast { data.id2Color, data.id2BackgroundColor, error = 0 } < 4.5 then
		if has_text(data.id2 or "") then
			legible = false
		end
	end

	if not legible then
		root:wikitext('분류:잠재적 문제가 있는 유저박스')
	end

	return tostring(root)
end

function p.categories
	-- Gets categories from Module:Category handler.
	-- The page parameter makes the function act as though the module was being called from that page.
	-- It is included for testing purposes.
	local cats = {}
	cats[#cats + 1] = args.usercategory or args['사용자분류']
	cats[#cats + 1] = args.usercategory2 or args['사용자분류2']
	cats[#cats + 1] = args.usercategory3 or args['사용자분류3']
	cats[#cats + 1] = args.usercategory4 or args['사용자분류4']
	cats[#cats + 1] = args.usercategory5 or args['사용자분류5']
	if #cats > 0 then
		-- Get the title object
		local title
		if page then
			title = mw.title.new(page)
		else
			title = mw.title.getCurrentTitle
		end
		-- Build category handler arguments.
		local chargs = {}
		chargs.page = page
		chargs.nocat = args.nocat
		chargs.main = 'Category:Pages with misplaced templates'
		if args.notcatsubpages then
			chargs.subpage = 'no'
		end
		-- User namespace.
		local user = ''
		for i, cat in ipairs(cats) do
			user = user .. makeCat(cat)
		end
		chargs.user = user
		-- Template namespace.
		local basepage = title.baseText
		local template = ''
		for i, cat in ipairs(cats) do
			template = template .. makeCat
		end
		chargs.template = template
		return categoryHandler(chargs)
	else
		return nil
	end
end