local export = {}

local lang = require('Module:languages').getByCode('es')
local entry_data = require('Module:es-conj/data/entry_data')
local paradigms = require('Module:es-conj/data/paradigms')
local json = require('Module:Json')

local ulen = mw.ustring.len
local usub = mw.ustring.sub
local ufind = mw.ustring.find
local ugsub = mw.ustring.gsub

local pronouns = {[ "me"] = {7,14,20,26,32,38,45,51,57},
				  [ "te"] = {8,15,21,27,33,39,46,52,58,69},
				  [ "se"] = {9,40,64,10,13,16,19,22,25,28,31,34,37,41,44,47,50,53,56,59,62,70,73},
				  ["nos"] = {11,17,23,29,35,42,48,54,60,71},
				  [ "os"] = {12,18,24,30,36,43,49,55,61,72}}

local vowels = {["a"]="á",
				["e"]="é",
				["i"]="í",
				["o"]="ó",
				["u"]="ú"}
local stressed_vowels = {
				["á"]="a",
				["é"]="e",
				["í"]="i",
				["ó"]="o",
				["ú"]="u"}

local function to_boolean(val)
	if type(val) == 'string' then
		val = mw.text.trim(val)
	end
	return not (not val or val == "" or val == "0" or val == "no" or val == "n" or val == "false")
end

function unpack_args(frame)
	local args = frame.args
	if (not args[1]) then
		args = frame:getParent().args
	end
	
	local ending = args[1]
    local pattern = args[2]
    local ref = to_boolean(args['ref'])
	local json = to_boolean(args['json'])
	local combined = to_boolean(args['combined'])
	local json_combined = to_boolean(args['json_combined'])
	
    local stems = {}
    local i = 3
    while args[i] do
   		stems[i-2] = args[i]
   		i = i + 1
    end
    return {ending, pattern, stems, ref, json, combined, json_combined}
end

function strip_accents(form)
	if form then
		return (mw.ustring.gsub(form, ".", stressed_vowels):gsub(",+", ","))
	end
end

function create_accented_form(form, ua_disyllabic)
	-- create accented stem for reflexive verbs
	
	-- split by commas if more than 1 form is given
	if form then
		if string.find(form, ',') then
			local result_t = {}
			for term in string.gmatch(form, '[^,]+') do
				table.insert(result_t, create_accented_form(term, ua_disyllabic))
			end
			return table.concat(result_t, ',')
		end

		local result = form
		
		-- c: index of current character
		local c = ulen(result)
		local vowel_count = 0
		
		while (c >= 0) do
			local v1 = usub(result, c, c)
			
			if vowels[v1] then
				vowel_count = vowel_count + 1
			end
			
			-- 'ue' or 'ua' count as 1 vowel, accent goes on e or a
			if c ~= ulen(result) then
				local v2 = usub(result, c+1, c+1)
				if (v1 == 'u' and v2 == 'e') then
					vowel_count = vowel_count - 1
				end
				if (not ua_disyllabic) then
					if (v1 == 'u' and v2 == 'a') then
						vowel_count = vowel_count - 1
					end
				end
			end
			
			-- 'ai' or 'oi' counts as 1 vowel, accent goes on a or o
			if (c ~= 1) then
				local v3 = usub(result, c-1, c-1)
				if (v3 == 'a' and v1 == 'i') then
					vowel_count = vowel_count - 1
				end
				if (v3 == 'o' and v1 == 'i') then
					vowel_count = vowel_count - 1
				end
			end
		
			if vowel_count == 3 then
				return usub(result, 1, c-1)..vowels[v1]..usub(result, c+1, ulen(result))
			end
			c = c - 1
		end
	end

	return form

end

local function makeLink(term, face, allow_self_link)
    local m_links = require('Module:links')
    local alt, sc, id, annotations = nil, nil, nil, nil
    
    if string.find(term, ',') then
    	local result = {}
    	local c = 1
    	local found = {}
    	for i in string.gmatch(term, '[^,]+') do
    		if not found[i] then
    			found[i] = true
	    		result[c] = m_links.full_link({lang = lang, term = i}, face, allow_self_link)
    			c = c + 1
    		end
		end
		return table.concat(result, ', ')
	end
	
    return m_links.full_link({lang = lang, term = term}, face, allow_self_link)
end

function decorate_inflections(inflections, ref, defective, patterns)
	local defective_t = {}
	
	if defective then
		for k1,v1 in pairs(defective) do
			defective_t[v1] = true
		end
	end

	local result = {}
	for k1,v1 in pairs(inflections) do
		if not defective_t[k1] then
			result[k1] = makeLink(v1, nil, true)
		else
			result[k1] = "<div style=\"color:#aaa\">" .. v1 .. "</div>"
		end
	end
	
	if ref then
		for k1,v1 in pairs(pronouns) do
			for k2,v2 in pairs(v1) do
				if result[v2] then
					if not defective_t[v2] then
						result[v2] = k1.." "..result[v2]
					else
						result[v2] = "<div style=\"color:#aaa\">" .. k1 .. "</div> " .. result[v2]
					end
					
				end
			end
		end
	end
	
	return result
end

function deepcopy(orig)
    local orig_type = type(orig)
    local copy
    if orig_type == 'table' then
        copy = {}
        for orig_key, orig_value in next, orig, nil do
            copy[deepcopy(orig_key)] = deepcopy(orig_value)
        end
        setmetatable(copy, deepcopy(getmetatable(orig)))
    else -- number, string, boolean, etc
        copy = orig
    end
    return copy
end

function export.json_raw(frame)
	local args = unpack_args(frame)
	local forms = export.inflect(args, {}, false)
	local result = {}
	
	for i, form in pairs(forms) do
		if not result[form] then
			result[form] = {}
		end
		for j, def in pairs(entry_data.data[i]) do
			table.insert(result[form], def)
		end
	end
	
	return json.jsonValueFromValue(result)
	
end

function export.json_wikt_forms(frame)
	local args = unpack_args(frame)
	local forms = export.inflect(args, {}, false)
	local ending = args[1]
	local pattern = args[2]
	
	local pattern_data = deepcopy(require("Module:es-conj/data/"..ending))
	if pattern ~= ending then
		pattern_data = deepcopy(require("Module:es-conj/data/"..ending.."/"..pattern))
	end

	local defective = pattern_data["defective"]
	if not defective then
		defective = {}
	end
	
	local defective_t = {}
	for k1,v1 in pairs(defective) do
		defective_t[v1] = true
	end
	
	local infinitive = forms[1]
	local ending = args[1]
	local result = {}
	
	for i, form in pairs(forms) do
		if not defective_t[i] then
			if (form ~= infinitive) then
				 for form_s in string.gmatch(form, '[^,]+') do
				 	local form_s_t = mw.text.trim(form_s)
					if not result[form_s_t] then
						result[form_s_t] = {}
					end
					for j, def in pairs(entry_data.data[i]) do
						table.insert(result[form_s_t], entry_data.make_template(def, ending, infinitive))
					end
				end
			end
		end
	end
	
	return json.jsonValueFromValue(result)	
end

function export.json_paradigms(frame)
	return json.jsonValueFromValue(paradigms)
end

function export.inflect(args, categories, toggle_decorate)

	table.insert(categories, "[[Category:西班牙語動詞]]")
	
	local ending = args[1]
	local pattern = args[2]
	local stems = args[3]
	local ref = args[4]
	
	local ending_data = deepcopy(require("Module:es-conj/data/"..ending))
	local pattern_data = deepcopy(require("Module:es-conj/data/"..ending))
	if pattern ~= ending then
		pattern_data = deepcopy(require("Module:es-conj/data/"..ending.."/"..pattern))
	end

	local description = pattern_data["description"]
	local defective = pattern_data["defective"]
	local unstressed = pattern_data["unstressed"] or {}
	
	if not defective then
		defective = {}
	else
		table.insert(categories, "[[Category:缺少變位形式的西班牙語動詞]]")
	end
	
	if ref then
		table.insert(categories, "[[Category:西班牙語自反動詞]]")
		for k1,v1 in pairs(ending_data["ref"]) do
			ending_data["patterns"][k1] = v1
		end
		
		if (pattern_data["ref"]) then
			for k1,v1 in pairs(pattern_data["ref"]) do
				pattern_data["patterns"][k1] = v1
			end
		end
		
	end
	
	if pattern_data["replacement"] then
		for k1,v1 in pairs(pattern_data["replacement"]) do
			for k2,v2 in pairs(ending_data["patterns"]) do
				ending_data["patterns"][k2] = ugsub(ending_data["patterns"][k2], k1, v1)
			end
		end
	end

	for k1,v1 in pairs(pattern_data["patterns"]) do
		
		if v1 == '-' then
			ending_data["patterns"][k1] = nil
		else
			if ending_data["patterns"][k1] ~= v1 then
				if entry_data.data[k1] then
					for k2,v2 in pairs(entry_data.data[k1]) do
						tense = v2.tense
						mood = v2.mood
						if mood ~= 'infinitive' then
							if tense then
								table.insert(categories, "[[Category:".. mood .. tense .. "不規則的西班牙語動詞]]") 
							else
								table.insert(categories, "[[Category:".. mood .. "不規則的西班牙語動詞]]") 
							end
						end
					end
				end
				ending_data["patterns"][k1] = v1
			end
		end
	end

	for k1,v1 in pairs(ending_data["patterns"]) do
		for k2,v2 in pairs(stems) do
			ending_data["patterns"][k1] = ugsub(ending_data["patterns"][k1], k2, v2)
		end
	end

	if ref then
		if not unstressed[63] then
			ending_data["patterns"][63] = create_accented_form(ending_data["patterns"][63], false)
		end
		if not unstressed[65] then
			ending_data["patterns"][65] = create_accented_form(ending_data["patterns"][65], false)
		end
		if not unstressed[68] then
			ending_data["patterns"][68] = create_accented_form(ending_data["patterns"][68], false)
		end
	end
	
	for i=98,121 do
		ending_data["patterns"][i] = create_accented_form(ending_data["patterns"][i], false)
	end

	for i=146,157 do
		ending_data["patterns"][i] = create_accented_form(ending_data["patterns"][i] , false)
	end

	if not toggle_decorate then
		return ending_data["patterns"]
	end
	
	-- detect redlinks
	local defective_t = {}
	for k1,v1 in pairs(defective) do
		defective_t[v1] = true
	end
	for k1,v1 in pairs(ending_data["patterns"]) do
		if (k1 <= 73) then
			if not defective_t[k1] then
				
				for form_s in string.gmatch(v1, '[^,]+') do
					local form_s_t = mw.text.trim(form_s)
					local link_object = mw.title.new(form_s_t) or nil
					if (link_object) then
						local link_id = link_object.id
						if link_object.id == 0 then
							table.insert(categories, "[[Category:變位表中含有紅鏈的西班牙語動詞]]")
						end
					end
				end
			end
		end
	end

	ending_data = decorate_inflections(ending_data["patterns"], ref, defective, pattern_data["patterns"])
	ending_data["description"] = description
	return ending_data

end

function export.inflect_combined(forms, args, toggle_decorate, toggle_json)
	local ending = args[1]
	local pattern = args[2]
	local ref = args[4]
	
	local pattern_data = deepcopy(require("Module:es-conj/data/"..ending))
	if pattern ~= ending then
		pattern_data = deepcopy(require("Module:es-conj/data/"..ending.."/"..pattern))
	end

	local unstressed = pattern_data["unstressed"]
	
	local result = {}
	
	local aspects = {[1] = "acc", [2] = "dat"}
	
	local combined_data = require("Module:es-conj/data/combined")
	
	for paradigm,paradigm_data in pairs(combined_data) do
		local stem = forms[paradigm_data["index"]]
		
		if (ref) then
			stem = usub(stem,1,-3)
		end
		
		if (not toggle_json) then
			result[paradigm] = stem
		end
		
		for form in string.gmatch(stem, '[^,]+') do
			for k1,aspect in pairs(aspects) do
				for k2,pronoun_table in pairs(paradigm_data[aspect]) do

					local t = {}
					for k3,pronoun in pairs(pronoun_table) do
						local irregular = false
						local form_modified = deepcopy(form)

						if paradigm_data["stem_cuts"][k2] then
							if (#paradigm_data["stem_cuts"][k2] == 2) then
								form_modified = usub(form_modified, paradigm_data["stem_cuts"][k2][1], paradigm_data["stem_cuts"][k2][2])
							else
								if (pronoun == paradigm_data["stem_cuts"][k2][1]) then
									form_modified = usub(form_modified, paradigm_data["stem_cuts"][k2][2], paradigm_data["stem_cuts"][k2][3])
								end
							end
						end
						
						if paradigm_data["ending_irregularities"][ending] then
							for k4,pattern_data in  pairs(paradigm_data["ending_irregularities"][ending]) do
								if (aspect == pattern_data[1]) then
									if (k2 == pattern_data[2]) then
										if (pronoun == pattern_data[3]) then
											form_modified = pattern_data[4](form_modified)
										end
									end
								end
							end
						end

						if (paradigm_data["paradigm_irregularities"][pattern]) then
							for k4,pattern_data in pairs(paradigm_data["paradigm_irregularities"][pattern]) do
								if (aspect == pattern_data[1]) then
									if (k2 == pattern_data[2]) then
										if (pronoun == pattern_data[3]) then
											form_modified = pattern_data[4](form_modified)
											irregular = true
										end
									end
								end
							end
						end
					
						if (unstressed) then
							if (unstressed[paradigm_data["index"]]) then
								form_modified = strip_accents(form_modified)
							end
						end
						
						if (paradigm_data["paradigm_no_accent"][pattern]) then
							form_modified = strip_accents(form_modified)..pronoun
						else
							if (not irregular) then
								if (paradigm_data["accented_stem"])  then
									if paradigm_data["ua_disyllabic"][pattern] then
										form_modified = create_accented_form(strip_accents(form_modified)..pronoun, true)
									else
										form_modified = create_accented_form(strip_accents(form_modified)..pronoun, false)
									end
								else
									form_modified = form_modified..pronoun
								end
							end
						end
					
						if (not toggle_json) then
							table.insert(t, form_modified)
						else
							result[form_modified] = {pronoun, paradigm, stem}
						end
					
					end
					if (not toggle_json) then
						local parameter = paradigm .. "_" .. aspect .. "_" .. k2
						if (result[parameter]) then
							result[parameter] = result[parameter] .. ',' .. table.concat(t, ',')
						else
							result[parameter] = table.concat(t, ',')
						end
					end
				end
			end
		end
	end

	if (toggle_json) then
		return json.jsonValueFromValue(result)
	end
	
	if not toggle_decorate then
		return result
	end
	
	result["inf_ref"] = forms[1]
	return decorate_inflections(result, false, nil, nil)
	
end

function export.headword_line(frame)
   local args = unpack_args(frame)
   local categories = {}
   
   local forms = export.inflect(args, categories, true)
   
   if forms then
        local result = {title = "head", args = {[1] = "es",
        										[2] = "verb",
        										[3] = 'first-person singular present',
        										[5] = 'first-person singular preterite',
        										[7] = 'past participle'}}
        									
        if (forms[7]) then
        	result.args[4] = forms[7]
        end
        if (forms[20]) then
        	result.args[6] = forms[20]
    	end
    	if (forms[3]) then
    		result.args[8] = forms[3]
		end
		
		return frame:expandTemplate(result) .. table.concat(categories)
		
   else
      error("No inflection data found")
   end
end

function export.verb_table(frame)
	local calling_title = mw.title.getCurrentTitle()
	
	local m_table = require("Module:es-conj/table")
	
	local args = unpack_args(frame)
   
    if (args[5]) then
        return export.json_wikt_forms(frame)
    end
    
    categories = {}

    local forms = export.inflect(args, categories, true)

	if (args[7]) then
		return export.inflect_combined(export.inflect(args, categories, false), args, false, true)
	end

	if (args[6]) then
		local combined = export.inflect_combined(export.inflect(args, categories, false), args, true)
		if calling_title.namespace == 0 then
	   		return m_table.create(frame, args, forms, combined) .. table.concat(categories)
    	else
    		return m_table.create(frame, args, forms, combined)
		end
	end
	
    if forms then
    	if calling_title.namespace == 0 then
	    	return m_table.create(frame, args, forms) .. table.concat(categories)
    	else
    		return m_table.create(frame, args, forms)
		end
    else
       error("No inflection data found")
    end
   
end

function export.boiler(frame)
	local args = frame:getParent().args
	local ending = args[1]
	local pattern = args[2]
	
	local pattern_data = deepcopy(require("Module:es-conj/data/"..ending.."/"..pattern))
	local description = pattern_data["description"]
	local categories = '[[Category:按變位分類的西班牙語動詞]][[Category:以'..ending..'結尾的西班牙語動詞]]'
	if description then
		return description..categories
	else
		return categories
	end
end

return export