--------------------------------------------------------------------------------
-- Organisation of this module: the exports are listed at the bottom; their
-- direct functions are just above there, and supporting functions are higher
-- up.
--
-- Please add testcases before modifying anything.
--
--------------------------------------------------------------------------------
require('strict')

local instanceOfProp = 'P31'
local authorProp = 'P50'
local editionOrTranslationOfProp = 'P629'
local equivalentClassProp = 'P1709'
local topicsMainWikimediaPortalProp = 'P1151';

--------------------------------------------------------------------------------
-- Insert a value into a table, but not if it's already there.
local function tableInsertUnique( theTable, value )
	for _, item in pairs( theTable ) do
		if item == value then
			-- Already present
			return
		end
	end
	-- Otherwise, add the new value.
	table.insert( theTable, value )
end

--------------------------------------------------------------------------------
-- For the given author item, find the Wikisource sitelink. If there isn't one,
-- check to see if there the author has a topicsMainWikimediaPortal statement,
-- and then see if that portal has a sitelink instead.
local function get_author_sitelink( authorItem )
	local siteLink = authorItem:getSitelink( mw.language.getContentLanguage().code .. 'wikisource' )
	if ( siteLink ~= nil ) then
		return siteLink
	end
	local portalStatements = authorItem:getBestStatements( topicsMainWikimediaPortalProp )
	for _, mainPortal in pairs( portalStatements ) do
		local mainPortalId = mainPortal['mainsnak']['datavalue']['value']['id']
		local mainPortalItem = mw.wikibase.getEntity( mainPortalId )
		siteLink = mainPortalItem:getSitelink( mw.language.getContentLanguage().code .. 'wikisource' )
	end
	return siteLink
end

--------------------------------------------------------------------------------
-- Get the schema.org URL for the itemtype of the given item.
local function getSchemaorgItemtype( item )
	local schemaPrefix = 'http://schema.org/'
	for _, instanceOf in pairs( item:getBestStatements( instanceOfProp ) ) do
		local instanceOfId = instanceOf['mainsnak']['datavalue']['value']['id']
		local instanceOfItem = mw.wikibase.getEntity( instanceOfId )
		-- Now go through each of the instance-of item's class statements,
		-- seeing if we can find a matching schema.org URL.
		for _, equivClass in pairs( instanceOfItem:getBestStatements( equivalentClassProp ) ) do
			local val = equivClass['mainsnak']['datavalue']['value']
			if string.sub( val, 1, #schemaPrefix ) == schemaPrefix then
				-- This is a schema.org URL.
				return val
			end
		end
	end
	-- If we've not figured it out by now, give up with the default.
	return schemaPrefix .. 'Thing'
end

--------------------------------------------------------------------------------
-- Create the HTML (including wikitext link) for all provided authors,
-- and add it to the given authorLinks table (for later concatenation).
local function process_authors( authorLinks, authors )
	if authors == nil or #authors == 0 then
		return
	end
	for _, author in pairs( authors ) do
		local authorId = author['mainsnak']['datavalue']['value']['id']
		local authorItem =  mw.wikibase.getEntity( authorId )
		local siteLink = get_author_sitelink( authorItem )
		local authorName = authorItem:getLabel()
		if siteLink ~= nil then
			authorName = mw.title.new( siteLink ).text
		end
		local authorHtml = mw.html.create('span')
			:attr('itemprop', 'author')
			:attr('itemscope', '')
			:attr('itemtype', getSchemaorgItemtype( authorItem ) )
		local authorNameHtml = authorHtml:tag( 'span' )
		authorNameHtml:attr( 'itemprop', 'name' )
			:wikitext( authorName )
		local authorHtmlStr = tostring( authorHtml )
		if siteLink == nil then
			tableInsertUnique( authorLinks, authorHtmlStr )
		else
			tableInsertUnique( authorLinks, '[[' .. siteLink .. '|' .. authorHtmlStr .. ']]' )
		end
	end
end

--------------------------------------------------------------------------------
-- Get an Item based on what's passed in the 'wikidata' or 'page' parameters of
-- the args, or the current page's ID otherwise.
local function getItem( args )
	local id = nil
	-- If args is a table with an appropriate element, use it.
	if type( args ) == 'table' then
		if args.wikidata ~= '' and args.wikidata ~= nil then
			id = args.wikidata
		elseif args.wikidata_id ~= '' and args.wikidata_id ~= nil then
			id = args.wikidata_id
		elseif args.page ~= '' and args.page ~= nil then
			local title = mw.title.new( args.page )
			id = mw.wikibase.getEntityIdForTitle( title.nsText .. title.text )
			-- If no entity for this page, maybe it's a subpage and we should look for the root page's entity.
			if id == nil then
				id = mw.wikibase.getEntityIdForTitle( title.nsText .. title.rootText )
			end
		end
	end
	if type( args ) == 'string' and args ~= '' then
		id = args
	end
	return mw.wikibase.getEntity( id )
end

--------------------------------------------------------------------------------
-- Exported method.
--
local function author_list( args )
	local item = getItem( args )
	if item == nil then
		return ''
	end
	local authorLinks = {}

	-- Collect the authors of this item.
	local authors = item:getBestStatements( authorProp )
	process_authors( authorLinks, authors )

	-- Also collect the authors of the parent work.
	local works = item:getBestStatements( editionOrTranslationOfProp )
	for _, work in pairs( works ) do
		local workId = work['mainsnak']['datavalue']['value']['id']
		local workItem = mw.wikibase.getEntity( workId )
		authors = workItem:getBestStatements( authorProp )
		if #authors > 0 then
			process_authors( authorLinks, authors )
		end
	end

	-- Output the final list of links.
	local outHtml = mw.html.create()
	local separator = args.separator or '، و'
	local last_separator = args.last_separator or '، و'
	local i = 1
	for _, link in pairs( authorLinks ) do
		outHtml:wikitext( link )
		if i == ( #authorLinks - 1 ) then
			outHtml:wikitext( last_separator )
		elseif #authorLinks > 1 and i ~= #authorLinks then
			outHtml:wikitext( separator )
		end
		i = i + 1
	end
	return tostring( outHtml )
end

--------------------------------------------------------------------------------
-- Exported method.
--
local function inline( args )
	local item = getItem( args )
	local outHtml = mw.html.create()
	mw.logObject(args)
	mw.logObject(item)
	if ( item == nil ) then
		outHtml:wikitext( '<span class="error">غير قادر على تحديد عنصر ويكي بيانات</span>' )
		return tostring( outHtml )
	end

	-- Make sure it's an edition.
	local editionOrTranslationOfStmts = item:getBestStatements( editionOrTranslationOfProp )
	if #editionOrTranslationOfStmts == 0 then
		outHtml:wikitext( '<span class="error">' .. item.id .. ' ليس طبعة أو ترجمة لعمل (يفتقر إلى P629)</span>' )
		return tostring( outHtml )
	end


	-- Title/label.
	local title = item:getSitelink( 'arwikisource' )
	local label = item:getLabel( 'ar' )
	local hasWikisourcePage = false
	if title == nil or title == '' then
		title = label
	else
		hasWikisourcePage = true
		title = '[[' .. title .. '|' .. label .. ']]'
	end
	outHtml:wikitext( title .. ' ' );


	-- Publication date
	local publicationDate = item:formatPropertyValues( 'P577' )
	outHtml:wikitext( '(' .. publicationDate.value .. ') ' )


	-- Scanned file on Wikimedia Commons.
	if not hasWikisourcePage then
		-- Add links to Index page or Commons file.
		local hasIndexOrCommonsLink = false
		local scannedFileOnWikimediaCommons = 'P996'
		local scannedFileOnWikimediaCommonsStmts = item:getBestStatements( scannedFileOnWikimediaCommons )
		for _, stmt in pairs( scannedFileOnWikimediaCommonsStmts ) do
			local commonsFilename = stmt['mainsnak']['datavalue']['value']
			outHtml:wikitext( ' ' .. mw.getCurrentFrame():expandTemplate{ title = 'Small scan link', args = { commonsFilename } } )
			hasIndexOrCommonsLink = true;
		end
		-- Add link to the IA item if no links were added above.
		if not hasIndexOrCommonsLink then
			local internetArchiveIdProp = 'P724'
			local internetArchiveIdStmts = item:getBestStatements( internetArchiveIdProp )
			for _, stmt in pairs( internetArchiveIdStmts ) do
				local internetArchiveId = stmt['mainsnak']['datavalue']['value']
				outHtml:wikitext( ' ' .. mw.getCurrentFrame():expandTemplate{ title = 'IA small link', args = { internetArchiveId } } )
			end
		end
	end


	-- Wikidata and Wikipedia links.
	local img = '[[ملف:Wikidata-books-task-force-logo.svg|20px|alt=Wikidata books task force logo|link=d:' .. item.id .. '|View on Wikidata]]'
	outHtml:wikitext( img )

	return tostring( outHtml )
end

--------------------------------------------------------------------------------
-- Exported method. Get wikitext for displaying an edition's badges from Wikidata.
-- To get an indicator, pass args.indicator = true
-- Testing: =p.badge({args={wikidata='Q75043199'}})
--
local function badge( args )
	local item = getItem( args )
	if not ( item and item.sitelinks and item.sitelinks.arwikisource and item.sitelinks.arwikisource.badges ) then
		return ''
	end
	local badges = item.sitelinks.arwikisource.badges
	local out = ''
	for _, badge in pairs( badges ) do
		local badgeOut = ''
		local badgeItem = mw.wikibase.getEntity( badge )
		local badgeName = ''
		local wikisourceBadgeClass = 'Q75042035'
		if badgeItem.claims.P31[1].mainsnak.datavalue.value.id == wikisourceBadgeClass and badgeItem.claims.P18 ~= nil then
			local imageName = badgeItem.claims.P18[1].mainsnak.datavalue.value
			if mw.wikibase.getLabel( badge ) ~= nil then
				badgeName = mw.wikibase.getLabel( badge )
			end
			badgeOut = '<span class="indicator-badge">[[ملف:' .. imageName .. '|16px|link=Help:Text status|' .. badgeName .. ']]</span>'
			if args.indicator ~= nil then
				badgeOut = '<indicator name="wikisource-badge-' .. badgeName .. '">' .. badgeOut .. '</indicator>'
			end
			if args.category ~= nil and badgeItem.claims.P910 ~= nil then
				local categoryQid = badgeItem.claims.P910[1].mainsnak.datavalue.value.id
				local category = mw.wikibase.getEntity( categoryQid )
				badgeOut = badgeOut .. '[[' .. category.sitelinks.arwikisource.title .. ']]'
			end
			out = out .. badgeOut
		end
	end
	return mw.getCurrentFrame():preprocess( out )
end

--------------------------------------------------------------------------------
-- Exported method.
local function authority_control( args )
	local item = getItem( args )
	-- Gather every 'external-id' statement.
	local out = mw.html.create( '' )
	for propertyId,claims in pairs( item.claims) do
		local propItem = getItem( propertyId )
		for _,claim in pairs( claims ) do
			if claim.mainsnak.datatype == 'external-id' then
				local propLabel = propItem:getLabel()
				if not propLabel then
					if propItem.aliases.ar and propItem.aliases.ar[1].value then
						propLabel = propItem.aliases.ar[1].value
					else
						propLabel = propertyId
					end
				end
				out:wikitext( '* [[d:Special:EntityPage/'.. propertyId .. '|' .. propLabel .. ']]: ' .. claim.mainsnak.datavalue.value .. '\n' )
			end
		end
	end
	return tostring( out )
end

--------------------------------------------------------------------------------
-- Export all public functions.
return {
	-- =p.author_list({args={wikidata='Q28913867'}})
	author_list = function( frame ) return author_list( frame.args ) end;
	-- =p.inline({args={wikidata_id='Q28913867'}})
	inline = function( frame ) return inline( frame.args ) end;
	-- =p.badge({args={wikidata='Q28020002'}})
	badge = function( frame ) return badge( frame.args ) end;
	-- =p.authority_control({args={wikidata='Q19035838'}})
	authority_control = function( frame ) return authority_control( frame.args ) end;
}