-- lua module that draws a seal with rows of texts
require('strict')

local p = {} --p stands for package

local getArgs = require('Module:Arguments').getArgs
local make_style_string = require('Module:Optional style').make_style_string
local error_message = require('Module:Error')['error']

-- main function
function p.seal(frame)
	local args = getArgs(frame, {
	trim = false,
	removeBlanks = false
	})
	return p._seal(args)
end

-- function to draw the seal
function p._seal(args)
	-- arguments
	local arguments = {
		['color'] = args.color or 'red',
		['mode'] = args.mode or 'red',
		['mode_index'] = args['mode index'] or '',
		['direction'] = args.direction or 'column-rl',
		['font_size'] = args['font size'] or 18,
		['shape'] = args.shape or 'rectangular',
		['stretch'] = args.stretch or 0,
		['border_size'] = args['border size'] or 2,
		['border_corner'] = args['border corner'] or 0,
		['border'] = args.border or 'default',
		['hscale'] = args['hscale'] or ''
	}

	local row_number = 0
	for i,line in ipairs(args) do
		row_number = row_number + 1
	end

	if row_number == 0 then
		return error_message({['message'] = '錯誤!缺少文字内容。'})
	end
	
	-- style tables 
	local grid = {}
	local row = {}
	
	-- stacking and writing mode
	local stacking = 'v'
	local direction = 'rl';
	if arguments.direction == "row" then
		stacking = 'h'
	else
		if arguments.direction == "column-lr" then
			direction = 'lr'
		end
		grid['writing-mode'] = 'vertical-' .. direction
	end

	-- colors	
	local text_color = arguments.color
	local background_color = 'white'
	if arguments.mode == "white" then
		text_color = 'white'
		background_color = arguments.color
	end
	grid['background-color'] = background_color
	
	-- mixed mode
	local mode_string
	if arguments.mode == "mixed" then
		-- color
		local mode_length = #arguments.mode_index
		if mode_length > row_number then
			mode_string = string.sub(arguments.mode_index,1,row_number)
		else 
			mode_string = arguments.mode_index .. string.rep('r', row_number - mode_length)
		end
	end
	
	-- font size
	local font_size = tonumber(arguments.font_size)
	if font_size <= 0 then
		return error_message({['message'] = '錯誤!字體大小不能為0或負數'})
	end 
	row['font-size'] = tostring(font_size) .. 'px'
	
	-- padding
	grid['padding'] = '0.5px'
	
	-- stretch modes
	-- h stretch
	local stretch_h = false
	local hscale_string = ''
	if arguments.hscale ~= '' then
		stretch_h = true
		local hscale_length = #arguments.hscale
		if hscale_length > row_number then
			hscale_string = string.sub(arguments.hscale, 1, row_number)
		else 
			hscale_string = arguments.hscale .. string.rep('1', row_number - hscale_length)
		end
	end
	-- stretch
	local line_height = font_size
	local stretch = tonumber(arguments.stretch)
	local line_scale = 1
	local pad_white_row = 0
	if stretch > 0 then
		-- stretch the texts
		line_scale = math.floor( stretch  * 100 /  row_number) / 100
		if line_scale < 1 then
			line_height = math.floor( stretch * font_size / row_number)
			pad_white_row = line_height / 4
		else
			local row_gap = (line_scale - 1) * font_size
			grid['row-gap'] = tostring(row_gap) .. 'px'
			local extra_x_padding = 0.5 * (line_scale - 1) * font_size + 0.5
			grid['padding'] = grid['padding'] .. ' ' .. tostring(extra_x_padding) .. 'px'
		end
		if not stretch_h then
			if stacking == 'v' then
				row['transform'] = 'scale(' .. tostring(line_scale) .. ',1)'
			else
				row['transform'] = 'scale(1,' .. tostring(line_scale) .. ')'
			end
		end 
	elseif arguments.shape == 'circle' then
	end
	if stretch > 0 or stretch_h then
		row['transform-origin'] = 'center'
	end
	grid['line-height'] = tostring(line_height) .. 'px'
	
	-- border
	local border_size = tonumber(arguments.border_size)
	if border_size < 0 then
		return error_message({['message'] = '錯誤!border 大小不能為負數'})
	end 
	grid['border'] = tostring(border_size) .. 'px solid ' .. arguments.color
	
	-- double border
	if arguments.border == 'double' then
		local inner_border = math.floor( font_size /9 * 10)/10
		local shadow = border_size - inner_border
		local double_padding = math.min(0.5, inner_border)
		grid['border'] = tostring(double_padding) .. 'px solid ' .. arguments.color
		grid['padding'] = nil
		grid['margin'] = tostring(shadow) .. 'px'
		grid['box-shadow'] = '0 0 0 ' .. tostring(inner_border) .. 'px white, 0 0 0 ' ..  tostring(shadow) .. 'px ' .. arguments.color
	end 
	
	-- border corner
	local border_corner = tonumber(arguments.border_corner)
	local inner_corner = 0
	if border_corner > 0 then
		inner_corner = math.max(0, border_corner - border_size)
	end
	grid['border-radius'] = tostring(border_corner) .. 'px'

	local corner1 = ''
	local corner2 = ''
	local corner3 = ''
	local corner4 = ''	
	if inner_corner > 0 then
		if stacking == 'v' then
			local tail_a = (direction == 'lr' ) and 'left' or 'right'
			local tail_b =  (direction == 'lr' ) and 'right' or 'left'
			corner1 = 'top-' .. tail_a
			corner2 = 'bottom-' .. tail_a	
			corner3 = 'top-' .. tail_b
			corner4 = 'bottom-' .. tail_b
		else
			corner1 = 'top-left'
			corner2 = 'top-right'	
			corner3 = 'bottom-left'
			corner4 = 'bottom-right'				
		end
	end
	
	-- construct the table
	local grid_style = make_style_string(grid)
	local row_style = make_style_string(row)
	local seal_table = {}
	
	for i,line in ipairs(args) do
		local this_row = {}
		if arguments.mode == "mixed" and string.sub(mode_string, i, i) == 'w' then
			this_row['color'] = background_color
			this_row['background-color'] = text_color
			-- additional padding of rows of mode=white
			if line_scale < 1 then
				if stacking == 'v' then
					this_row['padding'] = '0 ' .. tostring(pad_white_row) .. 'px'
					this_row['margin'] = '0 -' .. tostring(pad_white_row) .. 'px'
				else
					this_row['padding'] = tostring(pad_white_row) .. 'px 0'
					this_row['margin'] = '-' .. tostring(pad_white_row) .. 'px 0'				
				end
			end
		else
			this_row['color'] = text_color
			this_row['background-color'] = background_color
		end
		
		if stretch_h then
			local h_scale = string.sub(hscale_string, i, i)
			if h_scale ~= 1 or line_scale ~= 1 then
				if stacking == 'v' then
					this_row['transform'] = 'scale(' .. tostring(line_scale) .. ',' .. h_scale .. ')'
				else
					this_row['transform'] = 'scale(' .. h_scale  .. ',' .. tostring(line_scale) .. ')'				
				end
			end
		end
		
		if i == 1 and border_corner > 0 then
			this_row['border-' .. corner1 .. '-radius'] = tostring(inner_corner) .. 'px'
			this_row['border-' .. corner2 .. '-radius'] = tostring(inner_corner) .. 'px'
		end	
		if i == row_number and border_corner > 0 then
			this_row['border-' .. corner3 .. '-radius'] = tostring(inner_corner) .. 'px'
			this_row['border-' .. corner4 .. '-radius'] = tostring(inner_corner) .. 'px'
		end
		
		local additional_row_style = make_style_string(this_row)
		
		local this_row_style = mw.ustring.sub(row_style, 1, -2) .. mw.ustring.sub(additional_row_style, 8, -1) 
		table.insert(seal_table, '<span class="seal-row-' .. stacking .. '" ' .. this_row_style .. '>' .. line .. '</span>')
	end
	
	local seal_rows = table.concat(seal_table)
	local seal_grid = '<span class="seal-grid-' .. stacking .. '" ' .. grid_style .. '>' .. seal_rows .. '</span>'
		--error(seal_grid)
	return seal_grid

end

return p