require('strict')

--[=[
This is a module to implement logic for [[MediaWiki:Proofreadpage index template]]
]=]

local p = {} -- p stands for package

local getArgs = require('Module:Arguments').getArgs
local messageBox = require('Module:Message box')
local language_name = require('Module:ISO 639').language_name
local tableTools = require('Module:TableTools')
local construct_defaultsort = require('Module:Header/sort')._construct_defaultsort

local cfg = mw.loadData('Module:Proofreadpage index template/config/sandbox')

local function construct_cat(cat)
	return "[[Category:" .. cat .. "]]"	
end

local function makeCatLink(cat, label)
	if label then
		return "[[:Category:" .. cat .. "|" .. label .. "]]"
	else
		return "[[:Category:" .. cat .. "]]"
	end
end

local function indicator(content, args)
	return mw.getCurrentFrame():extensionTag {
		name = 'indicator',
		content = content,
		args = args
	}
end

-- construct a basic "field" row

local function construct_field(id, content)
	if not id or not content then
		return nil
	end
	
	local id_cfg = cfg['headings'][id] or {}
	
	-- mapping of field ID to field properties
	local row = mw.html.create('tr')
		:attr('id', 'ws-index-' .. id .. '-row')
		:addClass('ws-index-row')
		:tag('th')
			:attr('id', 'ws-index-' .. id .. '-label')
			:addClass('ws-index-label')
			:attr('scope', 'row')
			:wikitext(id_cfg['txt'] or error('Can\'t find heading for ID: ' .. id))
			:done()
		:tag('td')
			:attr('id', 'ws-index-' .. id .. '-value')
			:addClass('ws-index-value')
			:wikitext(content)
			:allDone()
	
	return row
end

-- construct the title field (include volume after title if present)

local function construct_title_field(args)
	local content
	if args['Title'] and args['Volume'] then
		content = args['Title'] .. ', ' .. args['Volume']
	else
		content = args['Title'] or args['Volume']
	end
	
	return construct_field('title', content)
end

-- construct the source field

local function construct_source_field(args)
	local value
	local file = mw.title.new(mw.title.getCurrentTitle().text, 'File')
	
	if file.file.exists then
		-- if a file exists, link it
		value = '[[:' .. file.fullText .. '|' .. args['Source'] .. ']]'
	else
		-- otherwise just show the text
		value = args['Source']
	end
	
	return construct_field('source', value)
end

-- construct the status (proofread/validated/etc.) field

local function construct_status_field(args)
	-- mapping from status code to category and status text
	local sd = cfg['status'][args['Progress']]
	
	-- construct a span which might be classed "error"
	local progress_text = mw.html.create('span'):wikitext(sd['txt'])
	if sd['error'] then
		progress_text:addClass('error')
	end
	
	return construct_field('progress', makeCatLink(sd['cat'], tostring(progress_text)) .. construct_cat(sd['cat']))
end

-- construct the transclusion status field

local function construct_transclusion_status(args)
	local td = cfg['transclusion'][args['Transclusion']]
	if td then
		return construct_field('transclusion', makeCatLink(td['cat'], td['txt']) .. construct_cat(td['cat']))
	else
		return construct_field('transclusion', error('Unknown index status: ' .. args['Transclusion']))
	end
end

-- construct the validation field

local function construct_validated_date(args)
	if not args['Validation_date'] then
		return nil
	end
	
	local cat = 'Indexes validated in ' .. args['Validation_date']
	return construct_cat(cat)
		.. construct_field('validation_date', makeCatLink(cat, args['Validation_date']))
		.. indicator('[[File:Yes Check Circle.svg|15px|link=Category:' .. cat .. '|Validated in ' .. args['Validation_date'] .. '|alt=Validated index page.]]', {name = 'validated-index-date'})
end

-- construct external link fields

local function construct_url_gen(args, linktype)
	if not args[linktype] then
		return nil
	end
	
	return construct_field(linktype, '[' .. cfg["url_prefixes"][linktype] .. mw.uri.encode(args[linktype]) .. ' ' .. args[linktype] .. ']')
end

-- construct language categories

local function construct_language_cat(args)
	if not args['Language'] then
		return nil
	end
	
	local langs = mw.text.split(args['Language'], ',%s?', false)
	local cats = {}
	
	for _, lang in pairs(langs) do
		table.insert(cats, construct_cat('Index pages of works originally in ' .. language_name(lang, 'an unknown language')))
	end
	
	if #langs > 1 then
		table.insert(cats, construct_cat('Index pages of works originally in multiple languages'))
	end
	
	return table.concat(cats)
end

-- get the metadata fields.

local function _fields(args)
	local field_list = {
		construct_title_field(args),
		construct_field('author', args['Author']),
		construct_field('translator', args['Translator']),
		construct_field('editor', args['Editor']),
		construct_field('illustrator', args['Illustrator']),
		construct_field('year', args['Year']),
		construct_field('publisher', args['Publisher']),
		construct_field('place', args['Address']),
		construct_source_field(args),
		construct_status_field(args),
		construct_transclusion_status(args),
		construct_validated_date(args),
		construct_url_gen(args, 'OCLC'),
		construct_url_gen(args, 'DOI'),
		construct_field('volumes', args['Volumes']),
		construct_language_cat(args)
	}
	field_list = tableTools.compressSparseArray(field_list)
	return field_list
end

function p.fields(frame)
	return table.concat(_fields(getArgs(frame)))
end

-- construct wrapper for fields

local function _metadata(args)
	local metadatatable = mw.html.create('table')
		:attr('id', 'ws-index-metadata')
		:addClass('ws-index-metadata')
	
	local fields = _fields(args)
	for i = 1, #fields do
		if type(fields[i]) == 'string' then
			metadatatable:wikitext(fields[i])
		else
			metadatatable:node(fields[i])
		end
	end
	
	return metadatatable
end

function p.metadata(frame)
	return _metadata(getArgs(frame))
end

-- talk page message box

local function _talkremarks()
	local talkTitle = mw.title.getCurrentTitle().talkPageTitle
	local talkText = talkTitle.prefixedText
	
	-- if talk page exists, show message box
	-- if "Quick notes" section exists, transclude it
	if talkTitle.exists then
		return messageBox.main(
			'ombox',
			{
				type = 'content',
		    	text = 'Formatting guidelines specific to this work may have already been established. Please check [[' .. talkText .. '|the talk page]] for details.'
		    		.. mw.getCurrentFrame():callParserFunction('#lsth', talkText, 'Quick notes')
			}
		)
	end
	
	return ''
end

function p.talkremarks()
	return _talkremarks()
end

-- indicators

local function _indicators()
	local indicator_list = {}
	for k, v in pairs(cfg['indicators']) do
		table.insert(
			indicator_list,
			indicator(mw.ustring.format('[[%s|20px|link=%s|%s]]', v.image, v.link, v.caption), {name = v.name})
		)
	end
	return table.concat(indicator_list)
end

function p.indicators()
	return _indicators()
end

-- cover image

local function _cover(args)
	-- workaround for pagelist preview that tries to parse this template
	-- without any context and with missing parameters via the API
	if not args['Image'] then
		return ''
	end
	
	local function cover_file_link(file, attrs)
		return '[[' .. file .. '|250px|class=ws-cover ws-index-cover' .. (attrs or '') .. ']]'
	end
	
	-- if Image param is not a (page) number then this is an image-based Index
	-- so use the provided image as the cover image
	if not tonumber(args['Image']) then
		local imglink
		if mw.ustring.find(args['Image'], '^%[%[') then
			-- assume it's a full image specification
			imglink = args['Image']
		else
			-- assume it's a filename with or without File: prefix
			local file = mw.ustring.gsub(args['Image'], '^[Ff]ile:', '')
			imglink = cover_file_link('File:' .. file)
		end
		return imglink .. construct_cat('Image based indexes')
	end
	
	-- otherwise it's a DjVu/PDF-backed index
	-- so we fetch the cover image from a page in the (multipage) file
	local fileTitle = mw.title.makeTitle('File', mw.title.getCurrentTitle().text)
	if not fileTitle.file.exists then
		-- our associated file doesn't exist so we use a placeholder
		return cover_file_link('File:Placeholder book.svg', '|link=' .. fileTitle.prefixedText) .. construct_cat('Indexes with missing files')
	end
	
	-- file exists and we have a page number for the cover
	return cover_file_link(fileTitle.prefixedText, '|page=' .. tonumber(args['Image']))
end

function p.cover(frame)
	return _cover(getArgs(frame))
end

-- remarks

local function _remarks(args)
	if args['Remarks'] then
		-- TODO
		-- return mw.html.create('div'):addClass('ws-index-remarks ws-index-remarks-container'):wikitext(args['Remarks'])
		return mw.html.create('td'):attr('id', 'ws-index-remarks'):wikitext(args['Remarks'])
	else
		-- TODO
		-- return mw.html.create('div'):addClass('ws-index-remarks ws-index-remarks-container ws-index-remarks-empty')
		return mw.html.create('td'):attr('id', 'ws-index-remarks-empty')
	end
end

function p.remarks(frame)
	return _remarks(getArgs(frame))
end

-- sortkey

local function _sortkey(args)
	args.sortkey = args['Key'] or args.sortkey
	return construct_defaultsort(args)
end

function p.sortkey(frame)
	return _sortkey(getArgs(frame))
end

-- get the pagelist provided by PRP
-- PRP furnishes a finished rendered pagelist
-- so we just need to wrap it in a suitable container, add a legend, etc.

local function _pagelist(args)
	if type(args['Pages']) ~= 'string' then
		return mw.html.create('strong'):addClass('error'):wikitext('Pages parameter is missing')
	end
	
	-- main container
	local plcontainer = mw.html.create("div")
		:attr("id", "ws-index-pagelist-container") -- make it collapsible
		-- :addClass("ws-index-pagelist-container") TODO
		:addClass("mw-collapsible")
		-- the heading/legend
		:tag("div")
			:tag("strong"):wikitext("Pages"):done()
			--[=[ TODO
			:addClass("ws-index-pagelist-heading")
			:wikitext("Pages")
			]=]
			:wikitext(" ") -- Force a space between heading and legend.
			:tag("span")
				:attr("id", "ws-index-pagelist-legend")
				-- :addClass("ws-index-pagelist-heading-legend") TODO
				:wikitext("(key to [[Help:Page Status|Page Status]])")
				:done()
			:done()
		:tag("div") -- the pagelist itself
			:attr("id", "ws-index-pagelist")
			-- :addClass("ws-index-pagelist") TODO
			:addClass("index-pagelist") -- legacy support
			:addClass("mw-collapsible-content") -- make it collapsible
			:wikitext('\n' .. mw.text.trim(args['Pages']) .. '\n')
			:allDone()
	
	return plcontainer
end

-- assemble the overall structure
function p.main(frame)
	local args = getArgs(frame)
	
	-- set defaults
	for k, v in pairs(cfg['defaults']) do
		if args[k] == nil then
			args[k] = v
		end
	end
	
	-- the overall html structure into which each component is inserted
	local indexcontainer = mw.html.create('table')
		:attr('id', 'ws-index-container')
		:tag('tr')
			:tag('td')
				:attr('id', 'ws-index-main-cell')
				:tag('table')
					:attr('id', 'ws-index-main-table')
					:tag('tr') -- first row
						:tag('td')
							-- get the image to use as the cover image for this index
							:tag('div')
								:attr('id', 'ws-index-cover-container')
								:wikitext(_cover(args))
								:done()
							-- get metadata table
							:node(_metadata(args))
							:done()
						:done()
					:tag('tr') -- second row
						:tag('td')
							-- get pagelist
							:node(_pagelist(args))
							:done()
						:done()
					:done()
				:done()
			-- get remarks
			:node(_remarks(args))
				:allDone()
	
	--[=[ TODO
	local indexcontainer = mw.html.create('div')
		:addClass('ws-index-container')
		:tag('div')
			:addClass('ws-index-cover-container')
			:wikitext(_cover(args))
			:done()
		:tag('div')
			:addClass('ws-index-metadata-container')
			:node(_metadata(args))
			:done()
		:node(_pagelist(args))
		:node(_remarks(args))
	
	if tonumber(args["tmplver"]) == 42 then
		indexcontainer:addClass("ws-tng") -- Tag for "TNG" mode
	end
	]=]
	
	-- return the HTML structure and the accumulated categories
	return _indicators() .. _talkremarks() .. tostring(indexcontainer) .. _sortkey(args)
end

return p