Documentation for this module may be created at ModuleDoc:Doc

local utils = require( 'Module:Utils' )

--------------------------------------------------------------------------------
------------------------------- PRIVATE FUNCTIONS ------------------------------
--------------------------------------------------------------------------------

function parse( content )
	local info = parseLines( content )
	local info = groupFuncs( info )
	local text = generateDocumentation( info )
	return text
end

function parseLines( content )
	content = mw.ustring.gsub( content, '[^%-%[]%[=-%[.-%]=-%]', '' )
	local lines = mw.text.split( content, '[\n\f\r\v]' )
	local info = {
		[ 'functions' ] = {},
	}
	local currDoc = {
		[ 'params' ] = {},
		[ 'desc' ] = ''
	}
	local inDocComment = false
	for i, line in ipairs( lines ) do
		line = mw.ustring.gsub( line, "'[^']-'", '')
		line = mw.ustring.gsub( line, '"[^"]-"', '')
		if not utils.empty( line ) then
			if inDocComment then
				if mw.ustring.find( line, '%]%]' ) then
					inDocComment = false
				end
				line = mw.ustring.gsub( line, '%]%].-', '')
				local tag = mw.ustring.match( line, '^%s-@%s-([^%s]+)')
				if tag then
					local content = mw.ustring.match( line, '^@%s-[^%s]+%s+([^%s].+)')
					handleTag(currDoc, tag, content)
				else
					currDoc.desc = currDoc.desc .. ' ' .. line
				end
			else
				local func = mw.ustring.match( line, '^%s-function%s+([^%(%s]+)' )
				if func then
					local fullname = mw.text.split( func, '%.')
					currDoc.name = fullname[#fullname]
					fullname[#fullname] = nil
					currDoc.owner = table.concat( fullname, '.' )
					table.insert( info.functions, currDoc )
					currDoc = {
						[ 'params' ] = {},
						[ 'desc' ] = ''
					}
				end
				local returns = mw.ustring.match( line, '^return%s+([^%s]+)')
				if returns then
					info.returns = returns
				end
				if mw.ustring.find( line, '--%[%[%[' ) then
					inDocComment = true
				end
			end
		end
	end
	return info
end

function handleTag( currDoc, tag, content )
	tag = mw.ustring.lower( tag )
	if tag == 'param' or tag == 'parameter' or tag == 'arg' or tag == 'argument' then
		local words = mw.text.split( content, "-" )
		local name = words[1] or '???'
		local type = ''
		if words[3] then
			type = words[2]
		end
		local desc = words[3] or words[2]
		table.insert( currDoc.params,  {
			[ 'name' ] = utils.strip( tostring( name ) ),
			[ 'type' ] = utils.strip( tostring( type ) ),
			[ 'desc' ] = utils.strip( tostring( desc ) )
		} )
	elseif tag == "return" or tag == "returns" then
		local words = mw.text.split( content, "-" )
		local type = ''
		if words[2] then
			type = words[1]
		end
		local desc = words[2] or words[1]
		currDoc.returns = {
			[ 'type' ] = utils.strip( tostring( type ) ),
			[ 'desc' ] = utils.strip( tostring( desc ) )
		}
	end
end

function groupFuncs( info )
	local private = {}
	local public = {}
	if info.returns then
		for i, func in ipairs( info.functions ) do
			if func.owner == info.returns then
				table.insert( public, func )
			else
				table.insert( private, func )
			end
		end
	else
		private = info.functions
	end
	info.functions = {
		[ 'private' ] = private,
		[ 'public' ] = public
	}
	return info
end

function generateDocumentation( info )
	local text = ''
	if next( info.functions.public ) then
		text = text .. '\n==Public Functions==\n'
		for i, func in ipairs( info.functions.public ) do
			text = text .. generateFuncDocumentation( func )
		end
	end
	if next( info.functions.private ) then
		text = text .. '\n==Private Functions==\n'
		for i, func in ipairs( info.functions.private ) do
			text = text .. generateFuncDocumentation( func )
		end
	end
	text = text .. '\n==Code==\n\nA copy of the code for this module follows:'
	return text
end

function generateFuncDocumentation( func )
	local text = '==='
	if not utils.empty( func.owner ) then
		text = text .. func.owner .. '.' .. func.name
	else
		text = text .. func.name
	end
	text = text .. '===\n'
	if not utils.empty( func.desc ) then
		text = text .. utils.strip( func.desc ) .. '\n\n'
	end
	if ( not utils.empty( func.params ) ) and next( func.params ) then
		text = text .. "'''Parameters:'''\n\n"
		for i, param in ipairs( func.params ) do
			text = text .. "*" .. param.name 
			if not utils.empty( param.type ) then
				text = text .. " (''" .. param.type .. "'')"
			end
			if not utils.empty( param.desc ) then
				text = text .. ' - ' .. utils.strip( param.desc )
			end
			text = text .. '\n'
		end
		text = text .. '\n'
	end
	if not utils.empty( func.returns ) then
		text = text .. "'''Returns:'''\n\n"
		text = text .. "''" .. func.returns.type .. "'' - " .. func.returns.desc
			.. '\n'
	end
	text = text .. '\n'
	return text
end
--------------------------------------------------------------------------------
------------------------------- PUBLIC FUNCTIONS -------------------------------
--------------------------------------------------------------------------------

local doc = {}

function doc.module( frame )
	local name = frame.args[1] or frame.args['Name']
	if utils.empty( name ) then
		return utils.error(
			'No module specified! Please provide a valid module name!',
			'documentation',
			'Invalid module documentation pages' )
	end
	name = tostring( name )
	local title = mw.title.new( name, 'Module' )
	if not title then
		return utils.error(
			'There is no module page entitled "' .. name .. '"!',
			'documentation',
			'Invalid module documentation pages' )
	end
	local content = title:getContent()
	if utils.empty( content ) then
		return utils.error(
			'There is no module page entitled "' .. name .. '"!',
			'documentation',
			'Invalid module documentation pages' )
	end
	return parse( content )
end

return doc