Documentation for this module may be created at Module:Quotations/doc

local m_scriptutils = require("Module:script utilities")
local m_utilities = require("Module:utilities")
local date_validation = require("Module:Quotations/date validation")
local loadModule = nil -- essentially a forward-declaration
local export = {}

function export.create(frame)
	local passed = true
	local results = ''
	passed, results = pcall(export.Create, frame)
	if passed then
		return results
	else
		return '<span class="wiktQuote" data-validation="red" style="display:none">'..results..'</span>'
	end
end

function export.Create(frame)
	--Set up our initial variables.
	local args = frame:getParent().args
	--Set empty tags to false
		for k,v in pairs(args) do
			if v == '' then
				args[k] = false
			end
		end
	local codedLangs = {['grc'] = true, ['la'] = true, ['xcl'] = true}
	local lang = args[1]; if lang == "" then lang = nil end
	lang = require("Module:languages").getLanguageByCode(lang)
	
	local ante = {}
	
	if codedLangs[lang:getCode()] then
		local m_langModule = loadModule(lang)
		ante = m_langModule.expand(args)
	else
		ante.author = args[2]
		ante.work = args[3]
		ante.ref = ''
		local dot = false
		for i=4, 10 do
			if args[i] then
				ante.ref = ante.ref..(dot and '.' or '')..args[i]
				dot = true
			else
				break
			end
		end
		for k,v in pairs(args) do
			if type(k) ~= 'number' then
				ante[k] = args[k]
			end
		end
	end
	local penult = {['year'] = '', ['author'] = '', ['work'] = '', ['ref'] = '',
		['notes'] = '', ['otherLines'] = '', ['s1'] = '', ['s2'] = '',
		['s3'] = '', ['s4'] = ''}
	local comma = false
	--Language specific modules are responsible for first line parameters.
	--Base formatting module will poll for other parameters,
	--pulling them only if the language module hasn't returned them.

	local otherOtherLineStuff = {'quote', 'transyear', 'transauthor', 'trans'}
	for i=1, 4 do
		ante[otherOtherLineStuff[i]] = ante[otherOtherLineStuff[i]] or args[otherOtherLineStuff[i]]
	end

	if not ante.code then
		penult.elAttr = ' class="wiktQuote" data-validation="white">'
	else
		penult.elAttr = ' class="wiktQuote" data-validation="'..ante.code..'">'
	end
	if ante.year then
		penult.year = "'''"..date_validation.main(ante.year).."'''"
		comma = true
	end
	if ante.author then
		penult.s1 = (comma and ', ' or '')
		penult.author = ante.author
		comma = true
	end
	if ante.work then
		penult.s2 = (comma and ', ' or '')
		penult.work = "''"..ante.work.."''"
		comma = true
	end
	if ante.ref then
		penult.s3 = (comma and ' ' or '')
		penult.ref = ante.ref
	end
	if ante.notes then
		penult.s4 = (comma and', ' or '')
		penult.notes = '('..ante.notes..')'
	end
	if ante.quote or ante.trans then
		penult.otherLines = '<dl><dd>'
		if ante.quote then
			local sc, scFix = m_utilities.detect_script(ante.quote, lang)
			penult.otherLines = penult.otherLines..m_scriptutils.tag_text(ante.quote, lang, sc)
		end
		if ante.trans then
			if ante.transyear or ante.transauthor then
				penult.otherLines = penult.otherLines..'<ul><li>'
				if ante.transyear then
					penult.otherLines = penult.otherLines.."'''"..ante.transyear.."''' translation"
				else
					penult.otherLines = penult.otherLines..'Translation'
				end
				if ante.transauthor then
					penult.otherLines = penult.otherLines..' by '..ante.transauthor
				end
				penult.otherLines = penult.otherLines..'<dl><dd>'..ante.trans..'</dd></dl></li></ul>'
			else
				if not ante.quote then
					penult.otherLines = penult.otherLines..ante.trans
				else
					penult.otherLines = penult.otherLines..'<dl><dd>'..ante.trans..'</dd></dl>'
				end
			end
		end
		penult.otherLines = penult.otherLines..'</dl></dd>'
	end
	local form = args['form'] or 'full'
	if form == 'full' then
		ultimate = '<div'..penult.elAttr..penult.year..penult.s1..penult.author..penult.s2..penult.work..penult.s3..penult.ref..penult.s4..penult.notes..penult.otherLines..'</div>'
	elseif form == 'inline' then
		ultimate = '<span'..penult.elAttr..penult.author..penult.s2..penult.work..penult.s3..penult.ref..'</span>'
	elseif form == 'work' then
		ultimate = '<span'..penult.elAttr..penult.work..penult.s3..penult.ref..'</span>'
	elseif form == 'ref' then
		ultimate = '<span'..penult.elAttr..penult.ref..'</span>'
	end
	return ultimate

end

function loadModule(lang)
	local sema = require('Module:Quotations/' .. lang:getCode())
	sema.library = sema.library or mw.loadData("Module:Quotations/" .. lang:getCode() .. "/data")

	sema.changeCode = sema.changeCode or function(color)
		if color == 'orange' then
			sema.code = 'orange'
		end
		if (color == 'yellow') and (sema.code == 'green') then
			sema.code = 'yellow'
		end
	end
	
	sema.reroute = sema.reroute or function(route)
		local temp = {}
		local data = sema.library.data
		
		for k,v in pairs(route) do
			temp[k] = sema.interpret(v)
		end
		
		for k, v in pairs(temp) do
			sema[k] = v
		end
		
		if sema.author and data[sema.author] then
			sema.aData = data[sema.author]
			if sema.work and sema.aData.works[sema.work] then
				sema.wData = sema.aData.works[sema.work]
			end
		end
	end
	
	sema.choose = sema.choose or function(choice, optionA, optionB)
		optionB = '' or optionB
		choice = sema.interpret(choice)
		if choice then
			decision = optionA
		else
			decision = optionB
		end
		decision = sema.interpret(decision)
		return decision
	end
	
	sema.isLetter = sema.isLetter or function(input)
		local isit = not tonumber(input)
		return isit
	end

	sema.lower = sema.lower or mw.ustring.lower

	sema.roundDown = sema.roundDown or function(period, verse)
		if not tonumber(verse) then
			sema.changeCode('orange')
		else
			local rounded = math.floor(verse/period) * period
			return rounded
		end
	end

	sema.chapterSelect = sema.chapterSelect or function(rubric, verse)
		verse = tonumber(verse)
		for k,v in pairs(rubric) do
			if v[1] <= verse and verse <= v[2] then
				return k
			end
		end
		sema.changeCode('orange')
	end

	sema.interpret = sema.interpret or function(item)
		if type(item) == 'string' then
			if string.sub(item, 1, 1) == '.' then
				address = string.sub(item, 2)
				local returnable = sema[address] or sema.library.data.Sundry and sema.library.data.Sundry[address]
				return returnable
			else
				return item
			end
		elseif type(item) == 'table' then
		--If it's a table, it's either a function call or a nested address.
			local presumedFunction = sema.interpret(item[1])
			if type(presumedFunction) == 'function' then
				local parameters = {}
				for i=2, 30 do
					local expanded = sema.interpret(item[i])
					if expanded then
						table.insert(parameters, expanded)
					else
						break
					end
				end
				local result = presumedFunction(unpack(parameters))
				return result
			else
				local nested = sema
				for i=1, 30 do
					local address = item[i]
					if address then
						nested = nested[address]
					else
						break
					end
				end
				return nested
			end
		else
			return item
		end
	end

	sema.convert = sema.convert or function(scheme, initiate)
		if type(scheme) == "table" then
			local initiate = tonumber(initiate) or initiate
			local converted = scheme[initiate]
			if converted == nil then
				sema.changeCode('orange')
			end
			return converted
		end
		if type(scheme) == "function" then
			local initiate = tonumber(initiate) or initiate
			local converted = scheme(initiate)
			if converted == nil then
				sema.changeCode('orange')
			end
			return converted
		end
		sema.changeCode('orange')
	end

	sema.numToRoman = sema.numToRoman or function(item)
		local j = tonumber(item)
		if (j == nil) then
			return item
		end
		if (j <= 0) then
			return item
		end

		local ints = {1000, 900,  500, 400, 100,  90, 50,  40, 10,  9,   5,  4,   1}
		local nums = {'M',  'CM', 'D', 'CD','C', 'XC','L','XL','X','IX','V','IV','I'}

		local result = ""
		for k = 1, #ints do
			local count = math.floor(j / ints[k])
			result = result .. string.rep(nums[k], count)
			j = j - ints[k]*count
		end
		return result
	end

	sema.period = sema.period or '.'

	sema.expand = sema.expand or function(args)
		--Instantiate our variables.
		local results = {}
		sema.code = 'green'
		local data = sema.library.data
		local ultimate = ''

		

		sema.author = args['author'] or args[2]
		sema.work = args['work'] or args[3]
		for i=1, 5 do
			local refName = 'ref'..tostring(i)
			local paramNumber = i + 3
			sema[refName] = args[refName] or args[paramNumber]
		end
		--Check if we've been given an author alias.
		if data.authorAliases[sema.author] then
			sema.author = data.authorAliases[sema.author]
		end

		if not data[sema.author] then
			sema.changeCode('yellow')
		else
			sema.aData = data[sema.author]
			if sema.aData.reroute then
				sema.reroute(sema.aData.reroute)
			else
				if sema.aData.aliases and sema.aData.aliases[sema.work] then
					sema.work = sema.aData.aliases[sema.work]
				end
				if not sema.aData.works or not sema.aData.works[sema.work] then
					sema.changeCode('yellow')
				else
					sema.wData = sema.aData.works[sema.work]
					if sema.wData.reroute then
						sema.reroute(sema.wData.reroute)
					end
				end
			end
		end

		--Load all author-level data.
		if sema.aData and sema.aData.aLink then
			results.author = '[[w:'..sema.aData.aLink..'|'..sema.author..']]'
		else
			results.author = sema.author
		end
		if sema.aData and sema.aData.year then
			results.year = sema.aData.year
		end

		--If the database has a link for the work, incorporate it.
		if not sema.wData or not sema.wData['wLink'] then
			results.work = sema.work
		else
			results.work = '[[w:'..sema.wData['wLink']..'|'..sema.work..']]'
		end
		--Some works have info which overrides the author-level info.
		if sema.wData then
			if sema.wData['year'] then
			results.year = sema.wData.year
			end
			if sema.wData['author'] ~= nil then
				results.author = sema.wData.author
			end
		end
		--The displayed reference is simply all the ref argument(s) joined with a period.
		sema.refDisplay = sema.ref1 and '' or false
		for i=1, 5 do
			local whichRef = 'ref'..tostring(i)
			if sema[whichRef] then
				if i > 1 then
					sema.refDisplay = sema.refDisplay..'.'
				end
				sema.refDisplay = sema.refDisplay..sema[whichRef]
			else
				break
			end
		end
		if args['through'] then
			args['thru'] = args['through']
		end
		if args['thru'] then
			sema.refDisplay = sema.refDisplay..'–'..args['thru']
		end
		--If the work is not in the database, or we don't have a source text link,
		--the ref is simply the display.
		--Otherwise, we have to create a reference link,
		--easily the most challenging function of this script.
		if sema.wData and sema.wData['rlFormat'] then
			sema.rlFormat = sema.aData['rlFormat'..tostring(sema.wData.rlFormat)]
			if sema.rlFormat then
				sema.rlTitle = sema.wData['rlTitle']
				sema.refLink = {}
				for i=1, 30 do
					local current = sema.rlFormat[i]
					if current then
						local results = sema.interpret(current)
						table.insert(sema.refLink, results)
					else
						break
					end
				end
				sema.refLink = table.concat(sema.refLink)
			end
		end
		if sema.refLink then
			results.ref = '[['..sema.refLink..'|'..sema.refDisplay..']]'
		else
			results.ref = sema.refDisplay
		end
		if args['notes'] then
			results.notes = args.notes
		end
		results.code = sema.code
		return results

	end

	return sema
end

return export