--
 keywords are used for languages: they are the names of the actual
 parameters of the template

local keywords = {
 barChart = '막대 도표',
 pieChart = '원 도표',
 width = '넓이',
 height = '높이',
 stack = '쌓기',
 colors = '색',
 group = '집합',
 xlegend = 'x 범례',
	yticks = 'y 틱 마크',
 tooltip = '말풍선',
 accumulateTooltip = '말풍선 합계',
 links = '연결',
 defcolor = '기본색',
 scalePerGroup = '집합 축척',
 unitsPrefix = '머리 단위',
 unitsSuffix = '꼬리 단위',
 groupNames = '집합 이름',
 hideGroupLegends = '집합 범례 숨김',
 slices = '조각합',
 slice = '조각',
 radius = '반지름',
 percent = '백분율',

local defColors = mw.loadData("Module:Chart/Default colors")
local hideGroupLegends

local function nulOrWhitespace( s )
	return not s or mw.text.trim( s ) == ''
end

local function createGroupList
	if #legends > 1 and not hideGroupLegends then
		table.insert( tab, mw.text.tag( 'div' ) )
		local list = {}
		local spanStyle = "padding:0 1em;background-color:%s;border:1px solid %s;margin-right:1em;-webkit-print-color-adjust:exact;"
		for gi = 1, #legends do
			local span = mw.text.tag( 'span', { style = string.format }, '&nbsp;' ) .. ' '.. legends[gi]
			table.insert( list, mw.text.tag )
		end
		table.insert( tab,
			mw.text.tag( 'ul',
				table.concat
			)
		)
		table.insert -- close "position:relative" div that contains slices and imagemap.
	addRes( '' ) -- close containing div
	return frame:preprocess( table.concat )
end

local function barChart( frame )
	local res = {}
	local args = frame.args -- can be changed to frame:getParent.args
	local values, xlegends, colors, tooltips, yscales = {}, {}, {}, {}, {}
	local groupNames, unitsSuffix, unitsPrefix, links = {}, {}, {}, {}
	local width, height, yticks, stack, delimiter = 500, 350, -1, false, args.delimiter or ':'
	local chartWidth, chartHeight, defcolor, scalePerGroup, accumulateTooltip

	local numGroups, numValues
	local scaleWidth

	local function validate
		local function asGroups
			if #tab == 0 and not emptyOK then
				error( "must supply values for " .. keywords[name] )
			end
			if #tab == 1 and toDuplicate then
				for i = 2, numGroups do tab[i] = tab[1] end
			end
			if #tab > 0 and #tab ~= numGroups then
				error 
			end
		end

		-- do all sorts of validation here, so we can assume all params are good from now on.
		-- among other things, replace numerical values with mw.language:parseFormattedNumber result

		chartHeight = height - 80
		numGroups = #values
		numValues = #values[1]
		defcolor = defcolor or 'blue'
		colors[1] = colors[1] or defcolor
		scaleWidth = scalePerGroup and 80 * numGroups or 100
		chartWidth = width - scaleWidth
		asGroups
		asGroups
		asGroups
		asGroups
		if stack and scalePerGroup then
			error( string.format )
		end
		for gi = 2, numGroups do
			if #values[gi] ~= numValues then error( keywords.group .. " " .. gi .. " does not have same number of values as " .. keywords.group .. " 1" ) end
		end
		if #xlegends ~= numValues then error( 'Illegal number of ' .. keywords.xlegend .. '. Should be exactly ' .. numValues ) end
	end

	local function extractParams
		local function testone
			local i = keyword == key and 0 or key:match( keyword .. "%s+(%d+)" )
			if not i then return end
			i = tonumber( i ) or error("Expect numerical index for key " .. keyword .. " instead of '" .. key .. "'")
			if i > 0 then tab[i] = {} end
			for s in mw.text.gsplit do
				table.insert
			end
			return true
		end

		for k, v in pairs( args ) do
			if k == keywords.width then
				width = tonumber( v )
				if not width or width < 200 then
					error( 'Illegal width value : ' .. v )
				end
			elseif k == keywords.height then
				height = tonumber( v )
				if not height or height < 200 then
					error( 'Illegal height value : ' .. v )
				end
			elseif k == keywords.stack then stack = true
			elseif k == keywords.yticks then yticks = tonumber(v) or -1
			elseif k == keywords.scalePerGroup then scalePerGroup = true
			elseif k == keywords.defcolor then defcolor = v
			elseif k == keywords.accumulateTooltip then accumulateTooltip = not nulOrWhitespace( v )
			elseif k == keywords.hideGroupLegends then hideGroupLegends = not nulOrWhitespace( v )
			else
				for keyword, tab in pairs( {
					group = values,
					xlegend = xlegends,
					colors = colors,
					tooltip = tooltips,
					unitsPrefix = unitsPrefix,
					unitsSuffix = unitsSuffix,
					groupNames = groupNames,
					links = links,
						if testone
							then break
						end
				end
			end
		end
	end

	local function roundup( x ) -- returns the next round number: eg., for 30 to 39.999 will return 40, for 3000 to 3999.99 wil return 4000. for 10 - 14.999 will return 15.
		local ordermag = 10 ^ math.floor( math.log10( x ) )
		local normalized = x / ordermag
		local top = normalized >= 1.5 and ( math.floor( normalized + 1 ) ) or 1.5
		return ordermag * top, top, ordermag
	end

	local function calcHeightLimits -- if limits were passed by user, use them, otherwise calculate. for "stack" there's only one limet.
		if stack then
			local sums = {}
			for _, group in pairs( values ) do
				for i, val in ipairs( group ) do sums[i] = ( sums[i] or 0 ) + val end
			end
			local sum = math.max( unpack( sums ) )
			for i = 1, #values do yscales[i] = sum end
		else
			for i, group in ipairs( values ) do yscales[i] = math.max( unpack( group ) ) end
		end
		for i, scale in ipairs( yscales ) do yscales[i] = roundup( scale * 0.9999 ) end
		if not scalePerGroup then for i = 1, #values do yscales[i] = math.max( unpack( yscales ) ) end end
	end

	local function tooltip
		if tooltips and tooltips[gi] and not nulOrWhitespace( tooltips[gi][i] ) then return tooltips[gi][i], true end
		local groupName = mw.text.killMarkers(not nulOrWhitespace( groupNames[gi] ) and groupNames[gi] .. ': ' or '')
		local prefix = unitsPrefix[gi] or unitsPrefix[1] or ''
		local suffix = unitsSuffix[gi] or unitsSuffix[1] or ''
		return string.gsub(groupName .. prefix .. mw.getContentLanguage:formatNum( tonumber( val ) or 0 ) .. suffix, '_', ' '), false
	end

	local function calcHeights
		local barHeight = math.max( 2, math.floor( val / yscales[gi] * chartHeight + 0.5 ) ) -- add half to make it "round" instead of "trunc", min height to 2 to avoid negative bar sizes
		local top, base = chartHeight - barHeight, 0
		if stack then
			for j = 1, gi - 1 do
				if tonumber(values[j][i]) > 0 then
					base = base + math.max( 2, math.floor( values[j][i] / yscales[gi] * chartHeight + 0.5 ) ) -- sum the "i" value of all the groups below our group, gi, and keep the same calculation for each bar 
				end
			end
		end
		return barHeight, top - base
	end

	local function groupBounds( i )
		local setWidth = math.floor( chartWidth / numValues )
		local setOffset = ( i - 1 ) * setWidth
		return setOffset, setWidth
	end

	local function calcx
		local setOffset, setWidth = groupBounds( i )
		if stack or numGroups == 1 then
			local barWidth = math.min( 38, math.floor( 0.8 * setWidth ) )
			return setOffset + (setWidth - barWidth) / 2, barWidth
		end
		setWidth = 0.85 * setWidth
		local barWidth = math.floor( 0.75 * setWidth / numGroups )
		local left = setOffset + math.floor( ( gi - 1 ) / numGroups * setWidth )
		return left, barWidth
	end

	local function drawbar
		if val == '0' then return end -- do not show single line (borders....) if value is 0, or rather, '0'. see talkpage

		local color, tooltip, custom = colors[gi] or defcolor or 'blue', tooltip
		local left, barWidth = calcx
		local barHeight, top = calcHeights

		-- borders so it shows up when printing
		local style = string.format
		local link = links[gi] and links[gi][i] or ''
		local img = not nulOrWhitespace( link ) and string.format or ''
		table.insert( res, mw.text.tag )
	end

	local function drawYScale
		local function drawSingle
			local yscale = yscales[gi]
			local _, top, ordermag = roundup( yscale * 0.999 )
			local numnotches = yticks >= 0 and yticks or
					(top <= 1.5 and top * 4
					or top < 4 and top * 2
					or top)
			local valStyleStr =
				single and 'position:absolute;height=20px;text-align:right;vertical-align:middle;width:%spx;top:%spx;padding:0 2px'
				or 'position:absolute;height=20px;text-align:right;vertical-align:middle;width:%spx;top:%spx;left:3px;background-color:%s;color:white;font-weight:bold;text-shadow:-1px -1px 0 #000,1px -1px 0 #000,-1px 1px 0 #000,1px 1px 0 #000;padding:0 2px'
			local notchStyleStr = 'position:absolute;height=1px;min-width:5px;top:%spx;left:%spx;border:1px solid %s;'
			for i = 1, numnotches do
				local val = i / numnotches * yscale
				local y = chartHeight - calcHeights
				local div = mw.text.tag( 'div', { style = string.format }, mw.getContentLanguage:formatNum( tonumber( val ) or 0 ) )
				table.insert
				div = mw.text.tag( 'div', { style = string.format }, '' )
				table.insert
			end
		end

		if scalePerGroup then
			local colWidth = 80
			local colStyle = "position:absolute;height:%spx;min-width:%spx;left:%spx;border-right:1px solid %s;color:%s"
			for gi = 1, numGroups do
				local left = ( gi - 1 ) * colWidth
				local color = colors[gi] or defcolor
				table.insert( res, mw.text.tag( 'div', { style = string.format } ) )
				drawSingle
				table.insert
		table.insert( res, mw.text.tag( 'div', { style = string.format } ) )
		drawYScale
		table.insert
		table.insert
	end

	extractParams
	validate
	calcHeightLimits
	drawChart
	return table.concat
end

return {
	['bar-chart'] = barChart,
	['bar chart'] = barChart,
	['pie chart'] = pieChart,
	[keywords.barChart] = barChart,
	[keywords.pieChart] = pieChart,