Module:SportsRankings
require('strict');
 
 
 local p = {} 
 
 local error_msg = '&lt;span style=\&quot;font-size:100%\&quot; class=\&quot;error\&quot;&gt;&lt;code style=\&quot;color:inherit; border:inherit; padding:inherit;\&quot;&gt;&amp;#124;_template=&lt;/code&gt; missing or empty&lt;/span&gt;';
 
 -- data for various rankings held in module subpages, e.g. &quot;Module:SportsRankings/data/FIFA World Rankings&quot;
 local data = {}      --[[ parameters containing data help in three tables
 						data.source = {}     -- parameters for using in cite web (title, url, website)
 						data.updated = {}    -- date of latest update (month, day, year)
 						data.rankings = {}   -- the rankings list (country code, ranking, movement)
 					    data.alias = {}      -- alias list (country code, country name [=key])
 					    
 					--]]
 
 local  templateArgs = {} -- contains arguments from template involking module
 
 
 local function getArgs(frame)
 	local parents = mw.getCurrentFrame():getParent()
 		
 	for k,v in pairs(parents.args) do
 		--check content
 		if v and v ~= &quot;&quot; then
 			templateArgs[k]=v --parents.args[k]
 		end
 	end
 	for k,v in pairs(frame.args) do
 		--check content
 		if v and v ~= &quot;&quot; then
 			templateArgs[k]=v --parents.args[k]
 		end
 	end
 	-- allow empty caption to blank default
 	if parents.args['caption'] then templateArgs['caption'] = parents.args['caption'] end
 	if frame.args['caption'] then templateArgs['caption'] = frame.args['caption'] end
 end
 
 local function loadData(frame)
     
     local source = frame.args[1] -- source of rankings e.g. FIFA World Rankings
     data = require('Module:SportsRankings/data/'.. source);
     
 end
 
 local function getDate(option)
    
    local dateTable = data.updated         -- there must be date table (data.updated)
                                           -- TODO add a warning and/or category
    if option == &quot;LAST&quot; then 
    		local lastDateTable = data.previous 
    		if lastDateTable then             -- there might not be a previous data table (data.previous)
    			dateTable = lastDateTable
 	   else 
 	   		return &quot;No previous date available (data.updated missing)&quot;
        end
    end
    
    if templateArgs['mdy'] and templateArgs['mdy'] ~= &quot;&quot; then
    	   return dateTable['month'] .. &quot; &quot; .. dateTable['day'] .. &quot;, &quot; .. dateTable['year']
    else
    	   return dateTable['day'] .. &quot; &quot; .. dateTable['month'] .. &quot; &quot; .. dateTable['year']
    end
 end
 
 local function addCiteWeb(frame)  -- use cite web template
 	
 	return frame:expandTemplate{ title = 'cite web' , args = {
     		url = data.source['url'],            --&quot;https://www.fifa.com/fifa-world-ranking/ranking-table/men/index.html&quot;, 
 			title = data.source['title'],        -- &quot;The FIFA/Coca-Cola World Ranking&quot;,
 			website = data.source['website'],    --&quot;FIFA&quot;,
 			['date'] = getDate(),
 			['access-date'] = getDate()
 			}}
 end
 local function addReference(frame)
 	
 	local text = &quot;&quot;
 	if data.source['text'] then text = data.source['text'] end
 	
 	return frame:expandTemplate{ title = 'refn' , args = {
 		name=frame.args[1],                                 --ranking used, e.g. &quot;FIFA World Rankings&quot;,
 	    text .. addCiteWeb(frame)
 	}}
 
 end
 
 --[[ the main function returning ranking for one country
       - takes three-letter country code or name of country as parameters
       - displays as rank | movement |date
 ]]
 function p.main(frame)
 	
     getArgs(frame) -- returns args table having checked for content
     loadData(frame)
     local outputString = &quot;&quot;
     local validCode = false
     local country = templateArgs[2] -- country name or county code passed as parameter
     local rank, move
     
     if string.len( country) ==  3 then -- if we have a three letter country code 
 	    for _,u in pairs(data.alias) do  -- run through alias list { 3-letter code, country name }
 	    	if u[1]==country then        -- if code = passed parameter
 	       		country = u[2]           -- set country name as key for ranking table
 	       		validCode = true
 	       		break
 	       	end
 	    end    
 	    -- if no match of code to country name, set category
 	    if not validCode then
 	    	outputString=&quot;[[Category:Pages using SportsRankings with unknown parameters]]&quot; .. outputString
         end
     end
     for _,v in pairs(data.rankings) do
     	if v[1]==country then 
        		rank = v[2]    -- get rank
        		move = v[3]    -- get move from last ranking
        		break
        	end
     end
     if not rank then -- no ranking found (do we want a tracking for no rank found?)
     	rank = 'NR' 
 	    --outputString=&quot;[[Category:Pages using SportsRankings with unknown parameters]]&quot; .. outputString
         --outputString=&quot;[[Category:Pages using SportsRankings with no ranking]]&quot; .. outputString
 	end
 	
 	if rank ~= 'NR' then
 		outputString = outputString .. ' ' .. rank .. ' '
 		if move &lt; 0 and math.abs( move ) == math.abs( rank ) then -- new teams in ranking: move = -ranking
 			outputString = outputString .. frame:expandTemplate{ title = 'new entry' } 
 	    elseif move == 0 then                                    -- if no change in ranking
 	    	outputString = outputString .. frame:expandTemplate{ title = 'steady' } 
 	    elseif move &lt; 0 then                                 --  if ranking down
 	    	outputString = outputString .. frame:expandTemplate{ title = 'decrease' } 
 	    	if move ~= -1000 then outputString = outputString .. ' ' .. math.abs(move) end
 	    elseif move &gt; 0 then                                 -- if ranking up
 	    	outputString = outputString .. frame:expandTemplate{ title = 'increase' } 
 	    	if move ~= 1000 then outputString = outputString .. ' ' .. move end
 	    end	
     else
     	outputString = outputString .. frame:expandTemplate{ title = 'Abbr', args = { &quot;NR&quot;, &quot;Not ranked&quot;}  }
     	--	{{Abbr|NR|Not ranked}} 
 	end
 	outputString = outputString .. ' (' .. getDate() .. ')'
 	outputString = outputString .. addReference(frame)
     
     return outputString
 	
 end
 
 --[[  outputs a table of the rankings 
         called by list() or list2() 
         positional parameters - |ranking|first|last the ranking to use, fist and last in table
         other parameters: |style=               -- CSS styling
                           |headerN= footerN=    -- displays header and footer rows with additional information
                           |caption=             -- value of caption to display
                                                 -- by default it generates a caption
                                                 -- this can be suppressed with empty |caption=
 ]]
 local function table(frame, ranking, first,last)
 
     local styleString = &quot;&quot;
     if templateArgs['style'] and templateArgs['style'] ~= &quot;&quot; then styleString = templateArgs['style'] end
     
     
     local sublist2 = { &quot;ENG&quot;, &quot;SCO&quot;, &quot;WAL&quot;, &quot;IRE&quot;, &quot;NIR&quot;, &quot;FRA&quot;, &quot;England&quot;, &quot;France&quot;, &quot;Germany&quot; }
     local sublist3 = { &quot;AFG&quot;,&quot;AUS&quot;,&quot;BAN&quot;,&quot;BHR&quot;,&quot;BHU&quot;,&quot;BRU&quot;,&quot;CAM&quot;,&quot;CHN&quot;,&quot;GUM&quot;,&quot;HKG&quot;,&quot;IDN&quot;,&quot;IND&quot;,&quot;IRN&quot;,&quot;IRQ&quot;,&quot;JOR&quot;,
     	              &quot;JPN&quot;,&quot;KGZ&quot;,&quot;KOR&quot;,&quot;KSA&quot;,&quot;KUW&quot;,&quot;LAO&quot;,&quot;LIB&quot;,&quot;MAC&quot;,&quot;MAS&quot;,&quot;MDV&quot;,&quot;MNG&quot;,&quot;MYA&quot;,&quot;NEP&quot;,&quot;OMA&quot;,&quot;PAK&quot;,
     	              &quot;PHI&quot;,&quot;PLE&quot;,&quot;PRK&quot;,&quot;QAT&quot;,&quot;SIN&quot;,&quot;SRI&quot;,&quot;SYR&quot;,&quot;THA&quot;,&quot;TJK&quot;,&quot;TKM&quot;,&quot;TLS&quot;,&quot;TPE&quot;,&quot;UAE&quot;,&quot;UZB&quot;,&quot;VIE&quot;,
     	              &quot;YEM&quot; }
     local lastRank = 0
     local selectCount = 0
     local selectData = nil
     local selectList = nil
     if templateArgs['select'] then 
     	if data.confederation[templateArgs['select']] then
 	    	selectList = templateArgs['select']
 	    	selectData = data.confederation[selectList]
 	    	selectCount = 1
     	end
     end
     
     -- column header customisation
     local rankHeader = templateArgs['rank_header'] or &quot;Rank&quot;
     local selectionHeader = templateArgs['selection_header'] or selectList or &quot;Rank&quot;
     local teamHeader = templateArgs['team_header'] or &quot;Team&quot;
     local pointsHeader = templateArgs['points_header'] or &quot;Points&quot;
     local changeHeader = templateArgs['change_header'] or &quot;Change&quot;
     
     --start table
     local outputString = '{| class=&quot;wikitable&quot; style=&quot;text-align:center;' .. styleString .. '&quot;'
     
     -- add default or custom caption
     local caption = ranking .. ' as of ' .. getDate() .. '.'
     if templateArgs['caption'] and templateArgs['caption']  ~= &quot;&quot; then 
     	caption = templateArgs['caption'] 
     	caption = p.replaceKeywords(caption)
     end
     if not (templateArgs['caption'] and templateArgs['caption']  == &quot;&quot;) then 
 		outputString = outputString ..	'\n|+' .. caption .. addReference(frame)
     end
     
     -- add header rows (logo, date of update etc)
     local count = 0
     local header = {}
     local tableWidth = 4
     if selectList then tableWidth = 5 end
     while count &lt; 5 do
     	count = count + 1
 	    if templateArgs['header'..count] then
 	    	header[count] = templateArgs['header'..count] 
 	    	header[count] = p.replaceKeywords( header[count])
 	    	outputString = outputString ..	'\n|-\n| colspan=&quot;'.. tableWidth .. '&quot; |' .. header[count]
 	    end
     end
     
     -- add the add part of the table
     local optionalColumn = &quot;&quot;
     if selectList then
     	optionalColumn = '\n!' .. selectionHeader 
     end
    	outputString = outputString .. '\n|-' .. optionalColumn
     	                        .. '\n!' .. rankHeader .. '\n!' .. changeHeader 
     	                        .. '\n!' .. teamHeader .. '\n!' .. pointsHeader
    
     local change,code = '', ''
     --while i&lt;last do 
     for k,v in pairs(data.rankings) do
 	   --v[2] = tonumber(v[2])
 	   if v[2] &gt;= first and v[2] &lt;= last then 
 
 		   for _,u in pairs(data.alias) do  -- get country code from name
 		    	if u[2]==v[1] then 
 		       		code = u[1]    -- if alias (country code) then use country name as key
 		       		break
 		       	end
 		    end   
 	   	   
 	   	    local continue = true
 	   	    if selectList then                 -- select from list
 	   	   	    continue = false 
 	   			for _,u in pairs(selectData) do
 	   				if u == v[1] or u == code then 
 	   					continue = true 
 	   					break
 	   				end
 	   			end
 	   	    end
 		
 			if continue ==true  then 
 	   	   
 			   local rowString = '\n|-'
 			   if selectList then 
 			   	    local selectRank = selectCount
 			   	    if v[2]==lastRank then selectRank = selectCount -1 end -- only handles two at same rank
 					rowString = rowString ..  '\n|' .. selectRank 
 					selectCount = selectCount + 1
 			   end
 			   rowString = rowString .. '\n|' .. v[2]  -- rank
 			   lastRank = v[2]
 			   
 			   local move = v[3]
 			   if move &lt; 0 and math.abs( move ) == math.abs( v[2] ) then -- new teams in ranking: move = -ranking
 					change = frame:expandTemplate{ title = 'new entry' } 
 			   elseif move == 0 then                                    -- if no change in ranking
 			    	change = frame:expandTemplate{ title = 'steady' } 
 			    elseif move &lt; 0 then                                 --  if ranking down
 			    	change = frame:expandTemplate{ title = 'decrease' } .. ' ' .. math.abs(move)
 			    elseif move &gt; 0 then                                 -- if ranking up
 			    	change = frame:expandTemplate{ title = 'increase' } .. ' ' .. move
 			    end	
 			   rowString = rowString .. '||' .. change
 			   
 	--[[		   for _,u in pairs(data.alias) do
 			    	if u[2]==v[1] then 
 			       		code = u[1]    -- if alias (country code) then use country name as key
 			       		break
 			       	end
 			    end   
 	]]		   
 			   --TODO reorganise the following with better logic
 			   --[[ template to display flag icon and team link (e.g. fb, fbw, bk, ih)
 			       e.g.  &quot;FIFA World Rankings&quot; = 'fb', &quot;FIFA Women's World Rankings&quot; 'fbw',
 			             &quot;FIBA World Rankings&quot; = 'bk', &quot;IIHF World Ranking&quot;  = 'ih'  
 			       tries with country code, then if error, tried with country name]]
 			   local countryTemplate = data.templates['flagged_team_link'] 
 			   local flagVariant = &quot;&quot;
                if data.templates.flagvar and data.templates.flagvar[code] then
             		flagVariant = data.templates.flagvar[code] 
 			   end
 			   local countryIconString = frame:expandTemplate{ title = countryTemplate, args = {code, flagVariant} }    -- country
 
 	 		   local _,test =  string.gsub( countryIconString, &quot;Template:Country data&quot;, &quot;&quot;) -- page does not exist
 	 		   if test == 1 then -- if error try country name
 	 		   	  countryIconString = frame:expandTemplate{ title = countryTemplate, args = {v[1]} }
 	 		   	  --countryIconString = &quot;testing&quot;
 	 		   else 
 	 		   	 -- countryIconString = &quot;exists&quot;
 	 		   end
 	 		   	  
 	 		   rowString = rowString .. '\n|style=&quot;text-align:left&quot;|' .. countryIconString
 			   
 			   local points = &quot;&quot;
 			   if v[4] then points = v[4] end
 			   rowString = rowString ..  '||' .. points       -- country for now, later points
 			   outputString = outputString .. rowString
 			end
 		end
 	end
 	
     -- add footer rows
     count = 0
     local footer = {}
     while count &lt; 5 do
     	count = count + 1
 	    if templateArgs['footer'..count] then
 	    	footer[count] = templateArgs['footer'..count] 
 	    	footer[count] = p.replaceKeywords(footer[count])
 	    	outputString = outputString ..	'\n|-\n| colspan=&quot;'.. tableWidth .. '&quot; |' .. footer[count]
 	    end
     end
 
 
     outputString = outputString .. &quot;\n|}&quot;
 
     return outputString
 	
 end
 
 function p.replaceKeywords(keyword)
 	keyword =  string.gsub( keyword, &quot;INSERT_UPDATE_DATE&quot;, getDate())
     keyword =  string.gsub( keyword, &quot;INSERT_LAST_DATE&quot;, getDate(&quot;LAST&quot;))
 	if string.find(keyword, &quot;INSERT_REFERENCE&quot;) then
 		keyword =  string.gsub( keyword, &quot;INSERT_REFERENCE&quot;, addReference(mw.getCurrentFrame()))
 	end
     return keyword
 end
 
 --[[ create a table of rankings
        parameters:  |ranking=        -- ranking to display (e.g. FIFA World Rankings)
                     |first= |last=   -- first and last ranking to display (defaults 1-10)
 ]]
 function p.list(frame)
 
     getArgs(frame) -- returns args table having checked for content
     loadData(frame)	
     local ranking = frame.args[1]
     
     local first = tonumber(frame.args['2']) or 1
     local last = tonumber(frame.args['3']) or 10
     
     return table(frame, ranking, first, last)
 end
 
 --[[ create a particla table of rankings above and below a country 
        parameters:  |ranking |country |span     
                                               -- ranking - the ranking to display (e.g. FIFA World Rankings)
                                               -- country - country table is centred around
                                               -- span=   - rows to display above and below country
 ]]
 function p.list2(frame)
 
     getArgs(frame) -- returns args table having checked for content
     loadData(frame)	
     local ranking = frame.args[1]
     local first, last = 1,10
     local country = frame.args[2]           -- name or code of country to center table around
     local span = frame.args[3] or 2         -- number of rows to display above and below country (default:2)
     
     if string.len(country) == 3 then        -- if three letter country code
     		for _,u in pairs(data.alias) do
 		    	if u[1]==country then 
 		       		country = u[2]          -- if country code then use country name 
 		       		break
 		       	end
 		    end   
     end
     for k,v in pairs(data.rankings) do      -- find position of country in rankings
        if v[1] == country then
        	  first = v[2]-span
        	  last = v[2]+span
        end
     end
     
     return table(frame, ranking, first, last)
 end