local p = {}
local getArgs = require('Module:Arguments').getArgs
local mm = require('Module:Math')
local yesno = require('Module:Yesno')
local lang = mw.getContentLanguage()
local currentyear = tonumber(os.date('%Y'))

local function formatnum(num)
	return lang:parseFormattedNumber(num) and lang:formatNum(lang:parseFormattedNumber(num)) or num
end

local function moduleExists(t)
	return package.loaded[t] or package.loaders[1](t) or package.loaders[2](t) or false
end

local function checkYearRangeForError(index, argstartyear, argendyear, datastartyear, dataendyear)
	local errorParams = {}
	local msg

	if argstartyear < datastartyear then
		msg = "{{para|start_year|'''" .. argstartyear
				.. "'''}} (parameter 3) is lower than the earliest available year ('''" .. datastartyear .. "''')"
				.. " in index \"'''" .. index .. "'''\""
		table.insert(errorParams, msg)
	end

	if argstartyear > argendyear then
		if argstartyear ~= currentyear then
			msg = "{{para|start_year|'''" .. argstartyear .. "'''}} (parameter 3) is greater than "
-- ToDo Fix
				.. "{{para|end_year|'''" .. argendyear .. "'''}} (parameter 4) "
				.. "the latest available year ('''" .. dataendyear .. "''') in index \"'''" .. index .. "'''\""
			table.insert(errorParams, msg)
		end
	end

	-- 4c: [parameter 4/end_year] greater than [parameter 1/index] highest year
	if argendyear > dataendyear then
		msg = "{{para|end_year|'''" .. argendyear
				.. "'''}} (parameter 4) is greater than the latest available year ('''" .. dataendyear .. "''') "
				.. "in index \"'''" .. index .. "'''\""
		table.insert(errorParams, msg)
	end

	if next(errorParams) ~= nil then
		return mw.text.listToText(errorParams, ', & #32;', '&#32; and &#32;') .. '.'
	end

	return nil
end

function p._parse(index, value, startyear, endyear, round)
	local dataset = mw.loadData('Module:Inflation/' .. index)

	-- Dataset is not properly configured
	if dataset.data == nil or dataset['start_year'] == nil or dataset.default == nil then
		return {inflatedValue = nil, endyear = nil, error = 'error'}
	end

	startyear = tonumber(startyear)

	if endyear ~= nil then
		endyear = tonumber(endyear)
	else
		endyear = tonumber(dataset.default)
	end

	-- Check if a parameter is out of calculable bounds
	local er = checkYearRangeForError(index, startyear, endyear, dataset['start_year'], dataset.default)

	if er ~= nil then
		return {inflatedValue = nil, endyear = nil, error = er}
	end

	-- ToDo Throw error
	-- dataset.data[dataset.default]

	

	local m
	if currentyear == startyear then
		m = 1
	else
		m = mm._divide(dataset.data[endyear], dataset.data[startyear])
	end

	inflatedValue = lang:parseFormattedNumber(value) * m
	inflatedValue = mm._round(inflatedValue, round)

	return {inflatedValue = inflatedValue, endyear = endyear, error = nil}
end

local function checkForError(index, value, startyear, endyear, round)
	-- Check if required parameters are set
	local errorParams = {}

	if not index then
		table.insert(errorParams, '{{para|index}} (parameter 1)')
	end

	if not value then
		table.insert(errorParams, '{{para|value}} (parameter 2)')
	end

	if not startyear then
		table.insert(errorParams, '{{para|start_year}} (parameter 3)')
	end

	if next(errorParams) ~= nil then
		return mw.text.listToText(errorParams, ', & #32;', '&#32; and &#32;') .. 'must be specified.'
	end

	-- Check if dataset exists
	if not moduleExists('Module:Inflation/' .. index) then
		return "{{para|index|'''" .. index .. "'''}} (parameter 1) not a recognized index."
	end

	-- Check if numeric parameters have non-numeric data
	errorParams = {}

	if lang:parseFormattedNumber(value) == nil then
		table.insert(errorParams, "{{para|value|'''" .. value .. "'''}} (parameter 2)")
	end

	if lang:parseFormattedNumber(startyear) == nil then
		table.insert(errorParams, "{{para|start_year|'''" .. startyear .. "'''}} (parameter 3)")
	end

	if lang:parseFormattedNumber(endyear) == nil then
		table.insert(errorParams, "{{para|end_year|'''" .. endyear .. "'''}} (parameter 4)")
	end

	if lang:parseFormattedNumber(round) == nil then
		table.insert(errorParams, "{{para|r|'''" .. round .. "'''}}")
	end

	if next(errorParams) ~= nil then
		return '[[NaN]], check parameters for non-numeric data:'
				.. mw.text.listToText(errorParams, ', & #32;', '&#32; and &#32;')
				.. '.'
	end

	return nil
end

local function formatResult(originalValue, endyear, inflatedValue, fmt, cursign, orig)
	local result = inflatedValue

	if fmt == 'eq' then
		result = 'equivalent to ' .. cursign .. formatnum(inflatedValue) .. ' in '

		if currentyear == startyear then
			result = result .. currentyear
		else
			result = result .. endyear
		end

		if orig then
			result = cursign .. originalValue .. ' (' .. result .. ')'
		end
	end

	if fmt == 'c' then
		result = formatnum(inflatedValue)
	end

	return result
end

local function formatError(er, nocat)
	er = "<span class=\"error\">Error when using {{tl|'''Inflation'''}}: " .. er .. "</span>"

	if not nocat then
		er = er .. '{{main other|[[Category:Pages with errors in inflation template]]}}'
	end

	return er
end

function p.main(frame)
	local args = getArgs(frame)
	if not frame then frame = mw.getCurrentFrame() end

	local index = args[1] or args.index --or frame:getParent().args[1] or frame:getParent().args.index
	local value = args[2] or args.value --or frame:getParent().args[2] or frame:getParent().args.value
	local startyear = args[3] or args['start_year'] --or frame:getParent().args[3] or frame:getParent().args['start_year']
	local endyear = args[4] or args['end_year'] --or frame:getParent().args[4] or frame:getParent().args['end_year']
	local round = args.r or 0
	local fmt = args.fmt or 'raw'
	local orig = yesno(args.orig) or false
	local cursign = args.cursign or '$'
	local nocat = yesno(args.nocat) or false

	local error = checkForError(index, value, startyear, endyear, round)
	if error ~= nil then
		return frame:preprocess(formatError(error, nocat))
	end

	local parsed = p._parse(index, value, startyear, endyear, round)

	if parsed.error ~= nil then
		return frame:preprocess(formatError(parsed.error, nocat))
	end

	return formatResult(value, parsed.endyear, parsed.inflatedValue, fmt, cursign, orig)
end

return p