-- Primary module authorship: Chernorizets (original Bulgarian syllabification code)
-- Port to Lua: Kiril Kovachev
-- Adaptation to Toki Pona: Kiril Kovachev
-- 17 April 2024.

local export = {}

local substring = mw.ustring.sub
local rsubn = mw.ustring.gsub
local rsplit = mw.text.split
local U = mw.ustring.char
local lang = require("Module:languages").getByCode("tok")
local script = require("Module:scripts").getByCode("Latn")

local hvowels_c = "[aioeu]"

local HYPH = U(0x2027)
local BREAK_MARKER = "."

-- version of rsubn() that discards all but the first return value
local function rsub(term, foo, bar)
	local retval = rsubn(term, foo, bar)
	return retval
end

local function char_at(str, index)
	return substring(str, index, index)
end

local function count_vowels(word)
	local _, vowel_count = mw.ustring.gsub(word, hvowels_c, "")
	return vowel_count
end

local function is_vowel(ch)
	for _, v in pairs{"a", "e", "i", "o", "u"} do
		if v == ch then
			return true
		end
	end
	return false
end

---- Main syllabification code
-- word: the word being scanned
-- left/right vowels: integers
local function find_next_syllable_onset(word, left_vowel, right_vowel)
    local n_cons = right_vowel - left_vowel - 1

    -- No consonants - syllable starts on rightVowel
    if n_cons == 0 then return right_vowel end

    -- Single consonant between two vowels - starts a syllable
    if n_cons == 1 then return left_vowel + 1 end

    -- Two ("or more") consonants between the vowels.
    -- In Toki Pona, the phonotactics only allow this if the first syllable
    -- ends in a nasal and the second begins in a consonant, so there can only
    -- ever be two consonants, and the sonority break occurs between the two
    -- consonants (so just add 2 to the left vowel).
	local sonority_break = left_vowel + 2

    return sonority_break
end

-- Returns a table of strings (list)
local function syllabify_poly(word)
    local syllables = {}

    local prev_vowel = -1
    local prev_onset = 1;
    
    for i = 1, mw.ustring.len(word) do
	    if is_vowel(mw.ustring.lower(char_at(word, i))) then
	        -- A vowel, yay!
	        local should_skip = false
	        if prev_vowel == -1 then
	            prev_vowel = i
	            should_skip = true;
	        end

	        -- This is not the first vowel we've seen. In-between
	        -- the previous vowel and this one, there is a syllable
	        -- break, and the first character after the break starts
	        -- a new syllable.
	        if not should_skip then
		        local next_onset = find_next_syllable_onset(word, prev_vowel, i)
		        table.insert(syllables, substring(word, prev_onset, next_onset - 1))
		        prev_vowel = i
		        prev_onset = next_onset
			end
	    end
    	
    end

    -- Add the last syllable
    table.insert(syllables, substring(word, prev_onset))

    return syllables
end

function export.syllabify_word(word)
    if mw.ustring.len(word) == 0 then return {} end;

    local n_vowels = count_vowels(word)
    local syllables = n_vowels <= 1 and {word} or syllabify_poly(word)

    return table.concat(syllables, HYPH)
end

function export.syllabify(term)
	local words = rsplit(term, " ")

	local out = {}
	for _, word in pairs(words) do
		table.insert(out, export.syllabify_word(word))	
	end
	return table.concat(out, " ")
end

function export.show_syllabification(frame)
	local params = {
		[1] = {},
	}
	
	local title = mw.title.getCurrentTitle()
	local args = require("Module:parameters").process(frame:getParent().args, params)
	local term = args[1] or title.nsText == "Template" and "sitelen" or title.subpageText

	local syllabification = export.syllabify(term)
	local syllables = rsplit(syllabification, HYPH)
	return require("Module:hyphenation").format_hyphenations(
		{ 
			lang = lang,
			hyphs = { { hyph = syllables } },
			sc = script,
			caption = "Hyphenation",
		}
	)
end

return export