-- Este é um módulo para gerar predefinições de caixas de aviso como as
-- {{mbox}}, {{ambox}}, {{imbox}}, {{tmbox}}, {{ombox}}, {{cmbox}} and {{fmbox}}.

require('strict')
local getArgs
local yesno = require('Módulo:Yesno')

--------------------------------------------------------------------------------
-- funções auxiliares
--------------------------------------------------------------------------------

local function getTitleObject(...)
	-- Get the title object, passing the function through pcall
	-- in case we are over the expensive function count limit.
	local success, title = pcall(mw.title.new, ...)
	if success then
		return title
	end
end

-- Retorna duas strings, a primeira frase e o restante do texto,
-- se a primeira frase for muito grande separa na primeira vírgula
local function splitFirstPhrase(str)
	local _, pos = string.find(str, "%.'* [A-Z]")
	local _, b = string.find(str, "%.'* ?[<\n]")
	if not pos and not b then
		return str
	elseif pos and b then
		pos = math.min(pos, b)
	else
		pos = pos or b
	end
	return str:sub(1, pos - 1), str:sub(pos)
end

local function changeIntro(str, intro)
	local change = str:match'Esta página ou sec?ção'
	change = change or str:match'Esta página'
	if change then
		str = str:gsub(change, intro, 1)
	end
	return str
end

--------------------------------------------------------------------------------
-- Definição da classe MessageBox
--------------------------------------------------------------------------------

local MessageBox = {}
MessageBox.__index = MessageBox

function MessageBox.new(boxType, args, cfg)
	args = args or {}
	local obj = {}

	for k, v in pairs(args) do
		if v == '' then
			args[k] = nil
		end
	end
	obj.args = args

	-- Set the title object and the namespace.
	obj.title = getTitleObject(args['página']) or mw.title.getCurrentTitle()

	-- Set the config for our box type.
	local config = cfg[boxType]
	if not config then
		local ns = obj.title.namespace
		-- boxType is "mbox" or invalid input
		if ns == 0 then
			config = cfg.ambox -- artigo
		elseif ns == 6 then
			config = cfg.imbox -- ficheiro
		elseif ns == 14 then
			config = cfg.cmbox -- categoria
		else
			if obj.title.isTalkPage then
				config = cfg.tmbox -- página de discussão
			else
				config = cfg.ombox -- outra página de conteúdo
			end
		end
	end

	-- Usando canfiguração global em campos não informados
	obj.cfg = {}
	for k, v in pairs(cfg.global) do
		obj.cfg[k] = v
	end
	for k, v in pairs(config) do
		obj.cfg[k] = v
	end

	-- Define a estrutura interna de dados
	obj.categories = {}
	obj.classes = {}
	obj.styles = {}
	obj.categories = {}

	return setmetatable(obj, MessageBox)
end

function MessageBox:addCat(cat, sort)
	if not cat then
		return nil
	end
	if sort then
		cat = string.format('[[Categoria:%s|%s]]', cat, sort)
	else
		cat = string.format('[[Categoria:%s]]', cat)
	end
	table.insert(self.categories, cat)
end

function MessageBox:addClass(class)
	if not class then
		return nil
	end
	table.insert(self.classes, class)
end

function MessageBox:addStyle(stl)
	if type(stl) == 'table' then
		for k, v in pairs(stl) do
			self.styles[k] = v
		end
	end
end

function MessageBox:setParameters()
	local args = self.args
	local cfg = self.cfg

	-- Obtém os dados de configuração
	self.type = args.tipo
	local typeData = cfg.tipos[self.type]
	typeData = typeData or cfg.tipos[cfg.tipoPadrao]

	-- Verifica se esta usando modo pequeno
        self.isSmall = cfg.permitePequeno and 
		(cfg.paramPequeno and args.pequeno == cfg.paramPequeno or
		not cfg.paramPequeno and yesno(args.pequeno))
	self.text = self.isSmall and args['texto_pequeno'] or args.texto
	self.issue = args.problema
	self.fix = args.conserto
	self.name = args.nome
	self.useCollapsibleTextFields = cfg.modoCompacto

	-- Verifica se a predefinição foi incorretamente substituida
	self.isSubstituted = args.subst == 'SUBST'
	if self.isSubstituted then
		self:addCat'!Páginas com predefinições substituídas incorretamente'
	end

	-- Adiciona atributos estilos e classes
	self.id = args.id
	for _, class in ipairs(cfg.classes or {}) do
		self:addClass(class)
	end
	self:addStyle(cfg.estiloBase)
	self:addStyle(typeData.estilo)
	if self.isSmall and cfg.estiloPequeno then
		self:addStyle(cfg.estiloPequeno)
	end
	self.attrs = args.attrs

	-- Verifica se está na página da predefinição que usa o módulo e adiciona categoria
	if self.title.namespace == 10 and cfg.catPredef then
		if not cfg.catPredefRequerNome then
			local xmbox = mw.getCurrentFrame():getParent()
			local template = xmbox and xmbox:getParent()
			if template
				and template.getTitle() == self.title.fullText
				and not self.title.isSubpage
			then
				self:addCat(cfg.catPredef)
			end
		elseif self.name and mw.title.equals(self.name, self.title.fullText) then
			self:addCat(cfg.catPredef)
		end
	end

	-- Muda o inicio do texto se for passado o parâmetro seção ou uso=seção
	local intro
	local masc = {['artigo']='Este', ['portal']='Este', ['tópico']='Este', ['módulo']='Este',
		 ['livro']='Este', ['ficheiro']='Este', ['arquivo']='Este'}
	if args['seção'] or args['secção'] then
		local sect = args['seção'] or args['secção']
		if sect:match'^[A-Z]' or sect:match' ' then
			intro = sect
		elseif yesno(sect) == true then
			intro = 'Esta seção'
		else
			intro = (masc[sect] or 'Esta') .. ' ' .. sect
		end
		if self.text then
			self.text = changeIntro(self.text, intro)
		end
		if self.issue then
			self.issue = changeIntro(self.issue, intro)
		end
	end

	-- Ligação para página de discussão
	local talk = args['discussão']
	if talk then
		local talkText = 'Veja discussão relacionada '
		if talk:match'^[^#]+:' then
			local link, text = talk:match'^([^#]+)#(.+)$'
			self.talk = talkText ..
				'em [[' .. (link and link .. '|' .. text or talk) .. ']].'
		else
			local link = self.title.talkPageTitle
			self.talk = link and talkText ..
				'na [[' .. tostring(link) .. '#' .. talk .. '|página de discussão]].'
		end
	end

	-- Linha abaixo do texto e imagem
	self.below = args.abaixo

	-- Imagem
	if not cfg.nenhumaImagem or args.imagem ~= cfg.nenhumaImagem then
		local image = self.isSmall and args.imagem_pequeno or args.imagem
		local img = image and image:match'%[%[%w+:[^%]|]+%.%a%a%a%a?|%d+px'
		if img then
			self.image = img .. '|link=|alt=]]'
		else
			img = image and image:match'^[%w -%(%)]+%.%a%a%a%a?$' or typeData.imagem
			if img then
				local imageSize = self.isSmall
					and (cfg.tamanhoImagemPequeno or '30x30px')
					or '40x40px'
				self.image = string.format('[[Ficheiro:%s|%s|link=|alt=]]', img
					or 'Info non-talk.png', imageSize)
			end
		end
	end

	if args.imagem_direita then
		local img = args.imagem_direita:match'%[%[%w+:[^%]|]+%.%a%a%a%a?|%d+px'
		if img then
			self.imageRight = img .. '|link=|alt=]]'
		else
			img = args.imagem_direita:match'^[%w -%(%)]+%.%a%a%a%a?$' or typeData.imagem
			if img then
				local imageSize = self.isSmall
					and (cfg.tamanhoImagemPequeno or '30x30px')
					or '40x40px'
				self.imageRight = string.format('[[Ficheiro:%s|%s|link=|alt=]]',
					img, imageSize)
			end
		end
	end
	
	-- A FAZER: criar um módulo para categorizar por data e assunto como a
	-- {{Manutenção/Categorização por assunto}} e chamá-lo aqui,
	-- por enquanto a data só está sendo adicionada no final do texto
	if args.data and not (self.text and  self.text:match'%(desde ') then
		self.date = '<small>(desde ' .. args.data .. ')</small>'
	end
end

function MessageBox:export()
	local root = mw.html.create()

	-- Add the subst check error.
	if self.isSubstituted and self.name then
		root:tag('b')
			:addClass('error')
			:wikitext(string.format(
				'A predefinição <code>%s[[Predefinição:%s|%s]]%s</code> foi incorretamente substituída.',
				mw.text.nowiki('{{'), self.name, self.name, mw.text.nowiki('}}')
			))
	end

	-- Cria a tabela
	local boxTable = root:tag('table')
	boxTable:attr('id', self.id or nil)
	for i, class in ipairs(self.classes or {}) do
		boxTable:addClass(class or nil)
	end
	boxTable
		:css(self.styles)
		:attr('role', 'presentation')

	if self.attrs then
		boxTable:attr(self.attrs)
	end

	-- Adiciona a imagem
	local row = boxTable:tag('tr')
	if self.image then
		local imageCell = row:tag('td')
		imageCell:css{['width']='52px', ['padding']='5px 0 5px 8px', ['text-align']='center'}
		if self.useCollapsibleTextFields then
			imageCell:addClass('mbox-image')
		end
		imageCell:wikitext(self.image)
	end

	-- Adiciona o texto
	local textCell = row:tag('td')
        textCell:css{padding='5px 12px'}
	if self.useCollapsibleTextFields then
		-- Caixas com classe ambox têm uma função de ocultar parte do texto
		-- e abrir o texto todo ao clicar na versão mobile
		textCell:addClass('mbox-text')
		local textShow, textHide = self.issue, self.fix
		if not textShow and self.text then
			textShow, textHide = splitFirstPhrase(self.text)
		end

		local textCellSpan = textCell:tag('span')
		textCellSpan:addClass('mbox-text-span')
		textCellSpan:wikitext(textShow)
		textCellSpan:wikitext(self.date and ' ' .. self.date .. ' ')
		if textHide then
			textCellSpan:tag('span')
				:addClass('hide-when-compact')
				:wikitext(self.talk and ' ' .. self.talk .. ' ')
				:wikitext(textHide)
		else
			textCellSpan:wikitext(self.talk and ' ' .. self.talk)
		end
	else
		textCell:wikitext(self.text)
		textCell:wikitext(self.date and ' ' .. self.date)
		textCell:wikitext(self.talk and ' ' .. self.talk)
	end

	-- Imagem direita
	if self.imageRight then
		local imageCell = row:tag('td')
		imageCell:css{['width']='52px', ['padding']='5px 8px 5px 0', ['text-align']='center'}
		if self.useCollapsibleTextFields then
			imageCell:addClass('mbox-image')
		end
		imageCell:wikitext(self.imageRight)
	end

	-- Linha de baixo
	if self.below then
		local row = boxTable:tag('td')
		row:attr('colspan', tostring(1 + (self.image and 1 or 0) + (self.imageRight and 1 or 0)))
		row:addClass(self.useCollapsibleTextFields and 'hide-when-compact' or nil)
		row:wikitext(self.below)
	end

	-- Adiciona categorias
	root:wikitext(table.concat(self.categories))

	return tostring(root)
end

--------------------------------------------------------------------------------
-- Exports
--------------------------------------------------------------------------------

local p, mt = {}, {}

function p._exportClasses()
	-- For testing.
	return {
		MessageBox = MessageBox
	}
end

function p.main(boxType, args, cfgTables)
	local box = MessageBox.new(boxType, args, cfgTables or mw.loadData('Módulo:mbox/configuração'))
	box:setParameters()
	return box:export()
end

function mt.__index(t, k)
	return function (frame)
		-- Coleta os argumentos como o Módulo:Arguments, porem mais leve
		local args = {}
		local parent = frame:getParent()
		if parent then
			for k, v in pairs(parent.args) do
				args[k] = v
			end
		end
		for k, v in pairs(frame.args) do
			args[k] = v
		end
		return t.main(k, args)
	end
end

return setmetatable(p, mt)