Module:LoRUtility

From League of Legends Wiki, LoL Wiki
Jump to navigation Jump to search

Documentation for this module may be created at Module:LoRUtility/doc

-- <pre>

local lib               = require('Module:Feature')
local MAX_KNOWN_VERSION = 5
local p                 = {}
local VarInt            = {}
local BitOperator       = {}
local Utility           = {}

local REGIONS = {
    [1]  = {code = "DE", name = "Demacia"},
    [2]  = {code = "FR", name = "Freljord"},
    [3]  = {code = "IO", name = "Ionia"},
    [4]  = {code = "NX", name = "Noxus"},
    [5]  = {code = "PZ", name = "Piltover and Zaun"},
    [6]  = {code = "SI", name = "Shadow Isles"},
    [7]  = {code = "BW", name = "Bilgewater"},
    [8]  = {code = "SH", name = "Shurima"},
    [10] = {code = "MT", name = "Targon"},
    [11] = {code = "BC", name = "Bandle City"},
    [13] = {code = "RU", name = "Runeterra"}
}

--To test debug this line mw.executeFunction(p.test)
function p.test(frame)
	output = p.adventureRegions{"RUIODE--"}
	mw.log(output)
end

function base32_decode(str)
    local base32Alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567"
    
    local binary =
        str:gsub(
        ".",
        function(char)
            if char == "=" then
                return ""
            end
            local pos = string.find(base32Alphabet, char)
            pos = pos - 1
            return string.format("%05u", Utility.dec2bin(pos))
        end
    )
    
    local bytes = Utility.str_split(binary, 8)

    local result = {}
    for _, byte in pairs(bytes) do
        table.insert(result, tonumber(byte, 2))
    end

    return result
end

function p.deckDataFromCode(frame)
    local args = lib.frameArguments(frame)
    
    local lorData   = mw.loadData("Module:LoRData/data")
    local bytes     = base32_decode(args['code'])
    local firstByte = bytes[1]
    local deckList  = {}
    
    table.remove(bytes, 1)
    local version = BitOperator.AND(tonumber(firstByte), 0xF)

    if version > MAX_KNOWN_VERSION then
        return "The provided code requires a higher version of this library; please update."
    end
    
    local totalShards = 0
    local regions = {}
    local followerCount = 0
    local spellCount = 0
    local landmarkCount = 0
    local equipmentCount = 0
    local rarities = {}
    local champions = {}
    local shardValues = {
        ["None"] = 0,
        ["Common"] = 100,
        ["Rare"] = 300,
        ["Epic"] = 1200,
        ["Champion"] = 3000,
    }
    local topChampion = nil
    local topFollower = nil
    local runeterranChampions = {}
    local allCards = {}
    
    function handleCardData(cardData, count, cardCode, regionName)
        local cardType = cardData.type
        if cardType == "Unit" then
            if cardData.supertype ~= nil and cardData.supertype == "Champion" then
                cardType = "Champion"
                if regionName == "Runeterra" then
                	table.insert(runeterranChampions,cardData.name)
                end
            else
                cardType = "Follower"
            end
        end
        
        table.insert(allCards,{code=cardCode, count=count, regionName=regionName})
        regions[regionName] = (regions[regionName] or 0) + count
        if args['detail'] then
            totalShards = totalShards + shardValues[cardData.rarity] * count
            if cardData.rarity ~= 'None' then
                if cardData.rarity == 'Common' then 
                    rarities[1] = (rarities[1] or 0) + count
                elseif cardData.rarity == 'Rare' then 
                    rarities[2] = (rarities[2] or 0) + count
                elseif cardData.rarity == 'Epic' then 
                    rarities[3] = (rarities[3] or 0) + count
                else
                    rarities[4] = (rarities[4] or 0) + count
                end
            end
            
            if cardType == "Champion" or cardType =='Follower' then
                if cardType == "Champion" then
                    table.insert(champions, {
                        code = cardCode,
                        count = count
                    })
                    if topChampion == nil or cardData.cost > topChampion.cost then
                        topChampion = {
                            cost = cardData.cost,
                            code = cardCode
                        }
                    end
                else
                    if topFollower == nil or cardData.cost > topFollower.cost then
                        topFollower = {
                            cost = cardData.cost,
                            code = cardCode
                        }
                    end
                    followerCount = followerCount + count
                end
            elseif cardType == "Spell" then
                spellCount = spellCount + count
            elseif cardType == "Landmark" then
            	landmarkCount = landmarkCount + count
            elseif cardType == "Equipment" then
            	equipmentCount = equipmentCount + count
            end
            
        else
            table.insert(
                deckList,
                {
                    code = cardCode,
                    name = cardData.name,
                    count = count,
                    region = regionName,
                    type = cardType
                }
            )
        end
    end
    
    for i = 3, 1, -1 do
        local numGroupOfs = tonumber(VarInt.Pop(bytes)) or 0
        for j = 1, numGroupOfs, 1 do
            local numOfsInThisGroup = tonumber(VarInt.Pop(bytes)) or 0
            local set = VarInt.Pop(bytes)
            local region = VarInt.Pop(bytes)
            
            for k = 1, numOfsInThisGroup, 1 do
                local card = VarInt.Pop(bytes)
                if card ~= nil then
                    local setString = Utility.padLeft(tostring(set), 2, "0")
                    local regionData = REGIONS[region + 1]
                    local cardString = Utility.padLeft(tostring(card), 3, "0")
    
                    local cardCode = setString .. regionData.code .. cardString
    
                    if (not lorData[cardCode]) then
                        return 'Card Code "' .. cardCode .. '" does not exist'
                    end
                    handleCardData(lorData[cardCode], i, cardCode, regionData.name)
                end
            end
        end
    end
	
	--For cards with over 3 copies
	while true do
		local count = tonumber(VarInt.Pop(bytes)) or nil
		local set = VarInt.Pop(bytes)
		local region = VarInt.Pop(bytes)
		local card = VarInt.Pop(bytes)
		
		if not count or not set or not region or not card then
			break --end of stream
		end
		
		local setString = Utility.padLeft(tostring(set), 2, "0")
        local regionData = REGIONS[region + 1]
        local cardString = Utility.padLeft(tostring(card), 3, "0")
        
        local cardCode = setString .. regionData.code .. cardString
        
        if (not lorData[cardCode]) then
            return 'Card Code "' .. cardCode .. '" does not exist'
        end
        handleCardData(lorData[cardCode], count, cardCode, regionData.name)
	end
	
    while #bytes > 0 do
        local fourPlusCount = VarInt.Pop(bytes)
        local fourPlusSet = VarInt.Pop(bytes)
        local fourPlusFaction = VarInt.Pop(bytes)
        local fourPlusNumber = VarInt.Pop(bytes)

        if fourPlusFaction == nil then
            return 'Invalid Deck Code'
        end
        
        local fourPlusSetString = Utility.padLeft(tostring(fourPlusSet), 2, "0")
        local fourPlusregionData = REGIONS[fourPlusFaction + 1]
        local fourPlusNumberString = Utility.padLeft(tostring(fourPlusNumber), 3, "0")

        local cardCode = fourPlusSetString .. fourPlusregionData.code .. fourPlusNumberString

        if (not lorData[cardCode]) then
            return 'Card Code "' .. cardCode .. '" does not exist'
        end
        
        handleCardData(lorData[cardCode], fourPlusCount, cardCode, regionData.name)
    end
    
    local multiregion = {}
    if args['detail'] then
    	for _, champion in ipairs(runeterranChampions) do 
    		for _, data in ipairs(allCards) do
    			if p.isFromRuneterran{data.code,champion} then
    				regions["Runeterra"] = (regions["Runeterra"] or 0) + data.count
    				regions[data.regionName] = (regions[data.regionName] or 0) - data.count
    				data.regionName = "Runeterra"
    			end
    		end
    	end
    	for _, data in ipairs(allCards) do
    		if data.regionName ~= "Runeterra" and lorData[data.code].categoryRefs and lorData[data.code].regions then
    			for _,ref in ipairs(lorData[data.code].categoryRefs) do
    				if ref == "Multi-region" then
	    				regions[data.regionName] = (regions[data.regionName] or 0) - data.count
						local region1 = data.regionName
						local region2 = nil
						for _,region in ipairs(lorData[data.code].regions) do
							if region1 ~= region then
								region2 = region
								break	
							end
						end
						table.insert(multiregion,{
							count=data.count,
							region1=region1,
							region2=region2
						})
						break
					end
    			end
			end
    	end
    	for _,data in pairs(multiregion) do
    		if not data.region2 or (regions[data.region1] and regions[data.region1] > 0) then
    			regions[data.region1] = (regions[data.region1] or 0) + data.count
    		elseif regions[data.region2] and regions[data.region2] > 0 then
    			regions[data.region2] = (regions[data.region2] or 0) + data.count
    		else
    			regions[data.region1] = (regions[data.region1] or 0) + data.count
    		end
    	end
    	for region, count in pairs(regions) do
    		if count <= 0 then
    			regions[region] = nil	
    		end
    	end
        return {
            shards = totalShards,
            topUnitCode = (topChampion or topFollower).code,
            regions = regions,
            champions = champions,
            followerCount = followerCount,
            spellCount = spellCount,
            landmarkCount = landmarkCount,
            equipmentCount = equipmentCount,
            rarities = rarities
        }
    else
    	for _, champion in ipairs(runeterranChampions) do
    		for _, data in ipairs(deckList) do
    			if p.isFromRuneterran{data.code,champion} then
    				data.region = "Runeterra"
    			end
    		end
    	end
    	for _, data in ipairs(allCards) do
    		if lorData[data.code].categoryRefs and lorData[data.code].regions then
    			for _,ref in ipairs(lorData[data.code].categoryRefs) do
    				if ref == "Multi-region" then
	    				regions[data.regionName] = (regions[data.regionName] or 0) - data.count
						break
					end
    			end
			end
    	end
    	for _, data in ipairs(deckList) do
			if data.region ~= "Runeterra" and lorData[data.code].categoryRefs and lorData[data.code].regions then
    			for _,ref in ipairs(lorData[data.code].categoryRefs) do
    				if ref == "Multi-region" then
    					local region1 = data.region
						local region2 = nil
						for _,region in ipairs(lorData[data.code].regions) do
							if region1 ~= region then
								region2 = region
								break	
							end
						end
						if not region2 or (regions[region1] and regions[region1] > 0) then
    						data.region = region1
						elseif regions[region2] and regions[region2] > 0 then
		    				data.region = region2
			    		else
			    			data.region = region1
			    		end
    					break
    				end
    			end
    		end
		end
        return deckList
    end
end

function p.isFromRuneterran(frame)
	local args = lib.frameArguments(frame)
	local lorData = require("Module:LoRData/data")
	local cardCode = args['code'] or args[1] or nil
	local region = args['region'] or args['champion'] or args[2] or nil
	
	if not cardCode or not region or not lorData[cardCode] then
		return false	
	end
	local card = lorData[cardCode]
	if card.supertype == "Champion" then
		return false
	end
	
	if region == "Aatrox" and card.subtype then
		for _, subtype in ipairs(card.subtype) do
			if subtype == "Darkin" then
				return true	
			end
		end
	elseif region == "Bard" then
		local CHIME_CARDS = { ["06BC011"] = true, ["06BC026"] = true, ["06BC031"] = true,
			["06BC032"] = true, ["06BC044"] = true, ["06MT029"] = true, ["06MT047"] = true,
			["08BC005"] = true, ["08SI021"] = true
		}
		if CHIME_CARDS[cardCode] then return true end
	elseif region == "Elder Dragon" then
		if card.cost >= 6 then return true end 
	elseif region == "Evelynn" and card.categoryRefs then
		for _, ref in ipairs(card.categoryRefs) do
			if ref == "Husk-generating" then
				return true	
			end
		end
	elseif region == "Jax" and card.subtype then
		for _, subtype in ipairs(card.subtype) do
			if subtype == "Weaponmaster" then
				return true	
			end
		end
	elseif region == "Jhin" and card.keywordRefs then
		for _, ref in ipairs(card.keywordRefs) do
			if ref == "Skill-generating" then
				return true	
			end
		end
	elseif (region == "Kayn" or region == "Varus") and card.subtype then
		for _, subtype in ipairs(card.subtype) do
			if subtype == "Cultist" then
				return true	
			end
		end
	elseif region == "Neeko" and card.subtype then
		local SUBTYPES = { ["Bird"] = true, ["Cat"] = true, ["Dog"] = true,
			["Elnuk"] = true, ["Fae"] = true, ["Reptile"] = true, ["Spider"] = true
		}
		for _, subtype in ipairs(card.subtype) do
			if SUBTYPES[subtype] then
				return true	
			end
		end
	elseif region == "Ryze" and card.type == "Spell" and card.keywords then
		for _, keyword in ipairs(card.keywords) do
			if keyword == "Burst" or keyword == "Focus" then
				return true	
			end
		end
	elseif region == "The Poro King" then
		local PORO_CARDS = { ["01FR016"] = true, ["01FR025"] = true, ["02FR003"] = true,
			["03FR018"] = true, ["03PZ018"] = true, ["06BC043"] = true, ["09FR009"] = true,
			["99FR001"] = true, ["99FR002"] = true, ["99PZ118"] = true
		}
		if PORO_CARDS[cardCode] then return true end
		if card.subtype then
			for _, subtype in ipairs(card.subtype) do
				if subtype == "Poro" then
					return true	
				end
			end
		end
	end
	
	return false
end

function p.adventureRegions(frame)
	local args = lib.frameArguments(frame)
	local str = args[1] or nil
	local size = args["size"] or args[2] or "20px"
	local sep = args["sep"] or "&nbsp;&nbsp;"
	
	if not str then return nil end  -- Exit if no input
    
    local result = {}
    local REGION_CODES = {
	    DE = "Demacia",
	    FR = "Freljord",
	    IO = "Ionia",
	    NX = "Noxus",
	    PZ = "Piltover and Zaun",
	    SI = "Shadow Isles",
	    BW = "Bilgewater",
	    SH = "Shurima",
	    MT = "Targon",
	    BC = "Bandle City",
	    RU = "Runeterra"
	}
    
    for i = 1, #str, 2 do
        local pair = str:sub(i, i + 1):upper()
        mw.log(pair)
        local region = REGION_CODES[pair] or "Neutral"
        table.insert(result, "[[File: " .. region .. " LoR Region.png|" .. size .. "|link=]]")
    end
    
    return table.concat(result, sep)
end

function p.adventureRewards(frame)
	local args = lib.frameArguments(frame)
	local step = args["step"] or "5"
	step = tonumber(step)
	if not step then
		return "Step Error."
	end
	
	local base_start = "<center><table style=\"border-collapse: collapse;\"><tr>"
	local base_end = "</tr></table></center>"
	local r = ""
	local count = 0
	
	for i, a in ipairs(args) do
		count = i
	end
	
	local rows = math.ceil(count/step)
	local index_lastrow = ((rows-1)*step)+1
	
	for i, a in ipairs(args) do
		if i % step == 1 then
			if i ~= 1 then
				r = r .. base_end
			end
			r = r .. base_start
			if i < index_lastrow then
				r = r .. "<td style=\"text-align: center; padding: 5px; border-right: 1px solid #3e4452; border-left: 1px solid #3e4452; border-bottom: 1px solid #3e4452;\"> "
			else
				r = r .. "<td style=\"text-align: center; padding: 5px; border-right: 1px solid #3e4452; border-left: 1px solid #3e4452;\"> "	
			end
		else
			if i < index_lastrow then
				r = r .. "<td style=\"text-align: center; padding: 5px; border-right: 1px solid #3e4452; border-bottom: 1px solid #3e4452;\"> "
			else
				r = r .. "<td style=\"text-align: center; padding: 5px; border-right: 1px solid #3e4452;\"> "	
			end
		end
		r = r .. args[i] .. "</td>"
	end
	r = r .. base_end
	return frame:preprocess(r)
end

function p.headerDeck(frame)
    local args = lib.frameArguments(frame)
    
    local builder = require("Module:SimpleHTMLBuilder")
    args['name'] = args['name'] or 'Untitled Deck'
    
    if args['code'] == nil then
        return 'Empty Code'
    end
    
    local result = {}
    
    local deckDetail = p.deckDataFromCode{code = args['code'], detail = true}
    if type(deckDetail) ~= 'table' then
        return deckDetail
    end
    
    local totalShards   = deckDetail['shards'] or nil
    local topUnitCode   = deckDetail['topUnitCode'] or nil
    local regions       = deckDetail['regions'] or nil
    local champions     = deckDetail['champions'] or nil
    local followerCount = deckDetail['followerCount'] or nil
    local spellCount    = deckDetail['spellCount'] or nil
    local landmarkCount = deckDetail['landmarkCount'] or nil
    local equipmentCount = deckDetail['equipmentCount'] or nil
    local rarities      = deckDetail['rarities'] or nil
    
    local template = builder.create('div')
        :css('position', 'relative')
        :css('font-family', 'BeaufortLoL')
        :tag('div')
            :css('opacity', '0.5')
            :wikitext('[[File:' .. topUnitCode .. '-full.png|700px|link=]]')
            :done()
            
    local deckHeader = builder.create('div')
            :addClass('deck-header')
            :css('position', 'absolute')
            :css('top', '10px')
            :css('left', '10px')
            :tag('span')
                :css('font-weight', 'bold')
                :css('color', '#FFE3B0')
                :css('font-size', '30px')
                :css('margin-left', '10px')
                :css('margin-right', '10px')
                :wikitext(args['name'])
                :done()
    for regionName, regionCount in pairs(regions) do
        deckHeader
            :tag('span')
                :addClass('inline-image')
                :wikitext('[[File:' .. regionName .. ' LoR Region.png|60px|link=LoR:' .. regionName .. ']]')
                :done()
    end 
    deckHeader
            :tag('span')
                :css('display', 'inline-block')
                :css('position', 'relative')
                :css('color', '#FFE3B0')
                :css('font-size', '16px')
                :wikitext('[[File:Shard icon.png|40px|link=LoR:Shard]]' .. Utility.comma_value(totalShards))
            :done()
    deckHeader:done()
    
    local deckInfo = builder.create('div')
        :addClass('deck-info')
        :css('position', 'absolute')
        :css('top', '65px')
        :css('left', '20px')
        :css('font-weight', 'bold')
        :css('color', '#FFE3B0')
            
    if args['description'] then
        deckInfo
            :tag('span')
                :css('display', 'inline-block')
                :css('max-width', '660px')
                :css('font-size', '16px')
                :wikitext(args['description'])
            :done()
    end
        deckInfo:done()
    
    template
        :node(deckHeader)
        :node(deckInfo)
        :tag('div')
            :addClass('deck-info')
            :css('position', 'absolute')
            :css('top', '240px')
            :css('left', '15px')
            :css('font-size', '20px')
            :css('color', '#FFE3B0')
            :tag('span')
                :addClass('inline-image')
                :wikitext('[[File:Follower card.png|25px|link=LoR:Follower]]' .. followerCount .. '&nbsp;')
            :done()
            :tag('span')
                :addClass('inline-image')
                :wikitext('[[File:Spell card.png|25px|link=LoR:Spell]]' .. spellCount .. '&nbsp;')
            :done()
            :tag('span')
                :addClass('inline-image')
                :wikitext('[[File:Keyword Landmark.png|25px|link=LoR:Landmark]]' .. landmarkCount .. '&nbsp;')
            :done()
            :tag('span')
                :addClass('inline-image')
                :wikitext('[[File:Keyword Equipment.png|25px|link=LoR:Equipment]]' .. equipmentCount .. '&nbsp;')
            :done()
            :tag('br')
            :done()
            :tag('span')
                :addClass('inline-image')
                :wikitext('[[File:LoR Common icon.png|25px|link=LoR:Card_types#By_rarity]]' .. (rarities[1] or 0) .. '&nbsp;')
            :done()
            :tag('span')
                :addClass('inline-image')
                :wikitext('[[File:LoR Rare icon.png|25px|link=LoR:Card_types#By_rarity]]' .. (rarities[2] or 0) .. '&nbsp;')
            :done()
            :tag('span')
                :addClass('inline-image')
                :wikitext('[[File:LoR Epic icon.png|25px|link=LoR:Card_types#By_rarity]]' .. (rarities[3] or 0) .. '&nbsp;')
            :done()
            :tag('span')
                :addClass('inline-image')
                :wikitext('[[File:LoR Champion icon.png|25px|link=LoR:Card_types#By_rarity]]' .. (rarities[4] or 0) .. '&nbsp;')
            :done()
        :done()
    
    if #champions then
        local deckChampions = builder.create('div')
            :addClass('deck-champions')
            :css('position', 'absolute')
            :css('bottom', '20px')
            :css('left', '200px')
            :css('font-size', '20px')
            :css('color', '#FFE3B0')
                :tag('span')
                    :addClass('inline-image')
                    :wikitext('[[File:Champion card.png|25px|link=LoR:Champion]]')
                :done()
        for _, deckData in pairs(champions) do
            deckChampions
                :tag('span')
                    :addClass('inline-image')
                    :wikitext('[[File:' .. deckData.code .. '.png|60px|link=LoR:'.. deckData.code .. ']]')
                :done()
                :tag('span')
                    :addClass('inline-image')
                    :wikitext('x' .. deckData.count .. '&nbsp;')
                :done()
        end
        deckChampions:
            done()
        template
            :node(deckChampions)
    end
    
    template:allDone()
    
    return tostring(template)
end

function p.deckDecoder(frame)
    local args = lib.frameArguments(frame)
    
    if args['code'] == nil then
        return 'Empty Code'
    end
    
    local groupBy = args['groupBy'] or 'none'
    local groupByValues = {'region', 'type', 'none'}
    if Utility.table_contains(groupByValues, groupBy) == false then
        return 'Only support Group By "region", "type" or none'
    end
    
    local header = args['header'] or true
    if header == 'false' then
    	header = false
    end
    
    local result = {}
    
    local deckList = p.deckDataFromCode{code = args['code']}
    if type(deckList) ~= 'table' then
        return deckList
    end
    
    for _, deckData in pairs(deckList) do
        local groupByKey = ''
        if groupBy == 'region' then
            groupByKey = deckData.region
        elseif groupBy == 'type' then
            groupByKey = deckData.type .. 's'
        else
            groupByKey = 'List'
        end
        
        if result[groupByKey] == nil then
            result[groupByKey] = {}
        end
        
        table.insert(
            result[groupByKey],
            {
                code  = deckData.code,
                count = deckData.count,
                name  = deckData.name,
                type  = deckData.type
            }
        )
    end

    local sortedKeys = {}
    for k in pairs(result) do
        table.insert(sortedKeys, k)
    end
    table.sort(sortedKeys)
    
    local template = ""
    if header then
    	template = template .. p.headerDeck{code = args['code'], name = args['name'], description = args['description']}
    end
    
    local newline = "\n"
    template = template .. "<tabber>" .. newline
    template = template .. "Hide=" .. newline .. '|-|' .. newline
    for i, groupByKey in ipairs(sortedKeys) do
        if i ~= 1 then
            template = template .. "|-|" .. newline
        end
        template = template .. groupByKey .. "=" .. newline .. "'''Code:''' " .. args['code'] .. newline

        local regionData = result[groupByKey]
        table.sort(
            regionData,
            function(a, b)
                if (a.type == b.type) then
                    return a.name < b.name
                end
                return a.type < b.type
            end
        )

        local type = ""
        for _, cardData in ipairs(regionData) do
            if cardData.type ~= type then
                if (type ~= "") then
                    template = template .. "}}" .. newline
                end
                if groupBy == 'region' or groupBy == 'none' then
                    template = template .. ";" .. cardData.type .. "s" .. newline
                end
                template = template .. "{{column|3|" .. newline

                type = cardData.type
            end

            local cardCodeTemplate = "* {{LoR|" .. cardData.name
            if cardData.type ~= "Champion" then
                cardCodeTemplate = cardCodeTemplate .. "|code=" .. cardData.code
            end
            cardCodeTemplate = cardCodeTemplate .. "}}"
            if cardData.count > 1 then
                cardCodeTemplate = cardCodeTemplate .. " (x" .. cardData.count .. ")"
            end

            template = template .. cardCodeTemplate .. newline
        end

        template = template .. "}}" .. newline
    end
    template = template .. "</tabber>"

    return frame:preprocess(template)
end

function p.pocHeaderDeck(frame)
    local args = lib.frameArguments(frame)
    
    local builder = require("Module:SimpleHTMLBuilder")
    local adventureData = require("Module:LoRPoCData/adventure")
    local lorData = require("Module:LoRData/data")
    args['name'] = args['name'] or args['foe'] or 'Untitled Deck'
    local cmp = args['campaign'] or "World Adventures"
    
    if args['adventure'] == nil or not adventureData[cmp][args['adventure']] then
    	return 'Adventure missing or invalid'
    end
    local adventure = adventureData[cmp][args['adventure']]
    if args['foe'] == nil or not adventure[args['foe']] then
    	return 'Foe missing or invalid'
    end
    local foe = adventure[args['foe']]
    local stars = adventure.stars or false
    local adventureType = adventure["type"] or false
    
    args['code'] = args['code'] or foe.deck or nil
    if args['code'] == nil then
        return 'Empty Code'
    end
    
    local hasPower = foe.power or false
    local hasPowerArgs = args['powername'] or args['powerdesc'] or false
    local powername = nil
    local powerdesc = nil
    local powericon = nil
    local powerrarity = nil
    if hasPower then
    	powername = foe.power.name or 'Power Name'
	    powerdesc = foe.power.desc or 'Power Description.'
    	powericon = foe.power.icon or nil
    	powerrarity = foe.power.rarity or 'Common'
    end
    if hasPowerArgs then
    	powername = args['powername'] or powername or 'Power Name'
	    powerdesc = args['powerdesc'] or powerdesc or 'Power Description.'
    	powericon = args['powericon'] or powericon or nil
    	powerrarity = args['powerrarity'] or powerrarity or 'Common'
    	hasPower = true
    end
    
    local deckDetail = p.deckDataFromCode{code = args['code'], detail = true}
    if type(deckDetail) ~= 'table' then
        return deckDetail
    end
    local deckSimple = p.deckDataFromCode{code = args['code'], detail = false}
    if type(deckSimple) ~= 'table' then
        return deckSimple
    end
    
    local topUnitCode   = args['topUnitCode'] or foe.code or deckDetail['topUnitCode'] or nil
    local regions       = deckDetail['regions'] or nil
    local champions     = deckDetail['champions'] or nil
    local followerCount = deckDetail['followerCount'] or nil
    local spellCount    = deckDetail['spellCount'] or nil
    local landmarkCount = deckDetail['landmarkCount'] or nil
    local equipmentCount = deckDetail['equipmentCount'] or nil
    local MAX_MOBILE_ITEMS = 3
    local MAX_ITEMS = 7
    
    --Extract items on cards
    local items = foe.items or nil
    
    --Fuse champions with items
    local championsItem = {}
    if items then
	    for code, data in pairs(items) do
	    	if lorData[code] and lorData[code]["supertype"] == "Champion" then
	    			table.insert(championsItem,{
	    				code=code,
	    				count=0,
	    				items=data
	    			})
	    	end
	    end
	end
    for _, deckData in pairs(champions) do
    	local found = false
    	for _, data in pairs(championsItem) do
    		if data.code == deckData.code then
    			found = true
    			data.count=deckData.count
    			break
    		end
    	end
    	if not found then
    		table.insert(championsItem, {
    			code=deckData.code,
    			count=deckData.count,
    			items=nil
    		})	
    	end
    end
    table.sort(championsItem, function(a, b)
	    if (lorData[a.code].name == args['foe']) ~= (lorData[b.code].name == args['foe']) then -- 1. Priority if "name" is the foe
	        return lorData[a.code].name == args['foe']
	    end
	    
	    local aitems = {}
	    local bitems = {}
	    if a.items then aitems = a.items end
	    if b.items then bitems = b.items end
	    
	    if #aitems ~= #bitems then -- 2. Sort by the number of items (higher count first)
	        return #aitems > #bitems
	    end
	    if lorData[a.code].cost ~= lorData[b.code].cost then -- 3. Sort by "cost" (lowest to highest)
	        return lorData[a.code].cost < lorData[b.code].cost
	    end
		return lorData[a.code].name < lorData[b.code].name -- 4. Sort alphabetically by "name"
    end)
    
    --detailed cards with items list
    local detailedItems = {}
	if items then
		for code, data in pairs(items) do
			table.insert(detailedItems, {
				code=code,
				name=lorData[code].name,
				cost=lorData[code].cost,
				items=data
			})	
		end
	end
	table.sort(detailedItems, function(a, b)
	    if (a.name == args['foe']) ~= (b.name == args['foe']) then -- 1. Priority if "name" is the foe
	        return a.name == args['foe']
	    end
	    if #a.items ~= #b.items then -- 2. Sort by the number of items (higher count first)
	        return #a.items > #b.items
	    end
	    if a.cost ~= b.cost then -- 3. Sort by "cost" (lowest to highest)
	        return a.cost < b.cost
	    end
		return a.name < b.name -- 4. Sort alphabetically by "name"
    end)
	
    local template = builder.create('div')
    	:css('display', 'inline-block')
        :css('position', 'relative')
        :css('font-family', 'BeaufortLoL')
        :tag('div')
            :css('opacity', '0.5')
            :wikitext('[[File:' .. topUnitCode .. '-full.png|800px|link=]]')
            :done()
            
    local deckHeader = builder.create('div')
            :addClass('deck-header')
            :css('position', 'absolute')
            :css('max-width', '650px')
            :css('top', '10px')
            :css('left', '10px')
            :tag('span')
                :css('font-weight', 'bold')
                :css('color', '#FFE3B0')
                :css('font-size', '30px')
                :css('margin-left', '10px')
                :css('margin-right', '10px')
                :wikitext(args['name'])
                :done()
    
    local regionSize = 60
    local count = 0
    for _ in pairs(regions) do
        count = count + 1
        if count > 9 then
        	regionSize = 50
        	break
        end
    end
    for regionName, regionCount in pairs(regions) do
        deckHeader
            :tag('span')
                :addClass('inline-image')
                :wikitext('[[File:' .. regionName .. ' LoR Region.png|' .. tostring(regionSize) .. 'px|link=LoR:' .. regionName .. ']]')
                :done()
    end
    
    if stars then
    	local starHeader= builder.create('div')
            :addClass('deck-header')
            :css('position', 'absolute')
            :css('top', '10px')
            :css('left', '730px')
            :tag('span')
                :css('font-weight', 'bold')
                :css('color', 'white')
                :css('font-size', '18px')
                	:tag('span')
                		:addClass('PoC-Star-Difficulty')
                		:css('white-space', 'nowrap')
                		:tag('span')
                			:css('position', 'relative')
                			:css('text-indent', '0')
                			:css('top', '-3px')
                			:wikitext('[[File:PoC Star icon.png|60px|link=]]')
                			:tag('span')
                				:css('position', 'absolute')
                				:css('left', '17px')
                				:css('top', '30%')
                				:css('user-select', 'none')
                				:css('line-height', '1.5')
                				:css('text-align', 'center')
                				:css('width', '25px')
                				:css('height', '25px')
                				:css('transform', 'translateY(-25%)')
                				:css('text-shadow', '0.5px 0.5px 1px #000, 0.5px 0.5px 1px #000, -0.5px -0.5px 1px #000, -0.5px -0.5px 1px #000, 0.5px -0.5px 1px #000,  0.5px -0.5px 1px #000, -0.5px 0.5px 1px #000, -0.5px 0.5px 1px #000, 0.5px 0.5px 1px #000, 0.5px 0.5px 1px #000;')
                				:wikitext(stars)
                			:done()
                		:done()
                	:done()
            :done()
            
        template
            :node(starHeader)
    end
    
    if adventureType then
    	local leftOffset = '660px'
    	if not stars then leftOffset = '730px' end
    	
    	local typeImage = ''
    	if adventureData.Icons[adventureType]  then
    		typeImage = '[[File:' .. adventureData.Icons[adventureType] .. '|60x60px|link=LoR:World Adventures]]'
    	end
    	
    	local typeHeader= builder.create('div')
            :addClass('deck-header')
            :css('position', 'absolute')
            :css('top', '8px')
            :css('left', leftOffset)
            :tag('span')
            	:wikitext(typeImage)
            :done()
            
        template
            :node(typeHeader)
    end
    
    if #championsItem > 0 then
        local deckChampions = builder.create('div')
            :addClass('deck-champions')
            :css('position', 'absolute')
            :css('top', '70px')
            :css('left', '20px')
            :css('font-size', '20px')
            :css('color', '#FFE3B0')
                :tag('span')
                    :addClass('inline-image')
                    :wikitext('[[File:Champion card.png|25px|link=LoR:Champion]]')
                :done()
        
        local n = 0
        for _, deckData in pairs(championsItem) do
        	if (n == MAX_MOBILE_ITEMS and args['mobile']) or (n == MAX_ITEMS) then
        		deckChampions
	                :tag('span')
		                :css('font-weight', 'bold')
		                :css('font-size', '30px')
	                    :addClass('inline-image')
	                    :wikitext(frame:preprocess('  ...'))
	                :done()
	             break
        	end
        	if deckData.items then
        		local s = '{{PoCCardItems|size=70|' .. deckData.code
        		for _, item in pairs(deckData.items) do
	        		s = s .. '|' .. item
	        	end
	        	s = s .. '}}'
        		deckChampions
	                :tag('span')
	                    :addClass('inline-image')
	                	:wikitext(frame:preprocess(s))
	                :done()
	                :tag('span')
	                    :addClass('inline-image')
	                    :wikitext(' x' .. deckData.count .. '&nbsp;')
	                :done()
	        else
	        	deckChampions
	                :tag('span')
	                    :addClass('inline-image')
	                    :wikitext('[[File:' .. deckData.code .. '.png|70px|link=LoR:'.. deckData.code .. ']]')
	                :done()
	                :tag('span')
	                    :addClass('inline-image')
	                    :wikitext('x' .. deckData.count .. '&nbsp;')
	                :done()
        	end
    		n = n+1
        end
        deckChampions:
            done()
        template
            :node(deckChampions)
    end
    
    if items then
        local deckItems = builder.create('div')
            :addClass('deck-champions')
            :css('position', 'absolute')
            :css('top', '175px')
            :css('left', '20px')
            :css('font-size', '20px')
            :css('color', '#FFE3B0')
                :tag('span')
                    :addClass('inline-image')
                    :wikitext('[[File:PoC Relic icon.png|25px]]')
                :done()
        
		local n = 0
        for _, itemList in ipairs(detailedItems) do
        	if (n == MAX_MOBILE_ITEMS and args['mobile']) or (n == MAX_ITEMS) then
        		deckItems
	                :tag('span')
		                :css('font-weight', 'bold')
		                :css('font-size', '30px')
	                    :addClass('inline-image')
	                    :wikitext(frame:preprocess('  ...'))
	                :done()
	             break
        	end
        	local count = 0;
        	local nonChampion = true
        	for _, data in pairs(championsItem) do
    			if data.code == itemList.code then
	    			nonChampion = false
	    			break
        		end
        	end
        	if nonChampion then
        		for _, data in pairs(deckSimple) do
	        		if data.code == itemList.code then
	        			count = data.count
	        			break
	        		end
	        	end
	        	local s = '{{PoCCardItems|size=70|' .. itemList.code
        		for _, item in ipairs(itemList.items) do
	        		s = s .. '|' .. item
	        	end
	        	s = s .. '}}'
	            deckItems
	                :tag('span')
	                    :addClass('inline-image')
	                    :wikitext(frame:preprocess(s))
	                :done()
	                :tag('span')
	                    :addClass('inline-image')
	                    :wikitext(' x' .. count .. '&nbsp;')
	                :done()
	        	n = n+1
	        end
        end
        deckItems:
            done()
        template
            :node(deckItems)
    end
    
    local powerImage
    local powerTitle
    local powerInfo
    if hasPower then
    	local image_leftOffset = 230
    	if args['mobile'] then image_leftOffset = 200 end
	    powerImage = builder.create('div')
	        :addClass('deck-info')
	        :css('position', 'absolute')
	        :css('top', '300px')
	        :css('left', tostring(image_leftOffset) .. 'px')
	    if powericon then
	    	powerImage
		    	:tag('span')
		            :css('display', 'inline-block')
		            :wikitext(frame:preprocess('{{PoCPowerIconOverlay|' .. powericon .. '|' .. powerrarity .. '|80}}'))
		        :done()
		end
	        powerImage
	        	:done()
	        
	    powerTitle = builder.create('div')
	        :addClass('deck-info')
	        :css('position', 'absolute')
	        :css('top', '300px')
	        :css('left', tostring(image_leftOffset+100) .. 'px')
	        :css('color', '#FFE3B0')
	        :css('font-weight', 'bold')
	        :tag('span')
	            :css('display', 'inline-block')
	            :css('font-size', '16px')
	            :wikitext(powername)
	        :done()
	        powerTitle:done()
	    
	    powerInfo = builder.create('div')
	        :addClass('deck-info')
	        :css('position', 'absolute')
	        :css('top', '330px')
	        :css('left', tostring(image_leftOffset+100) .. 'px')
	        :css('color', '#FFE3B0')
	        :tag('span')
	            :css('display', 'inline-block')
	            :css('max-width', '460px')
	            :css('font-size', '14px')
	            :wikitext(frame:preprocess(powerdesc))
	        :done()
	        powerInfo:done()
    end
    
    local health = foe.health or '?'
    local mana = foe.mana or '?'
    local hand = foe.hand or '?'
    local vicious = foe.vicious or false
    
    if hasPower then
    	template
	        :node(deckHeader)
	        :node(powerImage)
	        :node(powerTitle)
	        :node(powerInfo)
	else
		template
			:node(deckHeader)
    end

	local color = '#FFE3B0'
	if vicious then
		color = '#FF7474'
		if tonumber(health) then health = tostring(tonumber(health) + 10) end
		if tonumber(mana) then mana = tostring(tonumber(mana) + 1) end
		if tonumber(hand) then hand = tostring(tonumber(hand) + 1) end
	end
    
    template
        :tag('div')
            :addClass('deck-info')
            :css('position', 'absolute')
            :css('top', '320px')
            :css('left', '15px')
            :css('font-size', '20px')
            :css('color', '#FFE3B0')
            :tag('span')
                :addClass('inline-image')
                :wikitext('[[File:Follower card.png|25px|link=LoR:Follower]]' .. followerCount .. '&nbsp;')
            :done()
            :tag('span')
                :addClass('inline-image')
                :wikitext('[[File:Spell card.png|25px|link=LoR:Spell]]' .. spellCount .. '&nbsp;')
            :done()
            :tag('span')
                :addClass('inline-image')
                :wikitext('[[File:Keyword Landmark.png|25px|link=LoR:Landmark]]' .. landmarkCount .. '&nbsp;')
            :done()
            :tag('span')
                :addClass('inline-image')
                :wikitext('[[File:Keyword Equipment.png|25px|link=LoR:Equipment]]' .. equipmentCount .. '&nbsp;')
            :done()
        :done()
        :tag('div')
        	:addClass('deck-info')
            :css('position', 'absolute')
            :css('top', '360px')
            :css('left', '15px')
            :css('font-size', '20px')
            :css('color', color)
            :tag('span')
                :addClass('inline-image')
                :wikitext('[[File:PoC Health icon.png|25px|link=LoR:Nexus Health]]' .. health .. '&nbsp;')
            :done()
            :tag('span')
                :addClass('inline-image') 
                :wikitext('[[File:LoR mana icon alt.png|25px|link=LoR:Terminology#Mana]]' .. mana .. '&nbsp;')
            :done()
            :tag('span')
                :addClass('inline-image')
                :wikitext('[[File:Keyword Draw.png|25px|link=]]' .. hand .. '&nbsp;')
            :done()
            :tag('span')
                :addClass('inline-image')
                :wikitext(lib.ternary(vicious,frame:preprocess('  {{PoCPowerIconOverlay|04SI017-full.png|Special|30}}'),''))
            :done()
        :done()
    
    template:allDone()
    
    return tostring(template)
end

function p.pocDeckDecoder(frame)
    local args = lib.frameArguments(frame)
    local adventureData = require("Module:LoRPoCData/adventure")
    local cmp = args['campaign'] or "World Adventures"
    
    local groupBy = args['groupBy'] or 'none'
    local groupByValues = {'region', 'type', 'none'}
    if Utility.table_contains(groupByValues, groupBy) == false then
        return 'Only support Group By "region", "type" or none'
    end
    
    if args['adventure'] == nil or not adventureData[cmp][args['adventure']] then
    	return 'Adventure missing or invalid'
    end
    local adventure = adventureData[cmp][args['adventure']]
    if args['foe'] == nil or not adventure[args['foe']] then
    	return 'Foe missing or invalid'
    end
    local foe = adventure[args['foe']]
    local items = foe.items or {}
    local deckItems = {}
    
    args['code'] = args['code'] or foe.deck or nil
    if args['code'] == nil then
        return 'Empty Code'
    end
    
    local header = args['header'] or true
    if header == 'false' then
    	header = false
    end
    
    local result = {}
    
    local deckList = p.deckDataFromCode{code = args['code']}
    if type(deckList) ~= 'table' then
        return deckList
    end
    
    for _, deckData in pairs(deckList) do
        local groupByKey = ''
        if groupBy == 'region' then
            groupByKey = deckData.region
        elseif groupBy == 'type' then
            groupByKey = deckData.type .. 's'
        else
            groupByKey = 'List'
        end
        
        if result[groupByKey] == nil then
            result[groupByKey] = {}
        end
        
        if items[deckData.code] then
        	table.insert(deckItems,deckData.code)
        end
        
        
        table.insert(
            result[groupByKey],
            {
                code  = deckData.code,
                count = deckData.count,
                name  = deckData.name,
                type  = deckData.type
            }
        )
    end

    local sortedKeys = {}
    for k in pairs(result) do
        table.insert(sortedKeys, k)
    end
    table.sort(sortedKeys)
    
    local template = ""
    if header then
    	template = template .. p.pocHeaderDeck(frame)
    end
    
    local newline = "\n"
    template = template .. "<tabber>" .. newline
    template = template .. "Hide=" .. newline .. '|-|' .. newline
    for i, groupByKey in ipairs(sortedKeys) do
        if i ~= 1 then
            template = template .. "|-|" .. newline
        end
        template = template .. groupByKey .. "=" .. newline .. "'''Code:''' <span style='word-wrap: break-word;'>" .. args['code'] .. '</span>' .. newline

        local regionData = result[groupByKey]
        table.sort(
            regionData,
            function(a, b)
                if (a.type == b.type) then
                    return a.name < b.name
                end
                return a.type < b.type
            end
        )

        local type = ""
        local isFirst = true
        for _, cardData in ipairs(regionData) do
            if cardData.type ~= type then
                if (type ~= "") then
                    template = template .. "|}" .. newline
                end
                template = template .. "{| class=\"wikitable\"" .. newline .. "! colspan=5 style=\"background-color: #374b65; text-align: left;\" |" .. cardData.type .. "s" .. newline .. "|-" .. newline
				isFirst = true
                type = cardData.type
            end
            
            local cardCodeTemplate = ""
            
			if isFirst then
				cardCodeTemplate = cardCodeTemplate .. '|-' .. newline
				isFirst = false
			else
				cardCodeTemplate = cardCodeTemplate .. '|' .. newline
				isFirst = true
			end
			
            cardCodeTemplate = cardCodeTemplate .."| {{LoR|" .. cardData.name
            if cardData.type ~= "Champion" then
                cardCodeTemplate = cardCodeTemplate .. "|code=" .. cardData.code
            end
            cardCodeTemplate = cardCodeTemplate .. "}}" .. " (x" .. cardData.count .. ")" .. newline
            
            
            cardCodeTemplate = cardCodeTemplate .. "| "
            if items[cardData.code] then
            	for _, item in ipairs(items[cardData.code])	do
            		if item:sub(1, 1) == "I" then
            			cardCodeTemplate = cardCodeTemplate .. "{{PoC|item|code=" .. item .. "}} "
            		else
            			cardCodeTemplate = cardCodeTemplate .. "{{PoC|relic|code=" .. item .. "}} "
            		end
            	end
            end
    		
            template = template .. cardCodeTemplate .. newline
        end
        template = template .. "|}" .. newline
    end
    
    local extraItems = ""
    local isFirst = true
    for key, itemList in pairs(items) do
    	if true then
    	end
    	if not Utility.table_contains(deckItems,key) then
    		if isFirst then
				extraItems = extraItems .. '|-' .. newline
				isFirst = false
			else
				extraItems = extraItems .. '|' .. newline
				isFirst = true
			end
        	extraItems = extraItems .. "| {{LoR|code=" .. key .. "}}" .. newline .. "| "
    		for _, item in ipairs(itemList)	do
        		if item:sub(1, 1) == "I" then
        			extraItems = extraItems .. "{{PoC|item|code=" .. item .. "}} "
        		else
        			extraItems = extraItems .. "{{PoC|relic|code=" .. item .. "}} "
        		end
    		end
    		extraItems = extraItems .. newline
    	end
    end
    
    if extraItems ~= "" then
    	if groupBy == 'region' or groupBy == 'type' then
    		template = template .. "|-|" .. newline ..  "Non-deck=" .. newline .. "'''Code:''' " .. args['code'] .. newline	
    	else
    		-- template = template .. ";Non-deck cards with items" .. newline
    	end
    	template = template .. "{| class=\"wikitable\"" .. newline .. "! colspan=5 style=\"background-color: #374b65; text-align: left;\" | Non-deck cards with items" .. newline .. "|-" .. newline .. extraItems .. "|}" .. newline
    end
    
    template = template .. "</tabber>"

    return frame:preprocess(template)
end

function p.championRoster(frame)
    local args = lib.frameArguments(frame)
    	
	local size = args["size"] or "120px"
	
    local lorData  = require('Module:LoRData/data')
    local lorTable = {}
    local s        = ""
    
    local REGION_CODES = {
	    DE = "Demacia",
	    FR = "Freljord",
	    IO = "Ionia",
	    NX = "Noxus",
	    PZ = "Piltover and Zaun",
	    SI = "Shadow Isles",
	    BW = "Bilgewater",
	    SH = "Shurima",
	    MT = "Targon",
	    BC = "Bandle City",
	    RU = "Runeterra"
	}
    
    local cardtable = {}
    for cardcode in pairs(lorData) do
        table.insert(cardtable, cardcode)
    end
    table.sort(cardtable)
    
    local championtable = {}
    
    for i, cardcode in pairs(cardtable) do
        repeat
            local t        = lorData[cardcode]
            if t.rarity ~= 'Champion' then
                break
            end
            
            t['code'] = cardcode
            table.insert(championtable, t)
            
            break
        until true
    end
    -- END
    table.sort(championtable, function(a, b) return a['name'] < b['name'] end)
    
    local rosterText = '' -- Temporarily will be class poc-roster
    rosterText = rosterText .. '<div class="poc-roster-container" style="display: flex; flex-direction: column; margin: 0 auto; padding: 5px; text-align: center;">'
    rosterText = rosterText .. '<div class="poc-roster" style="display: flex; justify-content: space-evenly; flex-wrap: wrap;">'
    
    for _, card in pairs(championtable) do
    	rosterText = rosterText .. '<div class="poc-roster-champion" style="padding:0.5em;" data-champion="' .. card.name .. '" data-searchname="' .. card.name .. '" data-game="lor"'
    	if card.regions then
    		rosterText = rosterText .. ' data-region="' .. table.concat(card.regions, ", ") .. '">'
    	else
    		rosterText = rosterText .. ' data-region="' .. REGION_CODES[card.code:sub(3,4):upper()] .. '">'
    	end
    	rosterText = rosterText .. '[[File:' .. card.code .. '.png|' .. size .. '|link=LoR:' .. card.name .. ']] <br> [[LoR:' .. card.name .. '|' .. card.name .. ']]</div>'
    end
    
    rosterText = rosterText .. '</div>'
    rosterText = rosterText .. '</div>'
    
    return rosterText
end

function p.championRosterPoC(frame)
    local args = lib.frameArguments(frame)
    	
	local size = args["size"] or "120px"
	
    local pocData  = require('Module:LoRPoCData/data')
    local lorTable = {}
    local s        = ""
    
    local rosterText = ''
    rosterText = rosterText .. '<div class="poc-roster-container" style="display: flex; flex-direction: column; margin: 0 auto; padding: 5px; text-align: center;">'
    rosterText = rosterText .. '<div class="poc-roster" style="display: flex; justify-content: space-evenly; flex-wrap: wrap;">'
    
    pocData = pocData["Champion"]
    local championtable = {}
    for champion, data in pairs(pocData) do
    	if (data["playable"]) then
        	table.insert(championtable, data)
        end
    end
    table.sort(championtable, function(a, b) return a.name < b.name end)
    
    for _, data in pairs(championtable) do
    	rosterText = rosterText .. '<div class="poc-roster-champion" style="padding:0.5em;" data-champion="' .. data.name .. '" data-searchname="' .. data.name .. '" '
    	rosterText = rosterText .. 'data-game="lor" data-playstyle="' .. (data.playable.style or "no") .. '" data-difficulty="' .. (data.playable.difficulty or "no") .. '" '
    	rosterText = rosterText .. 'data-region="' .. table.concat(data.playable.region, ", ") .. '" '
    	if data.playable.constellationRegion then
    		rosterText = rosterText ..'data-constellation="' .. data.playable.constellationRegion .. ', hasConstellation" '
    	else
    		rosterText = rosterText ..'data-constellation="constellation-none" '
    	end
    	rosterText = rosterText .. '>[[File:' .. data.id .. '.png|' .. size .. '|link=LoR:' .. data.name .. '/PoC]] <br> [[LoR:' .. data.name .. '/PoC|' .. data.name .. ']]</div>'
    end
    
    rosterText = rosterText .. '</div>'
    rosterText = rosterText .. '</div>'
    
    return rosterText
end

function p.getPoCChampionGrid(frame)
	-- Author: @Bladenite
	local args = lib.frameArguments(frame)
    	
	local size = args["size"] or "120px"
	local pocData  = require('Module:LoRPoCData/data')
	local gridText = ''
	
	-- Creating outer container
	gridText = gridText .. '<div class="poc-champions-flexbox-container" style="display: flex; flex-wrap: wrap; gap: 7.5px; justify-content: space-around;">'
	
	-- Getting playable champions data
	pocData = pocData["Champion"]
    local championtable = {}
    for champion, data in pairs(pocData) do
    	if (data["playable"]) then
        	table.insert(championtable, data)
        end
    end
    table.sort(championtable, function(a, b) return a.name < b.name end)
    
    for _, data in pairs(championtable) do
    	gridText = gridText .. '<div class="poc-champion-container" '
    	
    	-- Adding data for search parameters
    	gridText = gridText .. 'data-champion="' .. data.name .. '" data-searchname="' .. data.name .. '" '
    	gridText = gridText .. 'data-game="lor" data-playstyle="' .. (data.playable.style or "no") .. '" data-difficulty="' .. (data.playable.difficulty or "no") .. '" '
    	gridText = gridText .. 'data-region="' .. table.concat(data.playable.region, ", ") .. '" '
    	if data.playable.constellationRegion then
    		gridText = gridText ..'data-constellation="' .. data.playable.constellationRegion .. ', hasConstellation" '
    	else
    		gridText = gridText ..'data-constellation="constellation-none" '
    	end
    	gridText = gridText .. '>' --closing div
    	
    	-- Building champion board
    	gridText = gridText .. '<div class="poc-champion-circle"> [[File:' .. data.id .. '-circle.png|75px|link=LoR:' .. data.name .. '/PoC]]</div>'
    	gridText = gridText .. '<div class="poc-champion-name"> [[LoR:' .. data.name .. '/PoC|' .. data.name .. ']]</div>'
    	gridText = gridText .. '<div class="poc-champion-region">'
    	if #data.playable.region == 1 then
    		gridText = gridText .. '[[File:' .. data.playable.region[1] .. ' LoR Region.png|35px|link=]]</div>'
    	elseif #data.playable.region == 2 then
    		gridText = gridText .. '[[File:' .. data.playable.region[1] .. ' LoR Region.png|35px|link=]] '
    		gridText = gridText .. '[[File:' .. data.playable.region[2] .. ' LoR Region.png|35px|link=]]</div>'
    	end
    	if data.playable.style then
    		gridText = gridText .. '<div class="poc-champion-info"><span class="poc-' .. data.playable.style:lower() .. '">[[File:' .. data.playable.style .. ' LoR icon.png|20px|link=]] ' .. data.playable.style .. '</span> • '
    	end
    	if data.playable.difficulty then
    		gridText = gridText .. '<span class="poc-' .. data.playable.difficulty:lower() .. '"> ' .. data.playable.difficulty .. '</span></div>'
    	end
    	
    	gridText = gridText .. '<div class="poc-champion-power" style="display: inline-flex; flex-wrap: wrap; justify-content: center;'
    	if data.name ~= 'Aurelion Sol' then
    		gridText = gridText .. 'width: 120px;' -- Controls the number of powers in one line (120px -> 3 powers per line)
    	end
    	gridText = gridText .. '>'
    	
    	-- Ordering the powers correctly
    	for i, power in ipairs(data.playable.power) do
    		if power.code then
	    		gridText = gridText .. '<span class="skin-icon" style="white-space: nowrap; order: ' .. tostring(i) .. ';"'
	    		gridText = gridText .. ' data-game="lor" data-skin="PoC" data-champion="' .. power.code .. '"><span style="position:relative;text-indent:0;">'
	    		gridText = gridText .. '[[File:' .. power.code .. '.png|30px|link=LoR:' .. data.name .. '/PoC]]&#8202;&#8202;</span></span>'
    		end
    	end
    	gridText = gridText .. '</div>'
    	if data.support then
    		gridText = gridText .. '<div class="poc-champion-reinforcement"><span class="lor-tooltip" data-param="' .. data.id .. ',' .. data.support[1] .. ',' .. data.support[2] .. ', 50%">[[File:LoR Card icon.png|17px|link=|alt=Reinforcements]]</span></div>'
    	end
    	
    	-- Closing champion container
    	gridText = gridText .. '</div>'
    end
	-- Closing grid container
    gridText = gridText .. '</div>'
    
    return gridText
end

function p.pocChampionNavigation(frame)
	local args = lib.frameArguments(frame)
    	
	local size = args["size"] or "80px"
	local constellationOnly = args["constellationOnly"] or false
	
    local pocData  = require('Module:LoRPoCData/data')
    local lorTable = {}
    local s = ""
    local navText = ""
    
    pocData = pocData["Champion"]
    local championtable = {}
    for champion, data in pairs(pocData) do
    	if constellationOnly ~= false then
    		if (data["playable"]) and (data["playable"]["constellationRegion"]) then
        		table.insert(championtable, data)
        	end
    	else
    		if (data["playable"]) then
        		table.insert(championtable, data)
        	end
    	end
    end
    table.sort(championtable, function(a, b) return a.name < b.name end)
    
    for _, data in pairs(championtable) do
    	navText = navText .. '[[File:' .. data.id .. '.png|' .. size .. '|link=LoR:' .. data.name .. '/PoC]]'
    end
    
    return navText
end

function p.pocWeekliesDateRange()
	local today = os.date("*t")
	local current_wday = today.wday
	local days_to_add = (2 - current_wday) % 7  -- 2 corresponds to Monday
	
	if days_to_add == 0 then
	    days_to_add = 7
	end
	days_to_add = days_to_add - 1  -- Last day is non-inclusive
	
	local next_sunday_time = os.time(today) + days_to_add * 86400  -- 86400 seconds in a day
	local next_sunday = os.date("*t", next_sunday_time)
	local next_month_name = os.date("%B", next_sunday_time)
	
	local prev_monday_time = next_sunday_time - 6 * 86400
	local prev_monday = os.date("*t", prev_monday_time)
	local prev_month_name = os.date("%B", prev_monday_time)
	
	return string.format("%s %d – %s %d", prev_month_name, prev_monday.day, next_month_name, next_sunday.day)
end

function p.subtypeContent(frame)
    local args = lib.frameArguments(frame)
    
    local cardcode = args['code'] or args[1] or nil
    
    if not cardcode then
    	return ''
    end
    
    local lorData   = mw.loadData('Module:LoRData/data')
    
    subtypetext = ''
    for _, subtype in pairs(lorData[cardcode].subtype) do
    	subtypelink = '[[LoR:' .. subtype .. '|' .. subtype .. ']]'
    	if subtypetext ~= '' then
    		subtypetext = subtypetext .. ", " .. subtypelink
    	else
    		subtypetext = subtypetext .. subtypelink
    	end
    	
    	subtypetext = subtypetext .. "[[" .. "Category:LoR " .. subtype .. "]]"
    end
    
    return frame:preprocess(subtypetext)
end

function p.regionContent(frame)
    local args = lib.frameArguments(frame)
    
    local cardcode = args['code'] or args[1] or nil
    if not cardcode then
    	return ''
    end
    
    local lorData   = mw.loadData('Module:LoRData/data')
    if not lorData[cardcode] then
    	return ''
	end
	
    local regions = {}
    if not lorData[cardcode].regions then
    	local regionCode = string.sub(cardcode, 3, 4)
    	for _, regionData in pairs(REGIONS) do
	    	if regionData.code == regionCode then
	    		table.insert(regions, regionData.name)
	    		break
	    	end
	    end
    else
    	regions = lorData[cardcode].regions
    end
    
    regiontext = ''
    for _, region in pairs(regions) do
    	regionlink = '[[File:' .. region .. ' LoR Region.png|20px]] '
    	regionlink = regionlink .. '[[LoR:' .. region .. '|' .. region .. ']]'
    	if regiontext ~= '' then
    		regiontext = regiontext .. ", " .. regionlink
    	else
    		regiontext = regiontext .. regionlink
    	end
    	
    	regiontext = regiontext .. "[[" .. "Category:LoR " .. region .. "]]"
    end
    
    if regiontext == '' then
    	regiontext = 'N/A'
    end
    
    return frame:preprocess(regiontext)
end

function p.artworkContent(frame)
    local args = lib.frameArguments(frame)
    
    local cardcode = args['code'] or args[1] or nil
    
    if not cardcode then
    	return ''
    end
    
    local lorData   = mw.loadData('Module:LoRData/data')
    local imageSize = '125px'
    
    artworkText = "<ul style='margin-left:0;'>"
    artworkText = artworkText .. "<li style='display:inline-block;text-indent:0px;'>[[File:" .. cardcode .. "-full.png|thumb|"
    	.. imageSize .. "|Primary|left]]</li>"

    if lorData[cardcode].alternate then
        artworkText = artworkText .. "<li style='display:inline-block;text-indent:0px;'>[[File:" .. cardcode
        	.. "-alt-full.png|thumb|" .. imageSize .. "|Censored|left]]"
    end
	
    for _, artwork in pairs(lorData[cardcode].artworks or {}) do
        local extension = 'png'
        local artworkParts = lib.split(artwork, "-")
        
        local HD            = false
	    local alternate     = false
	    local display       = false
	    local prerelease    = false
	    local old           = false
	    
	    for k, v in pairs(artworkParts) do
		  if (v == 'hd') then
		  	HD = true
		  end
		  if (v == 'alt') then
		  	alternate = true
		  end
		  if (v == 'display') then
		  	display = true
		  end
		  if (v == 'pre') then
		  	prerelease = true
		  end
		  if (v == 'old') then
		  	old = true
		  end
		end
        
        local artworkContents = {}
	
		if HD then
	    	table.insert(artworkContents, 'HD')
	    	extension = 'jpg'
	    end
	    if prerelease then
	    	table.insert(artworkContents, 'Pre-Release')
	    end
	    if alternate then
	    	table.insert(artworkContents, '[[LoR:List of cards/Alternate|Censored]]')
	    end
	    if display then
	    	table.insert(artworkContents, '[[League Displays]]')
	    	extension = 'jpg'
	    end
	    if old then
	    	table.insert(artworkContents, 'Old')
	    end
		
		if #artworkContents >= 1 then
			artworkContent = table.concat(artworkContents, " ")
		else
			artworkContent = 'Uncategorized'
		end

        artworkLink = "<li style='display:inline-block;text-indent:0px;'>[[File:" .. cardcode .. "-"
        	.. artwork .. "-full." .. extension .. "|thumb|" .. imageSize .. "|" .. artworkContent .. "|left]]</li>"

        if artworkText ~= '' then
            artworkText = artworkText .. " " .. artworkLink
        else
            artworkText = artworkText .. artworkLink
        end
    end

    artworkText = artworkText .. "</ul>"
    
    return frame:preprocess(artworkText)
end

function p.artworkTooltip(frame)
    local args = lib.frameArguments(frame)
    
    local artworkCode = args['code'] or args[1] or nil
    
    if not artworkCode then
    	return ''
    end
    
    local artworkList = lib.split(artworkCode, ",")
    
    local width = 250
    if string.find(artworkList[1], '-full') then
		width = 300
    end

	if string.find(artworkList[#artworkList], '%%') then
		width = math.floor(width * (tonumber(string.match(artworkList[#artworkList], '%d+')/100) or 1))
		table.remove(artworkList, #artworkList)
    end
	
	if (#artworkList > 4) then
		width = width / 2
	end
	
	local artworkTooltip = '<div style="display:inline-flex; flex-wrap:wrap; width:' .. math.min(width * 4, width * #artworkList) .. 'px; box-shadow: rgb(1, 10, 19) 0px 0px 25px 15px;">'
    
    for k, v in pairs(artworkList) do
		artworkTooltip = artworkTooltip .. '<div style="display:inline-block; background-color: rgb(1, 10, 19); flex: 1 1;">'
	  	artworkTooltip = artworkTooltip .. '[[File:' .. v .. '.png|' .. width .. 'px]]'
	  	artworkTooltip = artworkTooltip .. '</div>'
	end
    
	artworkTooltip = artworkTooltip .. '</div>'
    
    return artworkTooltip
end

function p.randomCard(frame)
	local args = lib.frameArguments(frame)
		
    local lorData   = mw.loadData('Module:LoRData/data')
    
    local keys = {}
    for key, _ in pairs(lorData) do
        keys[#keys+1] = key --Store keys in another table
    end
    
    local randomCode
    -- Random
    math.randomseed(os.time())
	math.random(); math.random(); math.random()
	for x = 1,10 do
	    randomCode = keys[math.random(1, #keys)]
	end

    local randomCard = lorData[randomCode]
    local link = 'LoR:' .. randomCode
    
    local text = '[[File:' .. randomCode .. '.png|250px|link=' .. link .. ']]'
    text = text .. '[[' .. link .. '|' .. randomCard.name .. ']]'
    
    local cardMode = randomCard.mode
    if cardMode then
	    text = text .. ' ([[LoR:List of cards/' .. cardMode .. '|' .. cardMode .. ']])' 
	end
    
    return frame:preprocess(text)
end

-- VarInit
VarInt.AllButMSB = 0x7f
VarInt.JustMSB = 0x80

function VarInt.Pop(bytes)
    local result = 0
    local currentShift = 0
    local bytesPopped = 0

    for i = 1, #bytes do
        bytesPopped = bytesPopped + 1
        local current = BitOperator.AND(bytes[i], VarInt.AllButMSB)
        result = BitOperator.OR(result, BitOperator.LEFT_SHIFT(current, currentShift))

        if BitOperator.AND(bytes[i], VarInt.JustMSB) ~= VarInt.JustMSB then
            Utility.table_splice(bytes, 1, bytesPopped)
            return result
        end

        currentShift = currentShift + 7
    end

    -- error('Invalid Deck Code: Byte array did not contain valid varints.')
    return nil
end

-- BitOperator
function BitOperator.OR(m, n)
    local tbl_m = Utility.to_bits(m)
    local tbl_n = Utility.to_bits(n)
    Utility.expand(tbl_m, tbl_n)

    local tbl = {}
    local rslt = math.max(table.getn(tbl_m), table.getn(tbl_n))
    for i = 1, rslt do
        if (tbl_m[i] == 0 and tbl_n[i] == 0) then
            tbl[i] = 0
        else
            tbl[i] = 1
        end
    end

    return Utility.table2number(tbl)
end

function BitOperator.AND(m, n)
    local tbl_m = Utility.to_bits(m)
    local tbl_n = Utility.to_bits(n)
    Utility.expand(tbl_m, tbl_n)

    local tbl = {}
    local rslt = math.max(table.getn(tbl_m), table.getn(tbl_n))
    for i = 1, rslt do
        if (tbl_m[i] == 0 or tbl_n[i] == 0) then
            tbl[i] = 0
        else
            tbl[i] = 1
        end
    end

    return Utility.table2number(tbl)
end

function BitOperator.RIGHT_SHIFT(n, bits)
    Utility.check_int(n)

    local high_bit = 0
    if (n < 0) then
        -- negative
        n = BitOperator.NOT(math.abs(n)) + 1
        high_bit = 2147483648 -- 0x80000000
    end

    for i = 1, bits do
        n = n / 2
        n = BitOperator.OR(math.floor(n), high_bit)
    end
    return math.floor(n)
end

function BitOperator.NOT(n)
    local tbl = Utility.to_bits(n)
    local size = math.max(table.getn(tbl), 32)
    for i = 1, size do
        if (tbl[i] == 1) then
            tbl[i] = 0
        else
            tbl[i] = 1
        end
    end
    return Utility.table2number(tbl)
end

function BitOperator.LEFT_SHIFT(n, bits)
    Utility.check_int(n)

    if (n < 0) then
        -- negative
        n = BitOperator.NOT(math.abs(n)) + 1
    end

    for i = 1, bits do
        n = n * 2
    end
    return BitOperator.AND(n, 4294967295) -- 0xFFFFFFFF
end

-- Utility
function Utility.str_split(str, size)
    local result = {}
    for i = 1, #str, size do
        table.insert(result, str:sub(i, i + size - 1))
    end
    return result
end

function Utility.dec2bin(num)
    local result = ""
    repeat
        local halved = num / 2
        local int, frac = math.modf(halved)
        num = int
        result = math.ceil(frac) .. result
    until num == 0
    return result
end

function Utility.padRight(str, length, char)
    while #str % length ~= 0 do
        str = str .. char
    end
    return str
end

function Utility.padLeft(str, length, char)
    while #str % length ~= 0 do
        str = char .. str
    end
    return str
end

function Utility.table2number(tbl)
    local n = table.getn(tbl)

    local rslt = 0
    local power = 1
    for i = 1, n do
        rslt = rslt + tbl[i] * power
        power = power * 2
    end

    return rslt
end

function Utility.to_bits(n)
    Utility.check_int(n)
    if (n < 0) then
        -- negative
        return Utility.to_bits(bit.bnot(math.abs(n)) + 1)
    end
    -- to bits table
    local tbl = {}
    local cnt = 1
    while (n > 0) do
        local last = math.mod(n, 2)
        if (last == 1) then
            tbl[cnt] = 1
        else
            tbl[cnt] = 0
        end
        n = (n - last) / 2
        cnt = cnt + 1
    end

    return tbl
end

function Utility.check_int(n)
    -- checking not float
    if (n - math.floor(n) > 0) then
        error("trying to use bitwise operation on non-integer!")
    end
end

function Utility.expand(tbl_m, tbl_n)
    local big = {}
    local small = {}
    if (table.getn(tbl_m) > table.getn(tbl_n)) then
        big = tbl_m
        small = tbl_n
    else
        big = tbl_n
        small = tbl_m
    end
    -- expand small
    for i = table.getn(small) + 1, table.getn(big) do
        small[i] = 0
    end
end

function Utility.table_splice(tab, idx, n, ...) --> removed
    local values = {...}
    local init_tab_size = #tab

    local removed = {}
    if n > 0 then
        for i = idx, (idx + n - 1) do
            table.insert(removed, tab[i])
            tab[i] = nil
        end
    end

    local tail = {}
    for i = (idx + n), init_tab_size do
        table.insert(tail, tab[i])
        tab[i] = nil
    end

    local i = idx
    for _, v in ipairs(values) do
        tab[i] = v
        i = i + 1
    end

    i = idx + #values
    for _, v in ipairs(tail) do
        tab[i] = v
        i = i + 1
    end

    return removed
end

function Utility.table_contains(table, val)
   for i = 1, #table do
      if table[i] == val then 
         return true
      end
   end
   return false
end

function Utility.comma_value(n) -- credit http://richard.warburton.it
	local left, num, right = string.match(n, '^([^%d]*%d)(%d*)(.-)$')
	return left .. (num:reverse():gsub('(%d%d%d)', '%1,'):reverse()) .. right
end

return p
-- </pre>
-- [[Category:Lua]]