local getArgs = require('Module:Arguments').getArgs
local p = {}

--possible book inputs
--(spaces, caps, and punctuation are stripped beforehand)
local book_aliases = {
	['1 Nephi'] = {'1nephi', '1ne', '1nep'},
	['2 Nephi'] = {'2nephi', '2ne', '2nep'},
	['3 Nephi'] = {'3nephi', '3ne', '3nep'},
	['4 Nephi'] = {'4nephi', '4ne', '4nep'},
	['Words of Mormon'] = {'wordsofmormon', 'wofm', 'wom'},
	['Jacob']   = {'jac'},
	['Enos']    = {'en'},
	['Jarom']   = {'jar'},
	['Omni']    = {'om'},
--	['Mosiah']  = {},
	['Alma']    = {'al'},
	['Helaman'] = {'hel', 'helam'},
	['Mormon']  = {'morm'},
	['Ether']   = {'eth'},
	['Moroni']  = {'moro'},
	['Doctrine and Covenants'] = {'doctrineandcovenants', 'd&c', 'dc'},
--	['Moses'] = {},
	['Abraham'] = {'abr'},
	['Joseph Smith–History'] = {'josephsmithhistory', 'jsh'},
	['Joseph Smith–Matthew'] = {'josephsmithmatthew', 'jsm'},
	['Articles of Faith'] = {'articlesoffaith', 'articleoffaith', 'aoff', 'aof'},
	['Official Declaration 1'] = {'officialdeclaration1', 'od1'},
	['Official Declaration 2'] = {'officialdeclaration2', 'od2'},
}

--these books only have one chapter, have to be handled differently
local no_chapters = {
	['Enos'] = true,
	['Jarom'] = true,
	['Omni'] = true,
	['Words of Mormon'] = true,
	['4 Nephi'] = true,
	['Joseph Smith–History'] = true,
	['Joseph Smith–Matthew'] = true,
	['Articles of Faith'] = true,
}

--pattern for the url of each site using _book etc.
-- (underscore then letters [a-z]) for variables
local url_path_tbl = {
	lds_org = 'https://www.churchofjesuschrist.org/study/scriptures/_book/_schap?lang=eng&id=_vrange#p_svers',
	coc_net = 'http://www.communityofchrist.net/Scriptures/'
}

--changes book name to use in url for each site, only if necessary
local site_book_tbl = {
	lds_org = {
		['1 Nephi']         = 'bofm/1-ne',
		['2 Nephi']         = 'bofm/2-ne',
		['Jacob']           = 'bofm/jacob',
		['Enos']            = 'bofm/enos',
		['Jarom']           = 'bofm/jarom',
		['Omni']            = 'bofm/omni',
		['Words of Mormon'] = 'bofm/w-of-m',
		['Mosiah']          = 'bofm/mosiah',
		['Alma']            = 'bofm/alma',
		['Helaman']         = 'bofm/hel',
		['3 Nephi']         = 'bofm/3-ne',
		['4 Nephi']         = 'bofm/4-ne',
		['Mormon']          = 'bofm/morm',
		['Ether']           = 'bofm/ether',
		['Moroni']          = 'bofm/moro',
		['Doctrine and Covenants'] = 'dc-testament/dc',
		['Moses']                = 'pgp/moses',
		['Abraham']              = 'pgp/abr',
		['Joseph Smith–Matthew'] = 'pgp/js-m',
		['Joseph Smith–History'] = 'pgp/js-h',
		['Articles of Faith']    = 'pgp/a-of-f',
		['Official Declaration 1'] = 'od/1',
		['Official Declaration 2'] = 'od/2',
	}
}

local function lookupTableReplace(arg, lookupTable)
	local abbrTitle = string.lower(arg)
	local fullTitle = arg
	for k, v in pairs(lookupTable) do
		if type(v) == "table" then
			for i = 1, #v do
				if v[i] == abbrTitle then
					fullTitle = k
					break --this is optional
				end
			end
		end
	end
	return fullTitle
end

local function processBookName(arg)
	arg = arg:gsub('[-–— .]', '') -- removing dashes, spaces, periods
	arg = lookupTableReplace(arg, book_aliases)
	arg = arg:gsub("^%l", string.upper) -- capitalize first letter
	return arg
end

local function prepareURL(book, ref, site)
	local split_ref = mw.text.split(ref, '[-–—]')   --split the ref into the part before and after the dash/hyphen
	local s_ref = mw.text.split(split_ref[1], '%p') --any punctuation can be used to separate chapter from verse
	local e_ref = split_ref[2] or split_ref[1]
	e_ref = mw.text.split(e_ref, '%p')
	for i, v in ipairs(s_ref) do s_ref[i] = v:gsub('%D', '') end  --remove any non-numeric character (such as f)
	for i, v in ipairs(e_ref) do e_ref[i] = v:gsub('%D', '') end

	local e_chap, e_vers, s_chap, s_vers
	local chapter_only = not s_ref[2]
	if no_chapters[book] then
		chapter_only = false
		s_chap = 1
		s_vers = s_ref[2] or s_ref[1] or 1   --verse 3 can be specified as "3" or "1:3"
		e_chap = 1
		e_vers = e_ref[2] or e_ref[1] or 1
	else
		s_chap = s_ref[1] or 1
		s_vers = s_ref[2] or 1
		if e_ref[2] or not s_ref[2] then     --chapter-chapter or chapter(:verse)?-chapter:verse
			e_chap = e_ref[1] or s_chap
		else                                 --chapter:verse-verse
			e_chap = s_chap
		end
		e_vers = e_ref[2] or e_ref[1] or s_vers
	end

	if site == 'lds_org' then
		book = site_book_tbl[site][book] or book
	end

	local v_range
	if e_chap == s_chap and e_vers == s_vers then
		v_range = s_vers
	else
		v_range = s_vers .. '-' .. e_vers
	end

	local url_path = url_path_tbl[site]
	--get the components into the url
	local url = url_path:gsub('_%l+', {
									_book    = book,
									_schap   = s_chap,
									_svers   = s_vers,
									_echap   = e_chap,
									_evers   = e_vers,
									_vrange  = v_range,
									_version = version,
							})
		
	-- cut off verse details
	if (site == 'lds_org' and chapter_only == true) then
		local tmp = mw.text.split(url, '&')
		url = tmp[1]
	end
	return url
end

local wikipedia_pages_verbatim = {
	['Words of Mormon'] = true,
	['Doctrine and Covenants'] = true,
	['Joseph Smith–Matthew'] = true,
	['Joseph Smith–History'] = true,
}

local wikipedia_pages_other = {
	['1 Nephi'] = 'First Nephi',
	['2 Nephi'] = 'Second Nephi',
	['3 Nephi'] = 'Third Nephi',
	['4 Nephi'] = 'Fourth Nephi',
	['Mormon'] = 'Book of Mormon (Mormon\'s record)',
	['Articles of Faith'] = 'Articles of Faith (Latter Day Saints)',
}

local function wikipediaLink(book)
	if (wikipedia_pages_verbatim[book]) then
		return '[[' .. book .. ']]'
	end
	if (wikipedia_pages_other[book]) then
		return '[[' .. wikipedia_pages_other[book] .. '|' .. book .. ']]'
	end
	return '[[Book of ' .. book .. '|' .. book .. ']]'
end

local function refLinkText(ref)
	if (ref:match("^%a")) then -- if begins with letter
		return processBookName(ref)
	end
	return ref:gsub('[-–—]', '–')
end

function p.main(frame)
	local args = getArgs(frame)

	local book = args[1]
	local ref = args[2]
	local version = 'lds_org' -- args[3] or 'lds_org'
		
	book = processBookName(book)
	local url = prepareURL(book, ref, version)

	return wikipediaLink(book)
			.. ' [' .. url .. ' ' .. refLinkText(ref) .. ']'
end

return p