local export = {}

local function remove_macron_breve(text)
	return mw.ustring.toNFD(text):gsub("\204[\132\134]", "")
end

local function link(text)
	return '<span class="Polyt" lang="grc">[['
		.. remove_macron_breve(text)
		.. '#Ancient Greek|' .. text .. ']]</span>'
end

local function anchor_link(text)
	return '<span class="Polyt" lang="grc">[[#'
		.. text
		.. '|' .. text .. ']]</span>'
end

local function tag(text)
	return '<span class="Polyt" lang="grc">' .. text .. '</span>'
end

local function individual_anchor(text)
	return '<span id="' .. text .. '"></span>'
end

local function make_anchors(text)
	if text:find(",") then
		local anchors = {}
		for word in text:gmatch("[^, ]+") do
			table.insert(anchors, individual_anchor(word))
		end
		return table.concat(anchors)
	else
		return individual_anchor(text)
	end
end

local function count(text, pattern, bytepattern)
	local _, count = (bytepattern and string.gsub or mw.ustring.gsub)(text, pattern, "")
	return count
end

local function get_length(text)
	return count(text, "[%z\1-\127\194-\244][\128-\191]*", true)
end

local U = require("Module:string/char")
local acute = U(0x301)
local grave = U(0x300)
local circumflex = U(0x342)

local function check(text)
	if get_length(text) == 1 then
		return ""
	end
	
	local errors = {}
	text = mw.ustring.toNFD(text)
	
	if count(text, grave) > 0 then
		table.insert(errors, "Grave found!")
	end
	
	local accent_count = count(text, "[" .. acute .. circumflex .. "]")
	if accent_count > 1 then
		table.insert(errors, "Too many accents!")
	elseif accent_count == 0 and text:sub(-1) ~= "-" then
		table.insert(errors, "No accent!")
	end
	
	if errors[1] then
		return ' <span style="color: goldenrod;">' .. table.concat(errors, " ") .. '</span>'
	else
		return ""
	end
end

-- For [[Appendix:Ancient Greek endings]]; using individual templates is way too slow.
function export.link_Greek(frame)
	local text = frame:getParent().args[1]
	if text then
		local data = mw.loadData "Module:grc-link/data"
		local fun = require "Module:fun"
		
		local macron = mw.ustring.char(0x306)
		local breve = mw.ustring.char(0x304)
		local subscript = mw.ustring.char(0x345)
		local replacements = {
			[macron] = "a", -- macron
			[breve] = "b", -- breve
			[subscript] = "c", -- iota subscript
		}
		
		local get_sort_value = fun.memoize(function (suffix)
			suffix = mw.ustring.gsub(mw.ustring.toNFD(suffix),
				"[" .. macron .. breve .. subscript .. "]",
				replacements)
			
			return suffix
		end)
		
		local entries = {}
		local i, j, entry, pos
		
		while true do
			i, j, entry = text:find("(...-)\n;", pos)
			
			if i == nil then
				table.insert(entries, text:sub(pos or 1))
				break
			end
			
			table.insert(entries, entry)
			pos = j - 1
		end
		
		return (table.concat(
			fun.map(
				-- Automatically list other suffixes that share the same last
				-- few letters, using [[Module:User:Erutuon/10/data]].
				function (entry)
					if entry:find("\n;") then
						local shares_ending
						
						for headword in entry:match("\n; %[%[([^%]]+)%]%]"):gmatch("%-[^%s,]+") do
							if data.shares_ending[headword] then
								shares_ending = shares_ending or {}
								for _, suffix in ipairs(data.shares_ending[headword]) do
									table.insert(shares_ending, "[[" .. suffix .. "]]")
								end
							end
						end
						
						if shares_ending then
							table.sort(
								shares_ending,
								function (ending1, ending2)
									return get_sort_value(ending1) < get_sort_value(ending2)
								end)
							
							return entry .. "\n: See also " .. table.concat(shares_ending, ", ") .. "."
						end
					end
					
					return entry
				end,
				entries))
			:gsub(
				"(\n?;? ?)%[%[((%-?)[^%]]+)%]%]",
				function (preceding, link_text, hyphen)
					if link_text:find("[\206\207\225]") then -- leading bytes for Greek and Coptic block and leading byte for Greek Extended block
						if preceding == "\n; " then
							return preceding .. make_anchors(link_text) .. tag(link_text)
						else
							if hyphen == "-" then
								return preceding .. anchor_link(link_text)
							else
								return preceding .. link(link_text) .. check(link_text)
							end
						end
					end
				end)
			:gsub(
				"(&[^;]+;)(&[^;]+;)",
				'<span class="Polyt" lang="grc">[[%2|%1%2]]</span>')
			:gsub("\n$", ""))
	end
end

-- Used in [[User:Erutuon/Classical Greek prose]].
function export.link_and_transliterate(frame)
	local text = frame.args[1] or frame:getParent().args[1]
	if not text then
		return
	end
	
	local open_paren = ' <span class="mention-gloss-paren annotation-paren">(</span><span class="tr Latn" xml:lang="grc-Latn" lang="grc-Latn">'
	local close_paren = '</span><span class="mention-gloss-paren annotation-paren">)</span>'
	local column_value = '10em'
	
	return '<div style="-moz-columns: ' .. column_value .. '; -webkit-columns: ' .. column_value .. '; columns: ' .. column_value .. ';">'
		.. text
			:gsub(
				"%[%[([^%]]+)%]%]",
				function (link_text)
					return link(link_text)
						.. open_paren .. (require("Module:languages").getByCode("grc"):transliterate(link_text)) .. close_paren
				end)
			:gsub("\n$", "")
		.. '</div>'
end


function export.strongs_list(frame)
	local text = frame.args[1]
	
	local function strip_diacritics(word)
		return mw.ustring.toNFD(word):gsub("[\204\205][\128\129\136\147\148\130\133]", "")
		-- U+0300 \204\128 COMBINING GRAVE ACCENT
		-- U+0301 \204\129 COMBINING ACUTE ACCENT
		-- U+0308 \204\136 COMBINING DIAERESIS
		-- U+0313 \204\147 COMBINING COMMA ABOVE
		-- U+0314 \204\148 COMBINING REVERSED COMMA ABOVE
		-- U+0342 \205\130 COMBINING GREEK PERISPOMENI
		-- U+0345 \205\133 COMBINING GREEK YPOGEGRAMMENI
	end
	
	local function get_first_letter(word)
		return mw.ustring.upper(strip_diacritics(word:match("^%-?([%z\1-\127\194-\244][\128-\191]*)")))
	end
	
	local prev_letter
	
	return text:gsub(
		"%f[^\n%z]([^\t\n]+)\t([^\t\n]+)",
		function(number, word)
			local header = ""
			local letter = get_first_letter(word)
			if letter ~= prev_letter then
				if number ~= "1" then
					header = "</ul>\n\n"
				end
				header = header .. ('===%s &ndash; %04d===\n<ul class="plainlinks" style="column-width: 12em;">\n'):format(letter, tonumber(number))
				prev_letter = letter
			end
			return header
				.. '<li> [https://www.blueletterbible.org/lexicon/g' .. number .. "/wlc G" .. number
				.. "]: " .. link(word)
				.. (word:find(" ", 1, true) and ("<br>(" .. word:gsub("[^ ]+", link) .. ")") or "")
		end) .. "</ul>"
end


return export