local p = {}
local i18n = require('মডিউল:আভিধানিক উপাত্ত/i18n')
local references = require('মডিউল:উইকিউপাত্ত তথ্যসূত্র বিন্যাসকরণ').format
local getArgs = require('Module:Arguments').getArgs
local formatter_urls = require('মডিউল:আভিধানিক উপাত্ত/urls').formatter_urls
local wb = mw.wikibase
local ustring = mw.ustring
local html = mw.html
local mw_lang = mw.language
local entity_cache = {}
local reference_cache = {}
local forms
local lang_code

local function countWords(string)
    local count = 0
    for word in ustring.gmatch(string, "%S+") do
        count = count + 1
    end
    return count
end

local function normalizeLemmas(text)
    text = mw.ustring.gsub(text, "ٰ", "")
    text = mw.ustring.gsub(text, "ِ", "")
	text = mw.ustring.gsub(text, "[ً-ٟ]", "")
    text = mw.ustring.gsub(text, "أ", "ا")
	--text = mw.ustring.gsub(text, "إ", "ا")
    text = mw.ustring.gsub(text, "آ", "ا")

    return text
end

local function serializeTable(val, name, skipnewlines, depth) -- https://stackoverflow.com/a/6081639
    skipnewlines = skipnewlines or false
    depth = depth or 0

    local tmp = string.rep(" ", depth)

    if name then tmp = tmp .. name .. " = " end

    if type(val) == "table" then
        tmp = tmp .. "{" .. (not skipnewlines and "\n" or "")

        for k, v in pairs(val) do
            tmp =  tmp .. serializeTable(v, k, skipnewlines, depth + 1) .. "," .. (not skipnewlines and "\n" or "")
        end

        tmp = tmp .. string.rep(" ", depth) .. "}"
    elseif type(val) == "number" then
        tmp = tmp .. tostring(val)
    elseif type(val) == "string" then
        tmp = tmp .. string.format("%q", val)
    elseif type(val) == "boolean" then
        tmp = tmp .. (val and "true" or "false")
    else
        tmp = tmp .. "\"[inserializeable datatype:" .. type(val) .. "]\""
    end

    return tmp
end

-- Use this to safely expand templates when you are not sure that they exist.
local function safeExpand(frame, title, args)
	local success, result = pcall(function()
		return frame:expandTemplate{ title = title, args = args }
	end)

	if result:find('does not exist') then -- expandTemplate{} doesn't seem to throw any error that can be handled with pcall() so string search is the only viable option.
		return nil
	end

	return result
end

local function getReference( id, reference )
    local out_id = nil
	local url_value
    if reference_cache[id] == nil then
        local ref_text = references(reference, wb, mw_lang, i18n['content_lang_code'], i18n['wikipedia'])
        if reference.snaks ~= nil then
            if reference.snaks['P248'] ~= nil then
                for _, snak in pairs(reference.snaks['P248']) do
                    if snak.datavalue and snak.datavalue.value.id == 'Q428' then -- কুরআন
                        ref_text = ustring.gsub(ref_text, 'নামক অনুচ্ছেদ', 'নং আয়াত')
                        break
                    end
                end
            end
            if reference.snaks['P854'] ~= nil then
                local snak = reference.snaks['P854'][1]
                if snak.datavalue then
	                url_value = snak.datavalue.value
                end
            end
        end
        if url_value ~= nil then
            ref_text = ref_text .. ', [' .. url_value .. ' সংযোগ]'
        end

        reference_cache[id] = ref_text
    else
        out_id = id
    end
    return {out_id, reference_cache[id]}
end

local function getEntity( id )
    if entity_cache[id] == nil then
        entity_cache[id] = wb.getEntity(id)
    end
    return entity_cache[id] ~= false and entity_cache[id] or nil
end

local function getLexemeLanguageCode(current_lexeme)
    local lang_item_id = current_lexeme:getLanguage()
    if lang_item_id == nil then
        return nil
    end

    local lang_entity = getEntity(lang_item_id)
    if lang_entity == nil then
        return nil
    end

    for i, statement_property in ipairs({'P305','P424', 'P220'}) do -- আইইটিএফ ভাষা ট্যাগ, উইকিমিডিয়া ভাষা কোড, আইএসও ৬৩৯-৩
        local statements = lang_entity:getBestStatements(statement_property)
        if statements[1] ~= nil then
            return statements[1].mainsnak.datavalue.value
        end
    end

    return nil
end

-- Return the first form of the lexeme which has exactly the given grammatical feature.
local function formWithSingleGrammaticalFeature( item_id )
    for i = 1, #forms do
        local grammaticalFeatures = forms[i]:getGrammaticalFeatures()
        if #grammaticalFeatures == 1 and grammaticalFeatures[1] == item_id then
            return forms[i]
        end
    end
    return nil
end

local function getArticleLinkTemplate(frame, stmt_value)
    local template = ''
    local sitelink = getEntity(stmt_value):getSitelink(i18n['wikipedia'])
    if sitelink ~= nil then
        template = frame:expandTemplate{
            title=i18n['template_wikipedia'],
            args={sitelink}
        }
    end
    return template
end

local function getArticleLinks (frame, sense )
    local article_links = ''
    for i, stmt in pairs(sense:getAllStatements('P5137')) do -- এই অর্থের জন্য আইটেম
        article_links = article_links .. getArticleLinkTemplate(frame, stmt.mainsnak.datavalue.value.id)
    end
    for i, stmt in pairs(sense:getAllStatements('P9970')) do -- এই অর্থের জন্য বিধেয়
        article_links = article_links .. getArticleLinkTemplate(frame, stmt.mainsnak.datavalue.value.id)
    end
    return article_links
end

-- @TODO: Generalise
local function expandTemplateForProperty(frame, object, property, template)
    local lemmas = {}
    local n = 0

    for _, stmt in pairs(object:getAllStatements(property)) do
        local lex = wb.lexeme.splitLexemeId(stmt.mainsnak.datavalue.value.id)
        lex = getEntity(lex)
        n = n + 1
        lemmas[n] = lex:getLemma(lang_code)
    end

    if not lang_code or n == 0 then
        return ''
    end

    -- Build args: first lang_code, then lemmas
    local args = {lang_code}
    for i = 1, n do
        args[#args + 1] = lemmas[i]
    end

    return frame:expandTemplate{
        title = template,
        args = args
    }
end

local function getExternalLinks( entity ) -- T418639
    local external_links = {}
    if entity.claims == nil then return external_links end
    for property_id, statements in pairs(entity.claims) do
        local formatter_url = formatter_urls[property_id]
        if formatter_url ~= nil then
            local property_source = wb.getBestStatements(property_id, 'P9073')
            local source_name

            if next(property_source) ~= nil then
                source_name = wb.getLabel(property_source[1].mainsnak.datavalue.value.id)
                        or property_source[1].mainsnak.datavalue.value.id
            else
                source_name = wb.getLabel(property_id) or property_id
            end

            for i = 1, #statements do
                local stmt = statements[i]
                if stmt.mainsnak.datavalue then
                    local formatted_link = ustring.gsub(
                        ustring.gsub(formatter_url, '$1', stmt.mainsnak.datavalue.value),
                        ' ', '+'
                    )
                    table.insert(external_links,
                        '[' .. formatted_link .. ' ' .. source_name .. ']')
                end
            end
        end
    end

    return external_links
end
p.getExternalLinks = getExternalLinks -- রেখে দিন যাতে ডিবাগিং সম্ভব হয়

local function termSpan( term )
    local text = term[1]
    local lang = term[2]
    local dir = mw_lang.new( lang ):getDir()
    local span = html.create( 'span' )
    span:attr( 'lang', lang )
        :attr( 'dir', dir )
        :wikitext( text )
    return tostring( span )
end

local function termLink( term, lang_qid )
    local text = term[1]
    local lang = term[2]
    local dir = mw_lang.new( lang ):getDir()
    local span = html.create( 'span' )
    span:attr( 'lang', lang )
        :attr( 'dir', dir )
        :wikitext( '[[' .. text .. '#' .. wb.getLabel(lang_qid) .. '|' .. text .. ']]' )
    return tostring( span )
end

local function getLemmata( current_lexeme )
    local lemma_string = ''
    for i, rep in pairs(current_lexeme:getLemmas()) do
        if lemma_string == '' then
            lemma_string = termSpan(rep)
        else
            lemma_string = lemma_string .. '/' .. termSpan(rep)
        end
    end
    return lemma_string
end

local function getLinkedLemmata( current_lexeme )
    local lemma_string = ''
    for i, rep in pairs(current_lexeme:getLemmas()) do
        if lemma_string == '' then
            lemma_string = termLink(rep, current_lexeme:getLanguage())
        else
            lemma_string = lemma_string .. '/' .. termLink(rep, current_lexeme:getLanguage())
        end
    end
    return lemma_string
end

local function getExamples( current_lexeme, sense_id, references_seen )
    local examples = html.create('dl')
    for i, stmt in pairs(current_lexeme:getAllStatements('P5831')) do -- ব্যবহারের উদাহরণ
        if stmt.qualifiers ~= nil and stmt.qualifiers['P6072'] ~= nil and stmt.qualifiers['P6072'][1].datavalue.value.id == sense_id then -- বিষয়ে লেক্সিমের অর্থ
            example_text = ustring.gsub(stmt.mainsnak.datavalue.value.text, ' / ','<br/>')
            example_lang = stmt.mainsnak.datavalue.value.language
            local example_form_strs = {}
            if stmt.qualifiers['P1810'] ~= nil then
                table.insert(example_form_strs, stmt.qualifiers['P1810'][1].datavalue.value)
            elseif stmt.qualifiers['P5830'] ~= nil then
                example_form = getEntity(stmt.qualifiers['P5830'][1].datavalue.value.id) -- বিষয়ে লেক্সিমের রূপ
                for i, rep in pairs(example_form:getRepresentations()) do
                    table.insert(example_form_strs, rep[1])
                end
            end

            example_str = nil
            for i, example_form_str in pairs(example_form_strs) do
                new_example_text = ustring.gsub(example_text, example_form_str, "'''" .. example_form_str .. "'''")
                if new_example_text ~= example_text then
                    example_str = termSpan({new_example_text, example_lang})
                    break
                end
                new_example_text = example_text
            end

            if example_str == nil then
                example_str = termSpan({example_text, example_lang})
            end

            local reference_text = ''
            if stmt.references ~= nil then
                for j, reference in pairs(stmt.references) do
                    table.insert(references_seen, reference.hash)
                    local got_reference = getReference(reference.hash, reference)
                    reference_text = reference_text .. '\n\n' .. got_reference[2]
                end
            end

            if example_str ~= nil then
                examples:tag('dd'):wikitext("''" .. example_str .. "''")
                if reference_text ~= '' then
                    examples:done():tag('dd'):css('text-indent', '2em'):wikitext(reference_text)
                end
            end
        end
    end

    for i, stmt in pairs(wb.getAllStatements(sense_id, 'P5831')) do -- ব্যবহারের উদাহরণ
        example_text = ustring.gsub(stmt.mainsnak.datavalue.value.text, ' / ','<br/>')
        example_lang = stmt.mainsnak.datavalue.value.language
        example_form = getEntity(stmt.qualifiers['P5830'][1].datavalue.value.id) -- বিষয়ে লেক্সিমের রূপ
        local example_form_str = nil

        if stmt.qualifiers['P1810'] ~= nil then
            example_form_str = stmt.qualifiers['P1810'][1].datavalue.value
        end
        if example_form_str == nil then
            example_form_str = example_form:getRepresentation(i18n['content_lang_code'])
        end
        if example_form_str == nil then
            example_form_str = example_form:getRepresentations()[1][1]
        end

        example_text = ustring.gsub(example_text, example_form_str, "'''" .. example_form_str .. "'''")
        example_str = termSpan({example_text, example_lang})

        local reference_text = ''
        if stmt.references ~= nil then
            for j, reference in pairs(stmt.references) do
                table.insert(references_seen, reference.hash)
                local got_reference = getReference(reference.hash, reference)
                reference_text = reference_text .. '\n\n' .. got_reference[2]
            end
        end

        if example_str ~= nil then
            examples:tag('dd'):wikitext("''" .. example_str .. "''")
            if reference_text ~= '' then
                examples:done():tag('dd'):css('text-indent', '2em'):wikitext(reference_text)
            end
        end
    end

    return { tostring(examples) , references_seen }
end

-- This calls frame:preprocess() instead of :callParserFunction() because the latter does not work for Wikifunctions function calls yet (see https://www.wikifunctions.org/wiki/Wikifunctions:Embedded_function_calls).
local function callWikifunctionsFunction(args, frame)
	return frame:preprocess('{{#function:' .. args .. '}}')
end

local function checkTitleCodePointInRange(title, start_point, end_point)
    return ustring.find( title, '[' ..ustring.char(start_point) .. '-' .. ustring.char(end_point) .. ']' )
end

local function getLanguageForCategories( lang_id, current_page_title )
    -- বিশেষ ভাষার জন্য
    if lang_id == 'Q11051' then -- হিন্দি/উর্দু
        if checkTitleCodePointInRange(current_page_title, 0x0600, 0x06ff) ~= nil then -- উর্দু
            lang_id = 'Q11051ur'
        elseif checkTitleCodePointInRange(current_page_title, 0x0900, 0x097f) ~= nil then -- হিন্দি
            lang_id = 'Q11051hi'
        end
    elseif lang_id == 'Q58635' then -- পাঞ্জাবি
        if checkTitleCodePointInRange(current_page_title, 0x0600, 0x06ff) ~= nil then -- শাহমুখী
            lang_id = 'Q58635pnb'
        elseif checkTitleCodePointInRange(current_page_title, 0x0a00, 0x0a7f) ~= nil then -- গুরুমুখী
            lang_id = 'Q58635pa'
        end
    elseif lang_id == 'Q56356571' then -- নয়া ফার্সি ভাষা
        if checkTitleCodePointInRange(current_page_title, 0x0600, 0x06ff) ~= nil then -- ফার্সি (ইরান/আফগানিস্তান)
            lang_id = 'Q56356571fa'
        elseif checkTitleCodePointInRange(current_page_title, 0x0400, 0x04ff) ~= nil then -- তাজিক
            lang_id = 'Q56356571tg'
        end
    end
    return lang_id
end

local function getOneStringForProperty(object, property)
	local val
	local stmts = object:getAllStatements(property)
	if #stmts ~= 0 then
		val = stmts[1].mainsnak.datavalue.value
	end
	return val
end

local function getTranslations(frame, senses) -- TODO: woefully incomplete until T185313 and T199887 are resolved
	if #senses == 0 then
		return nil
	end

	local all_translations = {}

	for _, sense in pairs(senses) do
		local translation_set = {}
		local gloss = sense:getGloss('bn')

		for _, stmt in pairs(sense:getAllStatements('P5972')) do
			local translation = stmt.mainsnak.datavalue.value.id
			local lexeme_id = wb.lexeme.splitLexemeId(translation)
			local language = wb.getLabel(getEntity(lexeme_id):getLanguage())

			table.insert(translation_set, language .. ': ' .. getLinkedLemmata(getEntity(lexeme_id)) .. '<br/>')
		end
		if #translation_set > 0 then
			local block = frame:expandTemplate{ title = i18n['template_trans-top'], args = { gloss } }
			block = block .. table.concat(translation_set, '\n') .. frame:expandTemplate{ title = i18n['template_trans-bottom'] }
			table.insert(all_translations, block)
		end
	end

	if #all_translations == 0 then
		return nil
	end

	return '====' .. i18n['heading_translation'] .. '==== \n' .. table.concat(all_translations, '\n')
end

local createicon = function(langcode, entityID, propertyID)
	langcode = langcode or ""
    propertyID = propertyID or ""
	local icon = "&nbsp;<span class='penicon autoconfirmed-show'>[["
	-- "&nbsp;<span data-bridge-edit-flow='overwrite' class='penicon'>[[" -> enable Wikidata Bridge
	.. "File:OOjs UI icon edit-ltr-progressive.svg |frameless |text-top |10px |alt="
	.. i18n['edit_wikidata']
	.. "|link=https://www.wikidata.org/entity/" .. entityID
	if langcode ~= "" then icon = icon .. "?uselang=" .. langcode end
	if propertyID ~= "" then icon = icon .. "#" .. propertyID end
	icon = icon .. "|" .. i18n['edit_wikidata'] .. "]]</span>"
	return icon
end

local function getMeanings( frame, args, current_lexeme, senses, references_seen, language_name)
    if #senses == 0 then
        return {createicon(i18n['content_lang_code'], current_lexeme:getId()) .. "''" .. i18n['text_category_rfdef'] .. "''" .. i18n.tocatlink(i18n['category_rfdef']), references_seen}
    end
    local meanings = html.create( 'ol' )

    for i, sense in pairs(senses) do
        local gloss_text_parts = {}
        local main_gloss_text = frame:expandTemplate{
            title=i18n['template_anchor'],
            args={sense:getId()}
        }

        local specifiers = {}
        for k, property_id in ipairs({'P6084', 'P6191', 'P9488'}) do -- অবস্থান যেখানে শব্দার্থ ব্যবহৃত, যে রীতিতে শব্দার্থ ব্যবহৃত হয়, যে ক্ষেত্রে ব্যবহৃত
            for i, stmt in pairs(sense:getAllStatements(property_id)) do
                local stmt_value = stmt.mainsnak.datavalue.value.id
                local reference_text = ''
				local refs = stmt.references
                if refs ~= nil then
					for j, reference in pairs(refs) do
	                    table.insert(references_seen, reference.hash)
	                    got_reference = getReference(reference.hash, reference)
	                    reference_text = reference_text .. '\n\n' .. frame:extensionTag('ref', got_reference[2])
	                end
                table.insert(specifiers, wb.getLabel(stmt_value, i18n['content_lang_code']) .. reference_text)
	            end
			end
        end
        if #specifiers > 0 then
            main_gloss_text = main_gloss_text .. "(''" .. table.concat(specifiers, "'', ''") .. "'') "
        end
        local gloss = sense:getGloss( i18n['content_lang_code'] )
        if gloss ~= nil then
			if countWords(gloss) == 1 then
					main_gloss_text = main_gloss_text .. "[[" .. gloss .. "#" .. i18n['content_lang_name'] .. "|" .. gloss .. "]]"
			else
				main_gloss_text = main_gloss_text  .. gloss
			end
			if gloss:match('^প্রদত্ত%s*(%S-)%s*নাম$') then -- given names
				main_gloss_text = main_gloss_text .. i18n.tocatlink(language_name .. ' ' .. i18n['category_given_names'])
			end
		else
            local other_gloss_text = nil
            local other_gloss_lang = nil

            local item_label_gloss_parts = {}
            for k, stmt in pairs(sense:getAllStatements('P5137')) do -- যদি 'এই অর্থের জন্য আইটেম' মানের বাংলা লেবেল থাকে
                local stmt_value = stmt.mainsnak.datavalue.value.id
                local stmt_label = wb.getLabel(stmt_value)
                if stmt_label ~= nil then
                    table.insert(item_label_gloss_parts, '[[:d:' .. stmt_value .. '|' .. stmt_label .. ']]')
                end
            end
            if #item_label_gloss_parts > 0 then
                other_gloss_text = table.concat(item_label_gloss_parts, '; ')
            end

            if other_gloss_text == nil then
                for i, fallback_lang in ipairs(mw_lang.getFallbacksFor( i18n['content_lang_code'] )) do
                    if sense:getGloss( fallback_lang ) ~= nil then
                        other_gloss_text, other_gloss_lang = sense:getGloss( fallback_lang )
                    end
                end
                if other_gloss_lang == nil then
                    local glosses = sense:getGlosses()
                    for j, gloss in pairs(glosses) do
                        other_gloss_text = gloss[1]
                        other_gloss_lang = gloss[2]
                        break
                    end
                end
                main_gloss_text = main_gloss_text .. other_gloss_text .. "<sup><em>" .. mw_lang.fetchLanguageName(other_gloss_lang, i18n['content_lang_code']) .. "</em></sup>"
            else
                main_gloss_text = main_gloss_text .. "''" .. other_gloss_text .. "''"
            end
            main_gloss_text = main_gloss_text .. i18n.tocatlink(i18n['category_rfdef_equivalent'])
        end
		local synonym = expandTemplateForProperty(frame, sense, 'P5973', i18n['template_synonym'])
		if synonym ~= '' then 
			main_gloss_text = main_gloss_text .. ' <br/> ' .. synonym
		end
		local antonym = expandTemplateForProperty(frame, sense, 'P5974', i18n['template_antonym'])
		if antonym ~= '' then 
			main_gloss_text = main_gloss_text .. ' <br/> ' .. antonym
		end
		local hypernym = expandTemplateForProperty(frame, sense, 'P6593', i18n['template_hypernym'])
		if hypernym ~= '' then
			main_gloss_text = main_gloss_text .. ' <br/> ' .. hypernym
		end
	    if lex_cat == 'Q1084' or lex_cat == 'Q147276' then -- noun or proper noun
			local demonym = expandTemplateForProperty(frame, sense, 'P6271', i18n['template_demonym-noun'])
			 main_gloss_text = main_gloss_text .. ' <br/> ' .. demonym
     elseif lex_cat == 'Q34698' then
			local demonym = expandTemplateForProperty(frame, sense, 'P6271', i18n['template_demonym-adj'])
			main_gloss_text = main_gloss_text .. ' <br/> ' .. demonym
		end
        table.insert(gloss_text_parts, main_gloss_text .. createicon(i18n['content_lang_code'], sense:getId()))

        for i, stmt in pairs(sense:getAllStatements('P8394')) do -- টিপ্পনীর উদ্ধৃতি
            gloss_quote = termSpan({stmt.mainsnak.datavalue.value.text, stmt.mainsnak.datavalue.value.language})
            if stmt.references[1] ~= nil then
                local got_reference = getReference ( stmt.references[1].hash, stmt.references[1] )
                gloss_quote = '"' .. gloss_quote .. '" ' .. got_reference[2]
            end
            table.insert(references_seen, stmt.references[1].hash)
            table.insert(gloss_text_parts, frame:extensionTag('ref', gloss_quote))
        end

        for i, stmt in pairs(sense:getAllStatements('P1343')) do -- বর্ণিত উৎস
            -- TODO: do away with making fake reference objects
            local fake_reference = { ['snaks'] = {} }
            fake_reference.snaks['P248'] = { [1] = stmt.mainsnak }
            qualifiers_order = stmt['qualifiers-order']
			if qualifiers_order ~= nil then
				for i, k in ipairs(qualifiers_order) do fake_reference.snaks[k] = stmt.qualifiers[k] end
			end
            fake_reference.hash = mw.hash.hashValue('sha3-512', serializeTable(fake_reference))
            table.insert(references_seen, fake_reference.hash)
            local got_reference = getReference(fake_reference.hash, fake_reference)
            if got_reference[1] == nil then
                table.insert(gloss_text_parts, frame:extensionTag('ref', got_reference[2], {name = fake_reference.hash}))
            else
                table.insert(gloss_text_parts, frame:extensionTag{name = 'ref', content='', args = {name = got_reference[1]}})
            end
        end

        local first_sense_image = ''
		local sense_images = sense:getAllStatements('P18')
		if next(sense_images) ~= nil then
		    first_sense_image = sense_images[1].mainsnak.datavalue.value
		end
        if first_sense_image ~= '' then
            table.insert(gloss_text_parts, '[[চিত্র:' .. first_sense_image .. "|thumb|'''" .. getLemmata(current_lexeme) .. "'''—" .. main_gloss_text .. ']]')
        end

        local idlinks = getExternalLinks(sense)
        if #idlinks > 0 then
            local idlinktext = '<small>('
            for _, idlink in pairs(idlinks) do
                idlinktext = idlinktext .. idlink .. '\n'
            end
            idlinktext = idlinktext .. ')</small>'
            table.insert(gloss_text_parts, idlinktext)
        end
        local externallinks = getArticleLinks(frame, sense)
        if externallinks ~= '' then
            table.insert(gloss_text_parts, externallinks)
        end

        local new_notes = {}
        local sense_keys = { sense:getId(), string.sub(sense:getId(), string.find(sense:getId(), '-')+1) }
        for _, v in ipairs(sense_keys) do
            if args[v] ~= nil then
                table.insert(new_notes, args[v])
            end
        end
        if #new_notes > 0 then
            for _, v in ipairs(new_notes) do
                if i == 1 then
                    table.insert(gloss_text_parts, '<br/>' .. v)
                else
                    table.insert(gloss_text_parts, v)
                end
            end
        end

        local examples, references_seen = unpack(getExamples( current_lexeme, sense:getId(), references_seen ))

        local gloss_text = table.concat(gloss_text_parts, '\n')

        meanings:tag('li'):wikitext(gloss_text):wikitext(examples)
    end
    return {meanings, references_seen}
end

local function getPronunciationBaseForm( lang_name, lex_cat)
    local base_form = nil

    -- (!) অন্য ভাষার শব্দের যদি অন্য রকম মূল ফর্ম থাকে সেগুলো এখানে নতুন if বিবৃতি দিয়ে যোগ করা যায়।
    if lang_name == 'বাংলা' then
        if lex_cat == 'Q1084' then -- বিশেষ্য
            base_form = formWithSingleGrammaticalFeature( 'Q131105' ) -- কর্তৃকারক
        elseif lex_cat == 'Q24905' then -- ক্রিয়া
            base_form = formWithSingleGrammaticalFeature( 'Q1350145' ) -- ক্রিয়া বিশেষ্য
        end
    end

    if base_form == nil then
        for i, form in pairs(forms) do
            base_form = form
            break
        end
    end
    return base_form
end

local function getCombines( current_lexeme )
    local combines = ''
    local index_mappings = {}

    for i, stmt in pairs(current_lexeme:getAllStatements('P5238')) do
        if stmt.qualifiers ~= nil and stmt.qualifiers['P1545'] ~= nil then -- ক্রম
            local current_index = tonumber(stmt.qualifiers['P1545'][1].datavalue.value)
            index_mappings[current_index] = stmt
        end
    end

    if #index_mappings ~= 0 then
        for i, stmt in ipairs(index_mappings) do
            local part_lexeme_id = stmt.mainsnak.datavalue.value.id
            local part_lexeme = getEntity(part_lexeme_id)
            local current_substring = getLinkedLemmata(part_lexeme)
            local part_etymology = getEtymology(part_lexeme)
            if part_etymology ~= '' and part_etymology ~= nil then
                current_substring = current_substring .. ' (← ' .. part_etymology .. ')'
            end
            if combines == '' then
                combines = current_substring
            else -- @TODO: This shoukd use the 'affix' template instead.
                combines = combines .. ' + ' .. current_substring
            end
        end
    end

    return combines
end

function getRoots ( current_lexeme )
	local stmts = current_lexeme:getAllStatements('P5920')
    if #stmts == 0 then return '' end
    local root_lexeme = getEntity(stmts[1].mainsnak.datavalue.value.id)
    return '√' .. getLinkedLemmata(root_lexeme)
end

function getEtymology ( current_lexeme )
    -- TODO: see if any etymology chains are not possible to render
    local etymology = ''
    local current_combines = getCombines(current_lexeme)
    local current_roots = getRoots(current_lexeme)
	local stmts = current_lexeme:getAllStatements('P5191')
    if #stmts == 0 then
        if current_roots ~= '' and current_combines ~= '' then
            return current_roots .. '<br/>(' .. current_combines .. ')'
        elseif current_roots ~= '' then
            return current_roots
        else
            return current_combines
        end
    end
    for i, stmt in pairs(stmts) do
		local origin_lexeme_dv = stmt.mainsnak.datavalue -- If this is nil, the origin lexeme is not known.
		if origin_lexeme_dv ~= nil then
	        local origin_lexeme = getEntity(origin_lexeme_dv.value.id)
	        local origin_lexeme_lang = origin_lexeme:getLanguage()
	        local origin_lexeme_string = getLinkedLemmata(origin_lexeme) .. ' (' .. wb.getLabel(origin_lexeme_lang) .. ')'
	        if stmt.qualifiers ~= nil and stmt.qualifiers['P5886'] ~= nil then
	            local mode_of_derivation = stmt.qualifiers['P5886'][1].datavalue.value.id
	            if mode_of_derivation == 'Q1345001' then
	                origin_lexeme_string = ustring.gsub(i18n['etymology_borrowing'], '$1', origin_lexeme_string)
	            elseif mode_of_derivation == 'Q845079' then
	                origin_lexeme_string = ustring.gsub(i18n['etymology_learned_borrowing'], '$1', origin_lexeme_string)
	            elseif mode_of_derivation == 'Q56611986' then
	                origin_lexeme_string = ustring.gsub(i18n['etymology_inheritance'], '$1', origin_lexeme_string)
	            end
	        end
	        local origin_origin = getEtymology(origin_lexeme)
	        local new_etymology_string = ''
	        if origin_origin ~= '' and origin_origin ~= nil then
	            new_etymology_string = origin_lexeme_string .. ' ← ' .. origin_origin
	        else
	            new_etymology_string = origin_lexeme_string
	        end
		end
        if etymology == '' then
            etymology = new_etymology_string
        elseif origin_lexeme_string ~= nil then
            etymology = etymology .. ' ' .. origin_lexeme_string
        end
    end
    if current_roots ~= '' then
        etymology = etymology .. ' ' .. current_roots
    end
    if current_combines ~= '' and etymology ~= nil then
        etymology = etymology .. '<br/>(' .. current_combines .. ')'
    end
    return etymology
end

local function getPronunciation( frame, current_lexeme, lang_name, lex_cat )
    local pronunciations = {}
    local base_form = getPronunciationBaseForm(lang_name, lex_cat )
    if base_form ~= nil then
        for i, stmt in pairs(base_form:getAllStatements('P443')) do -- উচ্চারণের অডিও
            local pronunciation_file = stmt.mainsnak.datavalue.value
            local specifier_text = ''
            local specifiers = {}
            if stmt.qualifiers ~= nil then
                for k, property_id in ipairs({'P5237'}) do -- উচ্চারণের ধরন
                    for l, qual in pairs(stmt.qualifiers[property_id]) do
                        local stmt_value = qual.datavalue.value.id
                        table.insert(specifiers, wb.getLabel(stmt_value))
                    end
                end
            end
            if #specifiers > 0 then
               specifier_text =  table.concat(specifiers, "'', ''")
            end
			local audio_text
			if specifier_text ~= '' then
				audio_text = i18n['text_audio'] .. ' (' .. specifier_text .. ')'
			else
				audio_text = i18n['text_audio']
			end
            table.insert(pronunciations, '* ' .. frame:expandTemplate{
                title= i18n['template_audio'],
                args = {lang_name, pronunciation_file, audio_text}
            })
        end
		local ipa_transcription = base_form:getAllStatements('P898') -- - আধ্বব প্রতিলিপিকরণ
		local iso15919_transcription = getOneStringForProperty(base_form, 'P5825') -- আইএসও ১৫৯১৯ প্রতিলিপিকরণ
		local itrans = getOneStringForProperty(base_form, 'P8881') -- ITRANS
		local iast = getOneStringForProperty(base_form, 'P7581') -- আইএএসটি প্রতিলিপিকরণ
		local xsampa = getOneStringForProperty(base_form, 'P2859')
		-- @TODO: অডিও ও প্রতিলিপিকরণ দুটোই থাকলে সেই ক্ষেত্রে একটার ঠিক পরেই আরেকটা দেখানো উচিত 
        if #ipa_transcription ~= 0 then
			for i, stmt in pairs(ipa_transcription) do
	            local ipa_text = stmt.mainsnak.datavalue.value
	            local specifier_text = ''
	            local specifiers = {}
	            if stmt.qualifiers ~= nil then
	                for k, property_id in ipairs({'P5237'}) do -- উচ্চারণের ধরন
	                    for l, qual in ipairs(stmt.qualifiers[property_id]) do
	                        table.insert(specifiers, wb.getLabel( qual.datavalue.value.id ))
	                    end
	                end
	            end
	            if #specifiers > 0 then
	               specifier_text = "(''" .. table.concat(specifiers, "'', ''") .. "'') "
	            end
	            table.insert(pronunciations, '* ' .. specifier_text .. frame:expandTemplate{
	                title= i18n['template_ipa'],
	                args = {lang_name, ipa_text}
	            } .. '\n* ' .. i18n['text_syllable_count'] .. callWikifunctionsFunction('Z30837|' .. ipa_text, frame))
	        end
		-- The following checks are ordered based on which one is expected to be true in a higher number of cases.
		elseif lang_name == 'বাংলা' then
			table.insert(pronunciations, '* ' .. frame:expandTemplate{
	            title='bn-IPA',
	        })
		elseif lang_name == 'আরবি' then
			local lemma = current_lexeme:getLemma('ar')
			table.insert(pronunciations, '* ' .. frame:expandTemplate{
	            title='ar-IPA',
				args={lemma}
			})
		elseif lang_name == 'ফালা' then
			table.insert(pronunciations, '* ' .. frame:expandTemplate{
	            title='fax-pron',
			})
		elseif lang_name == 'ফিনীয়' then
			table.insert(pronunciations, '* ' .. frame:expandTemplate{
	            title='fi-IPA',
	        })
		end
		if iso15919_transcription ~= nil then
			table.insert(pronunciations, '* ' .. i18n['text_iso15919'] .. iso15919_transcription)
		end
		if itrans ~= nil then
			table.insert(pronunciations, '* ' .. i18n['text_itrans'] .. itrans)
		end
		if iast ~= nil then
			table.insert(pronunciations, '* ' .. i18n['text_iast'] .. iast)
		end
		if xsampa ~= nil then
			table.insert(pronunciations, '* ' .. i18n['text_xsampa'] .. xsampa)
		end
    end -- {{আধ্বব|en|/ˈɪntəvjuː/}}
    return table.concat(pronunciations, '\n')
end

function getAlternativeSpellings( current_lexeme )
    local alt_spellings = {}

    for i, stmt in pairs(current_lexeme:getAllStatements('P11577')) do -- বিকল্প বানান
        if stmt.mainsnak.datavalue ~= nil then
            table.insert(alt_spellings, '* ' .. getLinkedLemmata(getEntity(stmt.mainsnak.datavalue.value.id)))
        end
    end

    return table.concat(alt_spellings, '\n')
end

local function heading_level(text, level)
    local heading_delimiter = string.rep('=', level)
    return heading_delimiter .. ' ' .. text .. ' ' .. heading_delimiter
end

function get_any_notes(sections, args, keys)
    local notes = {}
    for i, v in ipairs(keys) do
        if args[v] ~= nil then
            table.insert(notes, args[v])
        end
    end
    return notes
end

function add_specific_notes(sections, notes)
    for i, v in ipairs(notes) do
        table.insert(sections, v)
    end
end

local function add_any_notes(sections, args, keys)
    for i, v in ipairs(keys) do
        if args[v] ~= nil then
            table.insert(sections, args[v])
        end
    end
end

local function getMatchingLemmaForPageTitle(lexeme, title)
	local lemmas = lexeme:getLemmas()
	local matched_lemma
					
	for _, lemma_entry in ipairs(lemmas) do
			local lemma = lemma_entry[1]
			local clean_lemma = normalizeLemmas(lemma)
			if clean_lemma == title then
				matched_lemma = lemma
				break
			end
	end
	return matched_lemma
end

local function buildLanguageAgnosticInflectionTable()
	local has_image = false
	local form_images = {}
	for i, form in ipairs(forms) do
		local form_image = form:getAllStatements('P7407')
		if next(form_image) ~= nil then
			form_images[i] = form_image[1].mainsnak.datavalue.value
			has_image = true
		end
	end

	local table_class = "wikitable mw-collapsible sortable"
	if not has_image then
		table_class = table_class .. " mw-collapsed"
	end

	local text = "{| class='" .. table_class .. "' style='border:solid 1px rgb(80%,80%,100%); text-align:center;'\n"
	text = text .. "|+ " .. i18n['heading_inflection_table'] .. "\n"
	text = text .. "|- \n"
	text = text .. "! " .. i18n['heading_form'] .. " !! " .. i18n['heading_grammatical_features']

	if has_image then
		text = text .. " !! " .. (i18n['heading_image'])
	end
	text = text .. " \n"

	for i, form in ipairs(forms) do
		local rep = form:getRepresentations()
		local feat = form:getGrammaticalFeatures()

		local rep_text = ""
		for j, r in pairs(rep) do
			if rep_text == "" then
				rep_text = r[1]
			else
				rep_text = rep_text .. " / " .. r[1]
			end
		end

		local feat_text = ""
		if feat ~= nil then
			for j, f in ipairs(feat) do
				local label = wb.getLabel(f)
				if feat_text == "" then
					feat_text = label
				else
					feat_text = feat_text .. ", " .. label
				end
			end
		end

		text = text .. "|-\n"
		text = text .. "| " .. (rep_text ~= "" and rep_text or "—")
		text = text .. " || " .. (feat_text ~= "" and feat_text or "—")

		if has_image then
			local image_cell = "—"
			if form_images[i] ~= nil then
				image_cell = "[[চিত্র:" .. form_images[i] .. "|50px]]"
			end
			text = text .. " || " .. image_cell
		end

		text = text .. "\n"
	end

	text = text .. "|}"
	return text
end

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

    local lexeme_id = args[1]
    local current_lexeme = getEntity(lexeme_id)
    local current_language = current_lexeme:getLanguage()
	local senses = current_lexeme:getSenses()
    local add_heading = true
	forms = current_lexeme:getForms()
    if args[2] ~= nil then
       local val = mw.text.trim(tostring(args[2]))
       if val == "false" or val == "0" or val == "না" then
       	 add_heading = false
       end
    end

    local references_seen = {}
    local sections = {}
	local lang_name = wb.getLabel(current_language)
	if add_heading == true then
        local lang_heading = "== " .. lang_name .. " =="
		table.insert(sections, lang_heading)
	end

	local lex_cat = current_lexeme:getLexicalCategory()
	lang_code = getLexemeLanguageCode(current_lexeme)
	local title = mw.title.getCurrentTitle().text
    local lang_category = getLanguageForCategories(current_language, title)
	local cat = i18n.lang_category(wb.getLabel(lex_cat), wb.getLabel(lang_category))
	local lex_cat_template
	
	if cat ~= nil then
		table.insert(sections, '===' .. wb.getLabel(lex_cat) .. cat .. frame:expandTemplate{
			title = i18n['template_anchor'],
			args = { lexeme_id }
		} .. '===')
    table.insert(sections, frame:expandTemplate{
        title= i18n['template_lexeme'],
        args = {lexeme_id}
    })
    add_any_notes(sections, args, i18n['manual_category'])

    local etymology = getEtymology( current_lexeme )
    if etymology ~= '' and etymology ~= nil then
        table.insert(sections, heading_level(i18n['heading_etymology'], 4))
        table.insert(sections, tostring(etymology))
    end
    add_any_notes(sections, args, i18n['manual_etymology'])

    local pronunciation = getPronunciation( frame, current_lexeme, lang_name, lex_cat )
    if pronunciation ~= '' then
        table.insert(sections, heading_level(i18n['heading_pronunciation'], 4))
        table.insert(sections, tostring(pronunciation))
    end
    add_any_notes(sections, args, i18n['manual_pronunciation'])

	local alternative_spellings = getAlternativeSpellings( current_lexeme )
    if alternative_spellings ~= '' then
        table.insert(sections, heading_level(i18n['heading_alternative_spellings'], 4))
        table.insert(sections, alternative_spellings)
    end
		if lang_code ~= nil and lang_code ~= 'ksy' and lang_code ~= 'rkt' then -- Skip for Kharia Thar, Rangpuri
			if lex_cat == 'Q34698' then -- বিশেষণ
				lex_cat_template = safeExpand(frame, lang_code .. '-adj')
				if not lex_cat_template then
					lex_cat_template = safeExpand(frame, lang_code .. '-বিশেষণ')
				end
			elseif lex_cat == 'Q1084' then -- @TODO: Also check for plural forms
				local gender
				local stmts = current_lexeme:getAllStatements('P5185') -- ব্যাকরণগত লিঙ্গ
				if #stmts ~= 0 then
					if #stmts == 1 then
						local gender_qid = stmts[1].mainsnak.datavalue.value.id
						if gender_qid == 'Q499327' then
							gender = 'm'
						elseif gender_qid == 'Q1775415' then
							gender = 'f'
						elseif gender_qid == 'Q1775461' then
							gender = 'n'
						elseif gender_qid == 'Q1305037' then
							gender = 'c'
						end
					end
				else
					for i, stmt in pairs(stmts) do
						local qid = stmts[i].mainsnak.datavalue.value.id
						if qid == 'Q499327' then
							gender = gender .. 'm'
						elseif qid == 'Q1775415' then
							gender = gender .. 'f'
						end
					end
				end
			-- The following checks are ordered based on which one is expected to be true in a higher number of cases.
			if current_language == 'Q13955' then
				local matched_lemma = getMatchingLemmaForPageTitle(current_lexeme, title)
				if matched_lemma ~= nil then
						lex_cat_template = frame:expandTemplate{title='ar-noun', args={matched_lemma,gender}}
				else
						lex_cat_template = frame:expandTemplate{title='ar-noun', args={nil,gender}}
				end
			elseif current_language == 'Q29919' then
				lex_cat_template = frame:expandTemplate{title='arz-noun', args={g=gender}}
			elseif current_language == 'Q397' then
				local matched_lemma = getMatchingLemmaForPageTitle(current_lexeme, title)				
				if matched_lemma ~= nil then
					lex_cat_template = frame:expandTemplate{title='la-noun', args={matched_lemma,g=gender}}
				end
			elseif current_language == 'Q11059' then
				lex_cat_template = frame:expandTemplate{title='sa-noun', args={g=gender}}
			elseif current_language ~= 'Q1860' then -- These templates require the gender to be passed as the 1st argument.
				lex_cat_template = safeExpand(frame, lang_code .. i18n['noun_template_suffix'], {gender})
				if not lex_cat_template then
					lex_cat_template = safeExpand(frame, lang_code .. i18n['noun_template_suffix_fallback'], {gender})
				end
			end
		end
		end
	-- elseif lex_cat == 'Q147276' then
	--	lex_cat_template = safeExpand(frame, lang_code .. '-proper noun', {gender})
			--	if not lex_cat_template then
			--		lex_cat_template = safeExpand(frame, lang_code .. '-নামবাচক বিশেষ্য', {gender})
	--	end
	end
	if lex_cat_template ~= nil then
		table.insert(sections, lex_cat_template)
	else
		local matched_lemma = getMatchingLemmaForPageTitle(current_lexeme, title)
		if matched_lemma ~= nil then
			table.insert(sections, heading_level(matched_lemma, 4))
		else
			table.insert(sections, '[[Category:যেসব ভুক্তিতে লেমার হেডিং দেখানো অসম্ভব]]')
		end
	end
    local meanings, references_seen = unpack(getMeanings( frame, args, current_lexeme, senses, references_seen, lang_name))
    table.insert(sections, tostring(meanings))
    add_any_notes(sections, args, i18n['manual_meaning'])
	    local instance_of = current_lexeme:getBestStatements('P31') -- সত্ত্বার ধরন
	if #instance_of ~= 0 then
		local instance_of_entity = instance_of[1].mainsnak.datavalue.value.id
		if instance_of_entity == 'Q40437546' or instance_of_entity == 'Q120831827' or instance_of_entity == 'Q120717979' or instance_of_entity == 'Q124476844' then -- @TODO: generalise this so all types of roots are shown
			table.insert(sections, i18n['text_instance_of'] .. ' ' .. wb.getLabel(instance_of_entity))
		elseif instance_of_entity == 'Q376431' then -- বর্ণের নাম
			table.insert(sections, i18n.tocatlink(lang_code .. ':রং'))
		end
	end
	local translations = getTranslations(frame, senses)
	if translations ~= nil then
		table.insert(sections, translations)
	end
    -- (!) বিশেষ ভাষার বিভক্তির সারণি যদি থাকে সেগুলো এখানে নতুন if বিবৃতি যোগ করা যায়।
    if next(forms) ~= nil then
		if current_language == 'Q9610' then -- বাংলা
			if lex_cat == 'Q24905' then
	          local conjTable = require('মডিউল:আভিধানিক উপাত্ত/Q9610').getConjTable(frame, forms)
	        table.insert(sections, conjTable)
			elseif lex_cat == 'Q1084' then
				table.insert(sections, callWikifunctionsFunction('Z33244|' .. lexeme_id .. '|', frame))
			end
		--elseif current_language == 'Q13955' then -- আরবি
		--	if lex_cat == 'Q1084' then
		--		table.insert(sections, frame:expandTemplate{title='ar-decl-noun', args={lemma}})
		--	end
		elseif current_language == 'Q188' then -- জার্মান
			if lex_cat == 'Q1084' then
				table.insert(sections, callWikifunctionsFunction('Z28602|' .. lexeme_id .. '|', frame))
			end
		else
			if current_language ~= 'Q1860' then -- ইংরেজি
				table.insert(sections, buildLanguageAgnosticInflectionTable())
			end
		end
	end

    local reference_notes = get_any_notes(sections, args, i18n['manual_reference'])
    if #references_seen > 0 or #reference_notes > 0 then
        table.insert(sections, heading_level(i18n['heading_references'], 4))
        table.insert(sections, frame:extensionTag('references'))
        add_specific_notes(sections, reference_notes)
    end
    
    local external_link_table = getExternalLinks ( current_lexeme )
    if #external_link_table > 0 then
        local external_links = '* ' .. table.concat(external_link_table, '\n* ')
        table.insert(sections, heading_level(i18n['heading_external_links'], 4))
        table.insert(sections, external_links)
    end
    add_any_notes(sections, args, i18n['manual_external_link'])

    if #references_seen == 0 and #reference_notes == 0 and #external_link_table == 0 and #get_any_notes(sections, args, i18n['manual_external_link']) == 0 then
        if i18n['category_rfref'][lang_category] ~= nil then
            table.insert(sections, i18n.tocatlink(i18n['category_rfref'][lang_category]))
        else
            table.insert(sections, i18n.tocatlink(i18n['category_rfref']['_']))
        end
    end
    return table.concat(sections,"\n\n")
end

return p