local ON = true
bench={
	enabled 			=true,
	period        =10,
	step 		      =1,
	keys   				={},
	folded  			={},
	lines 				={},
	source				={},
	times  				={},
	trash  				={},
	meanTimes  		={},
	meanTrash  		={},
	startGarbage 	={},
	startTimes 		={},
	names 				={},
	hasChildren   ={}
}

function bench.run() 
	if not ON then bench.disabled = true  ; bench.enabled =false return end
	if editor.debug   then 
		bench.disabled=not editor.debug.bench
	end
	if bench.disabled then return end

	if not bench.folded[bench.keys[1]] then 
		trace("                                  ms   kb")
	end

	local folded = false 
	bench.step = bench.step<bench.period and bench.step + 1 or 1
	for i,k in ipairs(bench.keys) do 
		if folded then 
			if #k<#folded then 
				folded = false 
			end
		end
		if not folded then 
			if bench.step==1 then 
				bench.meanTimes[k] = bench.times[k]/bench.period
				bench.meanTrash[k] = bench.trash[k]/bench.period
				bench.times[k] = 0
				bench.trash[k] = 0
			end
			local i,str,indent,name = 1,k:sub(1,#k-1),""
			while not name do 
				local found = str:find(":",i+1)
				if found then 
					i = found 
					indent = indent.." "
				else
					name = indent..str:sub(i)
				end
			end
			name = name..(bench.hasChildren[k] and bench.folded[k] and " +" or "")
			local space = string.sub("                                 ",#name)
			if bench.meanTimes[k] then 
				if editor.debug and editor.debug.bench then 
					local fold = bench.folded[k] and " >" or " <"
					local kb = tostring(mu(bench.meanTrash[k]))
					trace({ 
						{ bench.fold, 
							bench.click },
						k},
						name..space..tostring(bench.meanTimes[k]):sub(1,4).." "..kb 
					)
				end
			end
		end
		if not folded and bench.folded[k]  then 
			folded = k 
		end
		bench.hasChildren[k] = false
	end
	bench.keys = {}
	trace("")

end

function bench.fold(name,line,source)
	if bench.hasChildren[name] then 
		print("from",name,"line",line,"source",source)
		bench.folded[name]=not bench.folded[name]
	end
end

function bench.click(name)
	bench.clicked=name
	waitFor(bench.getFile)
end

function bench.getFile()
	if bench.lines[bench.clicked] then 
		bench.open(bench.clicked,bench.lines[bench.clicked],bench.source[bench.clicked])
		return true 
	end
end

function bench.open(name,line,source)
	print("from",name,"line",line,"source",source)
	local command = string.format("subl "..ENGINE_DIR..source..":%i",line)
	print(command)
	os.execute(command)
end

function bench.enable() bench.enabled = true end
function bench.disable() bench.enabled = false end

function bench.name(name,pop)
	local out = ""
	if name and not pop then 
		table.insert(bench.names,name)
	end
	for w,word in ipairs(bench.names) do 
		out=out..word..":"
	end
	if pop then 
		if out:sub(#out-#name,#out-1) ~= name then 
			error("pop expected for "..bench.names[#bench.names])
		end
	end
	return out
end

function bench.push(name)
	-- print("bench.push",name)
	if bench.disabled then return end
	if not bench.enabled then return end
	bench.hasChildren[bench.name()] = true
	local name = bench.name(name)
	
	bench.startTimes[name] = ltimer.getTime( )
	bench.times[name] = bench.times[name] or 0
	bench.trash[name] = bench.trash[name] or 0
	bench.startGarbage[name] = collectgarbage("count")
	if bench.clicked==name then 
		local info = debug.getinfo(2)
		bench.lines[name] = info.currentline
		bench.source[name] = info.short_src
		bench.clicked=false
	end
	table.add(bench.keys,name)
end

function bench.pop(name)
	-- print("bench.pop",name)
	if bench.disabled then return end
	if not bench.enabled then return end
	local name = bench.name(name,true)
	bench.currentLevel = name
	bench.times[name] = bench.times[name] + (ltimer.getTime( ) - bench.startTimes[name]) * 1000
	local garbage = (collectgarbage("count") - bench.startGarbage[name]) 
	bench.trash[name] = bench.trash[name] + (garbage>0 and garbage or 0)
	table.remove(bench.names,#bench.names)
end


function manualGC(time_budget, safetynet_megabytes, disable_otherwise)
	local max_steps = 1000
	local steps = 0
	local start_time = ltimer.getTime()
	while
			ltimer.getTime() - start_time < time_budget and
			steps < max_steps
		do
		collectgarbage("step", 1)
		steps = steps + 1
	end
	-- print(ltimer.getTime(),"steps",steps)
	--safety net
	if collectgarbage("count") / 1024 > safetynet_megabytes then
		collectgarbage("collect")
	end
	--don't collect gc outside this margin
	if disable_otherwise then
		-- collectgarbage("stop")
	end
end

if not bench.enabled then 
	bench.push=nothing
	bench.pop=nothing
end