--- Statements…

-- @table local library variable
local libUtil = require 'libraryUtil'

local libRefs = require 'Module:Reference score'

local i18n = mw.loadData( 'Module:Statements/i18n' )

local h = {}

function h.findUnit( unit )
	-- is this a simple countable
	if unit == '1' then
		return nil
	end
	-- this is not well-defined
	local uri = mw.uri.new( unit )
	if not uri then
		return unit
	end
	local entityId = string.match( uri.path, 'Q%d+$')
	if not entityId then
		return unit
	end
	local label = mw.wikibase.getLabel( entityId )
	local pKey = string.format( '%s-%s', 'statements-unit', string.lower( entityId ) )
	local raw = i18n[pKey]
	if raw then
		local msg = mw.message.newRawMessage( raw )
		if msg:exists() then
			return label, msg:plain()
		end
	end
	return label
end

function h.findMessage( key, property )
	if not property then
		local raw = i18n[key] or mw.ustring.format( '[%s]', key )
		return mw.message.newRawMessage( raw )
	end
	local propertyId = mw.wikibase.resolvePropertyId( property )
	if not propertyId then
		local raw = i18n[key] or mw.ustring.format( '[%s]', key )
		return mw.message.newRawMessage( raw )
	end
	local pKey = string.format( '%s-%s', key, string.lower( propertyId ) )
	local raw = i18n[pKey] or i18n[key] or mw.ustring.format( '[%s]', key )
	return mw.message.newRawMessage( raw )
end
	
function h.exists( handler, type )
	if not handler then
		return nil, mw.html.create( 'span' )
			:addClass( 'warning' )
			:wikitext( mw.message.newRawMessage( i18n['statements-not-implemented'], type ):plain() )
		end
	return handler
end

-- @field datatypes holds handlers for mainsnaks
h.snaktypes = {}

h.snaktypes['somevalue'] = {
	text = function( snak )
		return h.findMessage( 'statements-somevalue', snak.property ):plain()
	end,

	html = function( snak )
		return mw.html.create( 'span' )
			:wikitext( h.findMessage( 'statements-somevalue', snak.property ):plain() )
	end,
}

h.snaktypes['novalue'] = {
	text = function( snak )
		return h.findMessage( 'statements-novalue', snak.property ):plain()
	end,

	html = function( snak )
		return mw.html.create( 'span' )
			:wikitext( h.findMessage( 'statements-somevalue', snak.property ):plain() )
	end,
}

h.snaktypes['value'] = {
	text = function( snak, property )
		local handler = h.datatypes[snak.datatype]
		if not handler then
			return ''
		end
		return handler.text( snak.datavalue, snak.property )
	end,

	html = function( snak, property )
		local handler, err = h.exists( h.datatypes[snak.datatype], snak.datatype )
		if not handler then
			return err
		end
		return mw.html.create( 'span' )
			:node( handler.html( snak.datavalue, snak.property ) )
	end,
}

-- @field datatypes holds handlers for datavalues
h.datatypes = {}

h.datatypes['wikibase-item'] = {
	text = function( datavalue, property )
		local handler = h.valuetypes[datavalue.type]
		if not handler then
			return ''
		end
		return handler.text( datavalue.value, property )
	end,

	html = function( datavalue, property )
		local handler, err = h.exists( h.valuetypes[datavalue.type], datavalue.type )
		if not handler then
			return err
		end
		return handler.html( datavalue.value, property )
	end
}

h.datatypes['quantity'] = {
	text = function( datavalue, property )
		local handler = h.valuetypes[datavalue.type]
		if not handler then
			return ''
		end
		return handler.text( datavalue.value, property )
	end,

	html = function( datavalue, property )
		local handler, err = h.exists( h.valuetypes[datavalue.type], datavalue.type )
		if not handler then
			return err
		end
		return handler.html( datavalue.value, property )
	end
}

-- @field datatypes holds handlers for values
h.valuetypes = {}

h.valuetypes['wikibase-entityid'] =  {
	-- @field text handler for generating a plain text layout
	text = function( value, property )
		local label = mw.wikibase.getLabel( value.id )
		return label or value.id
	end,

	-- @field html handler for generating a fancy html layout
	html = function( value, property )
		local label = mw.wikibase.getLabel( value.id )
		local sitelink = mw.wikibase.getSitelink( value.id )
		local html = mw.html.create( 'span' )
			:addClass( 'mw-wikibase-entityid' )
		if sitelink then
			html:addClass( 'extiw' )
		end
		return html:wikitext( mw.ustring.format( '[[%s|%s]]',
			sitelink or mw.ustring.format( 'd:%s', value.id ),
			label or value.id ) )
	end
}

h.valuetypes['quantity'] =  {
	-- @field text handler for generating a plain text layout
	text = function( value )
		local long, short = h.findUnit( value.unit )
		local msg = mw.message.newRawMessage( i18n['statements-format-quantity-scalar'] )
			:numParams( tonumber( value.amount ) )
			:params( short or long or '' )
		return msg:plain()
	end,

	-- @field html handler for generating a fancy html layout
	html = function( value )
		local long, short = h.findUnit( value.unit )
		local msg = nil
		if value.amount then
			msg = h.findMessage( 'statements-format-quantity-scalar', property )
				:numParams( tonumber( value.amount ) )
				:params( short or long or '' )
		elseif value.lowerBound or value.upperBound then
			local lower = value.lowerBound and h.findMessage( 'statements-format-quantity-range-lower-bound', property )
					:numParams( tonumber( value.lowerBound ) )
				or h.findMessage( 'statements-format-quantity-range-lower-open', property )
			local upper = value.upperBound and h.findMessage( 'statements-format-quantity-range-upper-bound', property )
					:numParams( tonumber( value.upperBound ) )
				or h.findMessage( 'statements-format-quantity-range-upper-open', property )
			msg = h.findMessage( 'statements-format-quantity-range', property )
				:params( lower, upper, short or long )
		else
			msg = h.findMessage( 'statements-format-quantity-empty', property )
		end
		return mw.html.create( 'span' )
			:addClass( 'mw-wikibase-quantity' )
			:wikitext( msg:plain() )
	end
}

--- Get entity.
-- This convenience function encapsulates base functionality,
-- and makes it possible to use both Q-ids and titles to reference
-- entities.
-- @throw on wrong argument.
-- @tparam nil|string entityIdOrTitle a string that can be both a Q-id and title
-- @treturn entity
function h.getEntity( entityIdOrTitle )
	libUtil.checkType( 'Statements:getEntity', 1, entityIdOrTitle, 'string', true )
	local entity = nil
	if entityIdOrTitle then
		if mw.ustring.match( entityIdOrTitle, '^Q%d+$' ) then
			entity = mw.wikibase.getEntity( entityIdOrTitle )
		elseif mw.ustring.match( entityIdOrTitle, '^.$' ) then
			entity = mw.wikibase.getEntityIdForTitle( entityIdOrTitle )
		end
	end

	if not entity then
		entity = mw.wikibase.getEntityIdForCurrentPage()
	end

	return entity
end

--- Render plain text form.
-- This is an access point for use in a template.
-- Will not generate visible warnings.
-- @throw on wrong argument.
-- @tparam nil|table frame
-- @treturn wikitext
function h.text( frame )
	libUtil.checkType( 'Statements:text', 1, frame, 'table', true )
	frame = mw.getCurrentFrame()
	local entity = h.getEntity( frame.args['from'] )
	if not entity then
		return 'no entity'
	end

	local statements = entity:getBestStatements( frame.args[1] )
	if not statements then
		return 'no statements'
	end

	local t = {}
	for k,v in ipairs( statements ) do
		if v.type == 'statement' then
			local handler = h.snaktypes[k..':'..v.mainsnak.snaktype] or h.snaktypes[v.mainsnak.snaktype]
			if handler then
				local item = handler.text( v.mainsnak )
				if item then
					table.insert( t, item )
				end
				local refs = ''
			end
		end
	end

	if #t == 0 then
		return '#t == 0'
	elseif #t == 1 then
		return t[1]
	elseif #t == 2 then
		return mw.ustring.format( '%s %s %s', t[1], mw.message.newRawMessage( i18n['statements-combiner'] ):plain(), t[2] )
	end

	local punctuation = mw.message.newRawMessage( i18n['statements-punctuation'] ):plain()
	punctuation = punctuation .. ' '
	local last = table.remove( t )
	local first = table.concat( t, punctuation )
	return mw.ustring.format( '%s, %s %s', first, mw.message.newRawMessage( i18n['statements-combiner'] ):plain(), last )
end

function h.qualifiers( snaks )
	local keys = {}
	for k,_ in pairs( snaks or {} ) do
		table.insert( keys, k )
	end
	if #keys == 0 then
		return ''
	end
	local t = {}
	for _,k in pairs( mw.wikibase.orderProperties( keys ) ) do
		-- lua keeps injection order
		t[k] = snaks[k]
	end
	local wiki = mw.wikibase.formatValues( t )
	if not wiki or wiki == '' then
		return ''
	end
	local html = mw.html.create( 'span' )
		:addClass( 'qualifiers' )
		:wikitext( ' (', wiki, ') ' )
	return html
end

--- Render fancy html form.
-- This is an access point for use in a template.
-- Generates visible warnings.
-- @throw on wrong argument.
-- @tparam nil|table frame
-- @treturn wikitext
function h.html( frame )
	frame = mw.getCurrentFrame()
	local entity = h.getEntity( frame.args['from'] )
	if not entity then
		return mw.html.create( 'span' )
			:addClass( 'warning' )
			:wikitext( mw.message.newRawMessage( i18n['statements-no-entity'] ):plain() )
	end

	local statements = entity:getBestStatements( frame.args[1] )
	if not statements then
		return mw.html.create( 'span' )
			:addClass( 'warning' )
			:wikitext( mw.message.newRawMessage( i18n['statements-no-statements'] ):plain() )
	end

	local t = {}
	for _,v in ipairs( statements ) do
		if v.type == 'statement' then
			local handler = h.snaktypes[v.mainsnak.snaktype]
			if handler then
				local item = handler.html( v.mainsnak )
				if item then
					item:node( h.qualifiers( v.qualifiers ) )
					item:wikitext( libRefs.render( frame, v.references ) )
					table.insert( t, item )
				end
			end
		end
	end
	local html = mw.html.create( 'span' )
		:addClass( 'mw-statement' )
	if #t == 0 then
		return html
	elseif #t == 1 then
		return html:node( t[1] )
	elseif #t == 2 then
		return html:node( t[1] ):wikitext( ' ', mw.message.newRawMessage( i18n['statements-combiner'] ):plain(), ' ' ):node( t[2] )
	end

	local last = table.remove( t )
	local punctuation = mw.message.newRawMessage( i18n['statements-punctuation'] ):plain()
	for _,v in ipairs( t ) do
		html:node( v ):wikitext( punctuation, ' ' )
	end
	html:wikitext( ' ', mw.message.newRawMessage( i18n['statements-combiner'] ):plain(), ' ' ):node( last )
	return html
end

return h