local RollupCoverage = require "RollupCoverage"
local LuaCoverage = require "LuaCoverage"
local LoadCoverage = require "LoadCoverage"
local DumpCoverage = require "DumpCoverage"
local partition = require "PartitionCoverage"

--------------------------------------------------------------------------------

local function noop() end

local mockScope = {
	setPortionComplete = noop,
	isCanceled = noop,
	setCaption = noop,
}

--------------------------------------------------------------------------------

local function linelist( filename )
	local lines = { }
	for line in io.lines( filename ) do
		lines[ #lines + 1 ] = line
	end

	return lines
end

--------------------------------------------------------------------------------

local function loadCoverage( symbolsFile, lcrFiles )
	
	LuaCoverage.loadSymbolsFile( symbolsFile )
	local convertedResults = { }
	for file in io.lines( lcrFiles ) do
		
		local fileResults = LoadCoverage.loadCoverageFile( file, noop )
		
		LuaCoverage.convertCoverageResults( fileResults, noop, convertedResults )
	end
	
	return convertedResults
end

--------------------------------------------------------------------------------

local function cloneOverview( accumulator )
	local copy = { }
	local totalHit = 0
	local totalActive = 0

	for _, part in ipairs( accumulator ) do

		if part.active > 0 then
			copy[ #copy + 1 ] = {
				name = part.name,
				hit = part.hit,
				active = part.active
			}
			totalHit = totalHit + part.hit
			totalActive = totalActive + part.active
		end

	end
	
	return copy, totalHit, totalActive
end

--------------------------------------------------------------------------------

-- Accumulator Overview Slices

local function coveragePercent( part )
	return part.hit * 100 / part.active
end

local function uncoveredLines( part )
	return part.active - part.hit
end

local function sortby( fn )
	return function( a, b )
		return fn( a ) > fn( b )
	end
end

--local function hitLines( part )
--	return part.hit
--end
--
--local function activeLines( part )
--	return part.active
--end

--local function partOfWhole( fn )
--	return function( part, whole )
--		return fn( whole ) - fn( part ), fn( part )
--	end
--end

--local function largeUntouched( part )
--	return part.hit == 0 and part.active or 0
--end

local overviewSlices = { {
		name = "Top Performers",
		description = "Subsystems by Coverage Percent",
		sorter = sortby( coveragePercent ),
		format = "(%2.1f%%)",
		number = coveragePercent
	},
	{
		name = "Big Targets",
		description = "Subsystems by Uncovered Lines",
		sorter = sortby( uncoveredLines ),
		number = uncoveredLines
	}--[[
	{
		name = "Untouched",
		description = "Subsystems with No Coverage (By Size)",
		sorter = sortby( largeUntouched ),
		number = activeLines,
		filter = function( part ) return part.hit == 0 and part end
	},
	{
		name = "Top Contributors",
		description = "Subsystems by Hit Lines",
		sorter = sortby( hitLines ),
		number = hitLines
	},]]
}

--------------------------------------------------------------------------------

-- this is a copy of LrPathUtils.makeRelative
local function makeRelative( a, b )
		local result
		a = string.gsub( a, "/*$", "" )
		b = string.gsub( b, "/*$", "" )
		if string.sub( a, 1, #b ) == b and string.sub( a, #b+1,#b+1 ) == '/' then
			result = string.sub( a, #b+2 )
		end
		return result or a
end

--------------------------------------------------------------------------------

-- defines the way that the Lua files are grouped for coverage rollups
-- this is a duplicate copy of same function from RollupCoverage
-- but this doesnt uses standard utils functions from LrPathUtils/FileUtils etc
-- because those functions are not available from command line headless loader
local function coveragePartition()
	local coveragePath = os.getenv( "COVERAGE_TOOLKIT" )
	
	local mainDir = string.match( coveragePath, "(.*)/build" )
	
	-- Selecting All just shows two subsystems, adobe/kernel and adobe/wichitafoundation
	-- when base is main dir. We dont get subgroups under adobe/wichitafoundation
	-- folder, so making main/adobe is base directory.
	local base = string.format( "%s/%s", mainDir, "adobe" )
	return function( filepath )
		
		local relative = makeRelative( filepath, base )
		-- string.replace is not available when running from command line so continue
		-- to use the lua standard string.gsub
		relative = string.gsub( relative, "\\", "/" )
		return partition( relative )
	end
end

-------------------------------------------------------------------------------

local symbolsInFile, pathsInFile, lcrFiles, coverageOutFile, jsonOutFile, summaryOutFile = ...

assert( symbolsInFile, "first argument should be a Lua symbols file (luac -i output)" )
assert( pathsInFile, "second argument should be a file with a list of Lua files" )
assert( lcrFiles, "third argument should be a file with a list of lcr files" )

local convertedResults = loadCoverage( symbolsInFile, lcrFiles )

-- generates a merged lcr file after removing duplicate entries
if coverageOutFile and coverageOutFile ~= "-" then
	DumpCoverage.writeCoverageLog( coverageOutFile, convertedResults )
end

if jsonOutFile or summaryOutFile then
	
	local accumulator = RollupCoverage.accumulateStatistics( convertedResults, mockScope, noop,
		linelist( pathsInFile ), coveragePartition(), LuaCoverage.computeActiveLines )

	if jsonOutFile and jsonOutFile ~= "-" then
		local writeLine, close = DumpCoverage.fileWriter( jsonOutFile )
		-- writing into .js file
		if writeLine then
			accumulator:toJSON_Wichita( writeLine )
			close()
		end
	end

	if summaryOutFile and summaryOutFile ~= "-" then
		local writeLine, close = DumpCoverage.fileWriter( summaryOutFile )
	
		if writeLine then
	
			local function percent( part, whole )
				return part * 100 / whole
			end
			
			local function number( slice, part )
				return string.format( slice.format or "(%d)", slice.number( part ) )
			end
		
			local overview = cloneOverview( accumulator )

			local writeLineF = function( ... ) return writeLine ( string.format( ... ) ) end
			
			-- Title of page
			writeLineF( '<html><head><title>Lua Code Coverage</title><meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /></head><body><h1><center>Lua Code Coverage - wichitafoundation</center></h1><p>&nbsp;</p>' )
			writeLine( '<hr width="100%">')
			
			-- coverage summary for whole wchitafoundation
			writeLineF( '<table WIDTH="100%%" border="7" cellspacing="3"><tr><th>Lines Hit</th><th>Lines Active</th><th>Coverage</th></tr><tr><td width="33%%" align="center">%s</td><td width="33%%" align="center">%s</td><td width="33%%" align="center">%2.1f%%</td></tr></table>', accumulator.hit, accumulator.active, percent(accumulator.hit, accumulator.active) )
			writeLine( '<hr width="100%">')
			
			-- subsystems summary sorted by percentage
			for _, slice in ipairs( overviewSlices ) do
				writeLineF( "<h2>%s</h2>", slice.name )
				writeLineF( "<h4>%s</h4>", slice.description )
				table.sort( overview, assert( slice.sorter ) )
				writeLineF( "<ol>")
				for i = 1, slice.count or 10 do
					local part = overview[ i ]
					if part and ( slice.filter and slice.filter( part ) or not slice.filter ) then
						writeLineF( "<li> %s %s</li>", part.name, number( slice, part ) )
					else
						break
					end
				end
				writeLineF( "</ol>")
			end
			writeLineF( "</body>")
			close()
		end
	end
end
