local debug = debug

DEFAULT_DEBUG = {
	enableDebugKeys = false,
	showStates = true,
	enableSlowDown = false,
	disableRender = false,
	showGameDebugRender = false,
	disableGc = false,
	showCollectedStats = false,
	showCreatedStats = false,
	showKeyCodes = true,
	showFps = true,
	showTime = true,
	showGraphs = false,
	showPerformanceBreakdown = false,
	disableSounds = false,
	oneFramePerRender = false,
	saveDebugLog = false,
	hideMessages = false,
	performanceGroup = "states",
}

local pgroups = {"states"}

DEBUG = {
	enableShortcutsKey = 220,
	showDebug = false,
	keyShortcuts = {
					{key = 123, entry = "showDebug", type = "toggle"},
					},
	options = {
					{name ="Slow Down", entry = "enableSlowDown", type = "toggle"},
					{name ="Show Collected Stats", entry = "showCollectedStats", type = "toggle"},
					{name ="Show Created Stats", entry = "showCreatedStats", type = "toggle"},
					{name ="Show Performance", entry = "showPerformanceBreakdown", type = "toggle"},
					{name ="Cycle Performance Group", entry = "performanceGroup", type = "cycle", cycle = pgroups},
					{name ="Show Graphs", entry = "showGraphs", type = "toggle"},
					{name ="Show States", entry = "showStates", type = "toggle"},
					{name ="Show Key Codes", entry = "showKeyCodes", type = "toggle"},
					{name ="Show Fps", entry = "showFps", type = "toggle"},
					{name ="Show Time", entry = "showTime", type = "toggle"},
					{name ="Disable Render", entry = "disableRender", type = "toggle"},
					{name ="Disable Gc", entry = "disableGc", type = "toggle"},
					{name ="Disable Sounds", entry = "disableSounds", type = "toggle"},
					{name ="One Frame Per Render", entry = "oneFramePerRender", type = "toggle"},
					{name ="Save Debug Log", entry = "saveDebugLog", type = "toggle"},
					{name ="Hide Messages", entry = "hideMessages", type = "toggle"},
					},
	fpsInterpolation = 0.02,
	graphInterpolation = {fps = 0.2},
	graphLength = 1000,
	graphSize = 200,
	performanceGroups = pgroups,
	performanceBreakdownInterpolation = 1,
	performanceVariables = {
		sections = {
			"totalGcTime",
			"totalRenderDebugTime",
			"totalRenderTime", 
			"totalUpdateTime", 
			"totalWaitTime"},
	},
	performanceVariableColors = {
		sections = {
			totalGcTime = {r = 255, g = 0, b = 0, desc = "GC"}, 
			totalRenderDebugTime = {r = 255, g = 220, b = 255, desc = "Rdebug", exclude = true},
			totalRenderTime = {r = 0, g = 190, b = 0, desc = "R"},
			totalUpdateTime = {r = 0, g = 0, b = 255, desc = "U"},
			totalWaitTime = {r = 128, g = 128, b = 128, desc = "W", exclude = true},
			},
		},
	graphVariableColors = {	
									fps = {r = 200, g = 210, b = 255, desc = "fps"}, 
									mem = {r = 140, g = 180, b = 240, desc = "memory"},
								},
	performanceBarHeight = 400,
	performanceBarLength = 400,
	performanceBarWidth = 400,
}


local debugRuntime = { 
	level = 1, --0 == always,  1 = errornous debug info, 2 = basic debug info, 3 = full debug info
	logs = {},
	fps = 0,
	performanceGroups = {},
	performanceAt = {},
	performance = {},
	performanceTrackers = {},
	avgPerformance = {},
	graphs = {},
	lastKey = 0,
	lastButton = 0,
	lastClick = 0,
	lastChar = 0,
	printLines = {},
	timeStamp = {},
	tags = {},
	createdTableClasses = {},
	rankedCollectedClasses = {},
	pause = false,
	objLog = {},
	completeLog = {},
	trackedPerformanceVariable = {},
	performanceRemoveDelay = {},
	performanceActive = {},
	time = 0,
}

for index, graph in pairs(DEBUG.graphVariableColors) do
	debugRuntime.graphs[index] = {}
end

for index, graph in pairs(debugRuntime.graphs) do
	graph.values = {0}
	graph.maxValue = 0
	graph.avgValue = 0
	graph.at = 1
end

for index, g in pairs(DEBUG.performanceGroups) do
	debugRuntime.performance[g] = {}
	debugRuntime.avgPerformance[g] = {}
	debugRuntime.timeStamp[g] = 0
	debugRuntime.performanceAt[g] = 1
	debugRuntime.performanceTrackers[g] = {}
	debugRuntime.performanceRemoveDelay[g] = {}
	debugRuntime.performanceActive[g] = {}
		
	for i=1, DEBUG.performanceBarLength do
		debugRuntime.performance[g][i] = {}
		if DEBUG.performanceVariables[g] then
			for index, var in pairs(DEBUG.performanceVariables[g]) do
				debugRuntime.performance[g][i][var] = 0
				debugRuntime.avgPerformance[g][var] = 0
			end
		end
	end
end



local rawprint = print
function print(...)
	local m	
	for i=1,select("#",...) do
		local msg = select(i,...)
		if m then
			m = m .. " " .. tostring(msg)
		else
			m = tostring(msg)
		end
	end
	rawprint(m)
end

local rawassert = assert
function assert(cond, ...)
	if cond then
		return cond
	else
		print("ASSERT FAILED:", ...)
		rawassert(false, ...)
	end
end

function printtable(t, limit)
	if t then
		print("#5"..tostring(t).."#4:")
		local n = 0
		for index, key in pairs(t) do
			print("#7"..index.. "#8>#3 "..tostring(key))
			n = n+1
			if limit and n > limit then
				break
			end
		end
	end
end


function debug.tb(lv)
	if lv then
		return debug.traceback("Depth "..lv, lv)
	else
		return debug.traceback()
	end
end


function debug.ptb(lv, limit)
	printtable(string.split(debug.tb(lv), "\n"), limit)
end


function debug.mouseClicked(btn)
	debugRuntime.lastClick = btn
end

function debug.joyPressed(btn)
	debugRuntime.lastButton = btn
end

local _time = 0
local _tag = nil
function debug.tag_dt(tag)
	local new_time = os.clock()
	if _tag then
		print(_tag,":",new_time-_time)
	end
	_tag = tag
	_time = new_time
end

function debug.tagPassedTime(tag)
	--local ts = os.clock()
	local ts = daisy.getSeconds()
	debugRuntime.tags[tag] = {timestamp = ts, tag = tag}
	if debugRuntime.currentTag then
		debugRuntime.tags[tag].duration = ts-debugRuntime.tags[debugRuntime.currentTag].timestamp
	else
		debugRuntime.tags[tag].duration = ts-_initClock
	end
	debugRuntime.currentTag = tag
end

function debug.print(level, category, text, r, g, b)
	local text = text
	if not text then
		text = level.." "..category
	end
	if DEBUG.saveDebugLog then
		table.insert(debugRuntime.completeLog, "L"..level..":"..category..":  "..text)
	end
	if (debugRuntime.level >= level or DEBUG["showPrints_"..category]) then
		table.insert(debugRuntime.printLines, {text = text, life = (DEBUG["showPrints_"..category] and 100 or 10), r = r or nil, g = g or nil, b = b or nil})
	end
end

function debug.printtable(level, category, t, limit)
	if DEBUG.saveDebugLog then
		table.insert(debugRuntime.completeLog, "L"..level..":"..category..":  TABLE")
		for index, var in pairs(t) do
			table.insert(debugRuntime.completeLog, "#"..index..":"..tostring(var))
		end
	end
	if t then
		if (debugRuntime.level >= level or DEBUG["showPrints_"..category]) then
			table.insert(debugRuntime.printLines, {text = "#5"..tostring(t).."#4:", life = 10})
			local n = 0
			for index, key in pairs(t) do
				table.insert(debugRuntime.printLines, {text = "#7"..index.. "#8>#3 "..tostring(key), life = 10})
				n = n+1
				if limit and n > limit then
					break
				end
			end
		end
	end
end

function debug.setPrintCategory(category, bool)
	DEBUG["showPrints_"..category] = bool
end

function debug.log(category, msg)
	if not debugRuntime.logs[category] then
		debugRuntime.logs[category] = {}
	end
	table.insert(debugRuntime.logs[category], msg)
end

function debug.logLast(category)
	if debugRuntime.logs[category] then
		return debugRuntime.logs[category][#debugRuntime.logs[category]]
	end
	return nil
end

function debug.printLog(level, category, explainer)
	if debugRuntime.level >= level then
		print(explainer)
		if debugRuntime.logs[category] then
			for index, msg in ipairs(debugRuntime.logs[category]) do
				print(msg)
			end
		end
		debugRuntime.logs[category] = nil
	end
end

function debug.isLevel(level, category)
	return debugRuntime.level >= level or (category and DEBUG["showPrints_"..category])
end

function debug.getLevel()
	return debugRuntime.level
end

function debug.setLevel(level)
	debugRuntime.level = level or 1
end

function debug.pl(level, msg)
	debug.print(level, "generic", msg, 255, 180, 180)
end

function debug.updatePrints(time)
	while #debugRuntime.printLines > 20 do
		table.remove(debugRuntime.printLines, 1)
	end
	for index,text in ipairs(debugRuntime.printLines) do
		text.life = text.life - time
		if text.life < 0 then
			table.remove(debugRuntime.printLines, index)
			break
		end
	end
end

function debug.getPerformanceType(g)
	local g = g or "sections"
	return debugRuntime.trackedPerformanceVariable[g]
end

function debug.enterPerformanceType(var, g)
	if DEBUG.showDebug and DEBUG.showPerformanceBreakdown then
		local g = g or "sections"
		if debugRuntime.trackedPerformanceVariable[g] then
			debug.exitPerformanceType(debugRuntime.trackedPerformanceVariable[g], g)
		end
		debugRuntime.trackedPerformanceVariable[g] = var
		if daisy.getSeconds then
			debugRuntime.timeStamp[g] =daisy.getSeconds()
		else
			debugRuntime.timeStamp[g] = os.clock()
		end
	end
end

function debug.exitPerformanceType(var, g)
	if DEBUG.showDebug and DEBUG.showPerformanceBreakdown then
		local g = g or "sections"
		local c = daisy.getSeconds and aisy.getSeconds() or os.clock()
		if not debugRuntime.performance[g][debugRuntime.performanceAt[g]][var] then
			debugRuntime.performance[g][debugRuntime.performanceAt[g]][var] = 0
			if not debugRuntime.performanceTrackers[g][var] then
				table.insert(debugRuntime.performanceTrackers[g], var)
				debugRuntime.performanceTrackers[g][var] = true
			end			
		end
		debugRuntime.performance[g][debugRuntime.performanceAt[g]][var] = debugRuntime.performance[g][debugRuntime.performanceAt[g]][var] + (c-debugRuntime.timeStamp[g])
		debugRuntime.trackedPerformanceVariable[g] = nil
		debugRuntime.timeStamp[g] = c
	end
end

function debug.addToGraph(type, var)
	debugRuntime.graphs[type].added = true
	debugRuntime.graphs[type].values[debugRuntime.graphs[type].at] = debugRuntime.graphs[type].values[debugRuntime.graphs[type].at] + var
end

function debug.logObject(obj)
	table.insert(debugRuntime.objLog, 1, obj.type)
	if #debugRuntime.objLog > 10 then
		table.remove(debugRuntime.objLog, 11)
	end
end

function debug.reportCreated(t)
	if DEBUG.showDebug and (DEBUG.showCollectedStats or DEBUG.showCreatedStats) then
		if not debugRuntime.createdTableClasses[tostring(t)] then
			table.insert(debugRuntime.rankedCollectedClasses, tostring(t))
			debugRuntime.createdTableClasses[tostring(t)] = {instances = setmetatable({}, {__mode = "v"}), lastKnownNum = 0, collected = 0}
		end	
		local cData = debugRuntime.createdTableClasses[tostring(t)]
		table.insert(cData.instances, t)
		cData.lastKnownNum = cData.lastKnownNum + 1
	end
end

function debug.enterUpdate(time)
	if debugRuntime.pause then
		return true
	end
	debugRuntime.time = debugRuntime.time + time
end

function debug.enterFrame(time)
	debug.updatePrints(time)
	if DEBUG.showDebug then
		if DEBUG.showPerformanceBreakdown  and DEBUG.performanceGroup == "sections" then
			debug.enterPerformanceType("totalUpdateTime")
		end		
		if DEBUG.showGraphs then
			for index, graph in pairs(debugRuntime.graphs) do
				local currentMax = 0
				local currentAvg = 0
				if graph.values then
					--if graph.added then
						for i=1, #graph.values do
							currentMax = math.max(currentMax, graph.values[i])
							currentAvg = currentAvg + graph.values[i]
						end
						graph.avgValue = currentAvg/#graph.values
						graph.maxValue = math.max(currentMax , graph.maxValue - time * graph.maxValue )
						--for i=1, DEBUG.graphLength-1 do
						--	local at = DEBUG.graphLength - i
						--	graph.values[at+1] = graph.values[at]
						--end
						graph.at = graph.at + 1
						if graph.at > DEBUG.graphLength then
							graph.at = 1
						end
						graph.values[graph.at] = 0
						graph.added = false
					--end
				end
			end
		end
	end
end

function debug.enterGc()
	if DEBUG.showDebug and DEBUG.showPerformanceBreakdown  and DEBUG.performanceGroup == "sections" then
		debug.enterPerformanceType("totalGcTime")
	end
end

local minFrameTime = 1/1000

local function collectedAmountSort(a,b)
	return debugRuntime.createdTableClasses[a].collected > debugRuntime.createdTableClasses[b].collected
end
			
function debug.exitUpdate(time)
	if DEBUG.showDebug then
		if DEBUG.showGraphs then
			if time > minFrameTime then
				debug.addToGraph("fps", (1/time))
			end
			debug.addToGraph("mem", collectgarbage("count"))
		end
		if DEBUG.showFps then
			if time > minFrameTime then
				debugRuntime.fps = debugRuntime.fps * (1-DEBUG.fpsInterpolation) + DEBUG.fpsInterpolation* (1/time)
			end
		end
		--if DEBUG.showTime then
		--	debugRuntime.totalTime = debugRuntime.totalTime + time
		--end
		
		if DEBUG.showCollectedStats or DEBUG.showCreatedStats then
			local any = false
			for index, class in pairs(debugRuntime.createdTableClasses) do
				if class.lastKnownNum > #class.instances then
					class.collected = class.collected + (class.lastKnownNum - #class.instances)
					class.lastKnownNum = #class.instances
					any = true
				end
			end
			if any then
				shellsort(debugRuntime.rankedCollectedClasses, collectedAmountSort)
			end
		end
	end
	
	
	
	if DEBUG.showDebug and DEBUG.showPerformanceBreakdown  and DEBUG.performanceGroup == "sections" then
		debug.enterPerformanceType("totalWaitTime")
	end
end

function debug.enterRender()
	if DEBUG.showDebug and DEBUG.showPerformanceBreakdown  and DEBUG.performanceGroup == "sections" then
		debug.enterPerformanceType("totalRenderTime")
	end
end

local function durationSort(a,b)
	return a.duration > b.duration
end

local DEBUG_COLORS = {
	{r = 1.0, g = 0.79, b = 1.0},
	{r = 0.60, g = 1.00, b = 1.00},
	{r = 1.00, g = 1.00, b = 0.58},
	{r = 0.53, g = 1.00, b = 0.51},
	{r = 1.00, g = 0.48, b = 0.39},
	{r = 1.00, g = 0.56, b = 0.25},
	{r = 0.94, g = 1.00, b = 0.91},
	{r = 0.81, g = 1.00, b = 1.00},
	{r = 1.00, g = 0.74, b = 0.58},
	{r = 0.72, g = 1.00, b = 0.74},
	{r = 1.00, g = 1.00, b = 0.71},
	{r = 1.00, g = 0.96, b = 0.96},
	{r = 1.00, g = 1.00, b = 0.30},
	{r = 0.35, g = 1.00, b = 0.30},
	{r = 1.0, g = 0.53, b = 0.17},
	{r = 1.0, g = 0.29, b = 0.42},
	{r = 1.0, g = 0.43, b = 0.64},
	{r = 1.0, g = 0.29, b = 0.16},
	{r = 0.63, g = 0.42, b = 1.0},
	{r = 0.35, g = 0.32, b = 1.0},
	{r = 0.12, g = 0.22, b = 1.0},
	{r = 1.00, g = 0.16, b = 0.19},
	{r = 1.00, g = 0.04, b = 0.07},
	{r = 0.73, g = 1.00, b = 0.19},
	{r = 0.24, g = 1.00, b = 0.12},
	{r = 1.00, g = 0.89, b = 0.01},
	{r = 0.07, g = 1.00, b = 0.51},
	{r = 1.00, g = 0.56, b = 0.61},
	{r = 0.16, g = 0.61, b = 1.00},
	{r = 1.00, g = 0.21, b = 0.00},
	{r = 0.45, g = 0.70, b = 1.00},
	{r = 1.00, g = 0.68, b = 0.30},
	{r = 0.50, g = 1.00, b = 0.01},
	{r = 0.97, g = 0.27, b = 0.96},
	{r = 0.43, g = 0.07, b = 1.00},
	{r = 0.00, g = 0.33, b = 1.00},
}



function debug.exitRender()

	if DEBUG.consoleEnabled then
		video.renderTextSprites("> " .. DEBUG.consoleBuffer, 10, 10, 0, "font.fnt", 255, 255, 255);
	end
	
	if DEBUG.showDebug and DEBUG.showPerformanceBreakdown  and DEBUG.performanceGroup == "sections" then
		debug.enterPerformanceType("totalRenderDebugTime")
	end
	local offsetY = 0
	if DEBUG.showDebug then		
		if DEBUG.showFps then
			video.renderTextSprites(math.round(debugRuntime.fps).." fps", window.w-10, offsetY+2, 2, "font.fnt", 192, 255, 255, 255)
			offsetY = offsetY + 16
		end
		
		if DEBUG.showTime then
			video.renderTextSprites(string.format("%0.2f", debugRuntime.time).." s", window.w-10, offsetY+2, 2, "font.fnt", 192, 255, 255, 255)
			offsetY = offsetY + 16
		end
		
		if DEBUG.showGraphs then
			local offsetX = 10
			for index, graph in pairs(debugRuntime.graphs) do
				for i = 1, #graph.values do
					video.renderSpriteRectangle(window.w-offsetX-(i/DEBUG.graphLength)*DEBUG.graphSize, offsetY+20-20*graph.values[i]/graph.maxValue, 1, graph.values[i]/graph.maxValue*20, math.min(255,math.max(0,DEBUG.graphSize/DEBUG.graphLength*255)), DEBUG.graphVariableColors[index].r, DEBUG.graphVariableColors[index].g, DEBUG.graphVariableColors[index].b)
				end
				video.renderSpriteRectangle(window.w-offsetX-(graph.at/DEBUG.graphLength)*DEBUG.graphSize, offsetY+20-20*graph.values[graph.at]/graph.maxValue, 1, graph.values[graph.at]/graph.maxValue*20, 255, math.min(255,2*DEBUG.graphVariableColors[index].r), math.min(255,2*DEBUG.graphVariableColors[index].g), math.min(255,2*DEBUG.graphVariableColors[index].b))
				
				video.renderTextSprites(math.round(graph.values[graph.at] or 0).." ("..math.round(graph.avgValue)..") "..DEBUG.graphVariableColors[index].desc, -5+window.w-offsetX-(#graph.values/DEBUG.graphLength) * DEBUG.graphSize, 2 + offsetY, 2, "font.fnt", 255, DEBUG.graphVariableColors[index].r, DEBUG.graphVariableColors[index].g, DEBUG.graphVariableColors[index].b)
				offsetY = offsetY + 21
			end
		end
	end

	if DEBUG.showDebug and DEBUG.showKeyCodes then
		if daisy.isKeyPressed(debugRuntime.lastKey) then
			video.renderTextSprites(debugRuntime.lastKey.." key", window.w-10, offsetY, 2, "font.fnt", 255, 255, 255, 255)
		else
			video.renderTextSprites(debugRuntime.lastKey.." key", window.w-10, offsetY, 2, "font.fnt", 128, 255, 255, 255)
		end
		if daisy.isMouseButtonPressed(debugRuntime.lastClick) then
			video.renderTextSprites(debugRuntime.lastClick.." clk", window.w-110, offsetY, 2, "font.fnt", 255, 255, 255, 255)
		else
			video.renderTextSprites(debugRuntime.lastClick.." clk", window.w-110, offsetY, 2, "font.fnt", 128, 255, 255, 255)
		end
		
		video.renderTextSprites(debugRuntime.lastButton.." btn", window.w-60, offsetY, 2, "font.fnt", 128, 255, 255, 255)
		video.renderTextSprites(debugRuntime.lastChar.." char", window.w-160, offsetY, 2, "font.fnt", 128, 255, 255, 255)
		
		offsetY = offsetY + 16
	end
	
	if DEBUG.showDebug and DEBUG.logObjects then
		video.renderTextSprites("Object Log:", window.w-20, offsetY, 2, "font.fnt", 128, 255, 128, 255)
		offsetY = offsetY + 16
		for index, t in ipairs(debugRuntime.objLog) do
			video.renderTextSprites(t, window.w-20, offsetY, 2, "font.fnt", 128, 255, 128, 255)
			offsetY = offsetY + 16
		end
	end

	if DEBUG.showDebug and DEBUG.showPerformanceBreakdown then
		video.renderSpriteRectangle(window.w-10-DEBUG.performanceBarWidth, offsetY, DEBUG.performanceBarWidth, DEBUG.performanceBarHeight, 64, 0, 0, 0)
		if not debugRuntime.performance[DEBUG.performanceGroup] then
			DEBUG.performanceGroup = "sections"	
		end
		local g = DEBUG.performanceGroup
		
		for i=1, #debugRuntime.performance[g] do
			local total = 0
			local vars = debugRuntime.performance[g][i]
			for name, val in pairs(vars) do
				if not DEBUG.performanceVariableColors[g] or not DEBUG.performanceVariableColors[g][name].exclude then
					total = total + val
				end
			end
			
			
			local movedy = 0
			for index, name in ipairs(DEBUG.performanceVariables[g] or debugRuntime.performanceTrackers[g]) do
				if not DEBUG.performanceVariableColors[g] or not DEBUG.performanceVariableColors[g][name].exclude then
					local size = (debugRuntime.performance[g][i][name] or 0)*DEBUG.performanceBarHeight/total
					local re,gr,bl = nil,nil,nil
					if DEBUG.performanceVariableColors[g] then
						re,gr,bl = DEBUG.performanceVariableColors[g][name].r, DEBUG.performanceVariableColors[g][name].g, DEBUG.performanceVariableColors[g][name].b
					else
						local col = DEBUG_COLORS[(index%(#DEBUG_COLORS-1)) + 1]
						re,gr,bl = col.r*255, col.g*255, col.b*255
					end
					if debugRuntime.performanceAt[g] == i then
						video.renderSpriteRectangle(window.w-10-i*DEBUG.performanceBarWidth/DEBUG.performanceBarLength,movedy+ offsetY, DEBUG.performanceBarWidth/DEBUG.performanceBarLength,size , 255, re, gr, bl)				
					else
						video.renderSpriteRectangle(window.w-10-i*DEBUG.performanceBarWidth/DEBUG.performanceBarLength,movedy+ offsetY, DEBUG.performanceBarWidth/DEBUG.performanceBarLength,size , 128, re, gr, bl)				
					end
					movedy = movedy + size
				end
			end
		end
		local totalAvg = 0
		for index, name in ipairs(DEBUG.performanceVariables[g] or debugRuntime.performanceTrackers[g]) do
			if not DEBUG.performanceVariableColors[g] or not DEBUG.performanceVariableColors[g][name].exclude then
				totalAvg = totalAvg + (debugRuntime.avgPerformance[g][name] or 0)
			end
		end
		local nY = 0
		for index, name in ipairs(DEBUG.performanceVariables[g] or debugRuntime.performanceTrackers[g]) do
			if not DEBUG.performanceVariableColors[g] or not DEBUG.performanceVariableColors[g][name].exclude then
				nY = nY + 0.5*(debugRuntime.avgPerformance[g][name] or 0)/totalAvg*(DEBUG.performanceBarHeight - #(DEBUG.performanceVariables[g] or debugRuntime.performanceTrackers[g])*10) 
				local re,gr,bl = nil,nil,nil
				if DEBUG.performanceVariableColors[g] then
					re,gr,bl = DEBUG.performanceVariableColors[g][name].r, DEBUG.performanceVariableColors[g][name].g, DEBUG.performanceVariableColors[g][name].b
				else
					local col = DEBUG_COLORS[(index%(#DEBUG_COLORS-1)) + 1]
					re,gr,bl = col.r*255, col.g*255, col.b*255
				end
				if DEBUG.performanceVariableColors[g] then
					video.renderTextSprites(DEBUG.performanceVariableColors[g][name].desc, window.w-12-DEBUG.performanceBarWidth, offsetY + nY, 2, "font.fnt", 128, re,gr,bl)
				else
					video.renderTextSprites(name, window.w-12-DEBUG.performanceBarWidth, offsetY + nY, 2, "font.fnt", 128, re,gr,bl)
				end
				nY = nY + 0.5*(debugRuntime.avgPerformance[g][name] or 0)/totalAvg*(DEBUG.performanceBarHeight - #(DEBUG.performanceVariables[g] or debugRuntime.performanceTrackers[g])*10) + 10
			end
		end
		
		offsetY = offsetY + DEBUG.performanceBarHeight
		video.renderTextSprites(DEBUG.performanceGroup, window.w-12, offsetY+2, 2, "font.fnt", 128, 255, 255, 255)
		
		offsetY = offsetY + 14
		
		
		for index, name in ipairs(DEBUG.performanceVariables[g] or debugRuntime.performanceTrackers[g]) do
			debugRuntime.avgPerformance[g][name] = (debugRuntime.avgPerformance[g][name] or 0) * 0.99 + 0.01 * (debugRuntime.performance[g][debugRuntime.performanceAt[g]][name] or 0)
		end
		
		if debugRuntime.performanceTrackers[g] then
			for index, name in ipairs(debugRuntime.performanceTrackers[g]) do
				if (debugRuntime.performance[g][debugRuntime.performanceAt[g]][name] or 0) > 0 then
					debugRuntime.performanceActive[g][name]  = true
				end
				if debugRuntime.performanceRemoveDelay[g][name] then
					if debugRuntime.performanceRemoveDelay[g][name] <= 0 then
						if not debugRuntime.performanceActive[g][name] then
							for ind, v in ipairs(debugRuntime.performanceTrackers[g]) do
								if v == name then
									for i=1, #debugRuntime.performance[g] do
										debugRuntime.performance[g][i][name] = nil
									end
									table.remove(debugRuntime.performanceTrackers[g], ind)
									break
								end
							end
							debugRuntime.performanceTrackers[g][name] = nil
							break
						else
							debugRuntime.performanceActive[g][name] = false
						end
					else
						debugRuntime.performanceRemoveDelay[g][name] = debugRuntime.performanceRemoveDelay[g][name] -1
					end
				else
					debugRuntime.performanceRemoveDelay[g][name] = DEBUG.performanceBarLength
				end
			end
		end
			
		debugRuntime.performanceAt[g] = debugRuntime.performanceAt[g] + 1
		if debugRuntime.performanceAt[g] > DEBUG.performanceBarLength then
			debugRuntime.performanceAt[g] = 1
		end
		
		for index, name in ipairs(DEBUG.performanceVariables[g] or debugRuntime.performanceTrackers[g]) do
			debugRuntime.performance[g][debugRuntime.performanceAt[g]][name] = 0
		end
	end
	
	if DEBUG.showDebug and DEBUG.showCollectedStats then
		video.renderTextSprites("Collected by GC:", window.w-12, offsetY, 2, "font.fnt", 192,255, 128, 128)
		offsetY = offsetY + 14
		for i=1, math.min(#debugRuntime.rankedCollectedClasses, 10) do
			local ind = debugRuntime.rankedCollectedClasses[i]
			local collected = debugRuntime.createdTableClasses[ind].collected
			if collected > 0 then
				video.renderTextSprites(ind.. ": "..collected, window.w-12, offsetY, 2, "font.fnt", 128,255, 128, 128)
				offsetY = offsetY + 14
			end
		end
	end
	
	if DEBUG.showDebug and DEBUG.showCreatedStats then
		video.renderTextSprites("Created Obj:", window.w-12, offsetY, 2, "font.fnt", 192,255, 128, 128)
		offsetY = offsetY + 14
		for i=1, math.min(#debugRuntime.rankedCollectedClasses, 10) do
			local ind = debugRuntime.rankedCollectedClasses[i]
			local lastKnownNum = debugRuntime.createdTableClasses[ind].lastKnownNum
			if lastKnownNum > 0 then
				video.renderTextSprites(ind.. ": "..lastKnownNum, window.w-12, offsetY, 2, "font.fnt", 180,128, 255, 128)
				offsetY = offsetY + 14
			end
		end
	end
	
	if DEBUG.showDebug and DEBUG.showStates then
		local currY = window.h - 25
		for index, state in ipairs(states.getAllRenderOrdered()) do
			local a,r,g,b = 192,255,255,255
			local st = state.id
			if state.enabled then
				video.renderTextSprites("U", window.w-5-4, currY, 2, "font.fnt", a, 0, 0, 255)
				if state.render then
					video.renderTextSprites("R", window.w-5-12, currY, 2, "font.fnt", a, 0, 255, 0)
				end
			end
			if state.protected then
				r = 90
				g = 90
				b = 90
			end
			video.renderTextSprites(st, window.w-5-20, currY, 2, "font.fnt", a, r, g, b)
			currY = currY - 15
		end
		video.renderTextSprites("mode>"..(app.runtime.mode or "release"), window.w-5, currY, 2, "font.fnt", 128, 255,255,255)
	end
	
	
	
	if DEBUG.showDebug and DEBUG.showInitTimes then
		if not debugRuntime.sortedTags then
			debugRuntime.sortedTags = {}
			for index, tag in pairs(debugRuntime.tags) do
				if tag.duration > 0 then
					table.insert(debugRuntime.sortedTags, tag)
				end
			end
			shellsort(debugRuntime.sortedTags, durationSort)
			local t = 0
			for index, tag in pairs(debugRuntime.sortedTags) do
				t = t + tag.duration
			end
			
			for index, tag in pairs(debugRuntime.sortedTags) do
				tag.percent = tag.duration/t
			end
		else
			for index, tag in ipairs(debugRuntime.sortedTags) do
				video.renderTextSprites(tag.tag.. " #3:"..string.format("%0.3f", tag.duration).."s ("..string.format("%0.1f", tag.percent*100).."%)", 20, 20 + index*15, 0, "font.fnt", 255, 255, 255, 255)
			end
		end
	end

	if DEBUG.enableShortcuts then
		local currY = 10
		local currX = 10
		for index, opt in pairs(DEBUG.options) do
			if not daisy.isKeyPressed(16) or (daisy.isKeyPressed(16) and index > string.totalKeys) then
				local charId = string.indexToNumberAndLowerCaseKey(index)
				local btnString = nil
				if not charId then
					btnString = "^+"..string.char(string.indexToNumberAndLowerCaseKey(index-string.totalKeys))
				else
					btnString = string.char(string.indexToNumberAndLowerCaseKey(index))
				end

				local a = 128
				btnString = "["..btnString.."] "..opt.name
				if opt.type == "toggle" then
					if DEBUG[opt.entry] then
						btnString = btnString.. " (ON)"
						a = 255
					else
						btnString = btnString.. " (OFF)"
					end
				elseif opt.type == "cycle" then
					btnString = btnString.. " ["..DEBUG[tostring(opt.entry)].."]"
					a = 255
				end

				video.renderTextSprites(btnString, currX, currY, 0, "font.fnt", a, 255, 255, 255)
				currY = currY + 15
				if currY > window.h-15 then
					currY = 15
					currX = currX + 160
				end
			end
		end
	end
	
	if not DEBUG.hideMessages and #debugRuntime.printLines > 0 then
		-- calculate box size for background
		local width,height = 100,0
		for index,text in pairs(debugRuntime.printLines) do
			local w,h = video.getTextSpritesSize(text.text)
			if (w+5) > width then
				width = (w+5)
			end
			height = h
		end

		local totalHeight = (height + 2) * #debugRuntime.printLines + 2
		local y = window.h - 50 - totalHeight + 2
		if #debugRuntime.printLines <= 1 then
			video.renderRectangle((window.w - width) / 2, y - 2, width, totalHeight, 64*(math.min(debugRuntime.printLines[1].life,1)), 0, 0, 0)
		else
			video.renderRectangle((window.w - width) / 2, y - 2, width, totalHeight, 64, 0, 0, 0)
		end
		for index,text in pairs(debugRuntime.printLines) do
			video.renderTextSprites(text.text, window.w/ 2, y, 1, nil, 255*math.min(text.life,1), text.r or 255, text.g or 255, text.b or 255)
			y = y + height + 2
		end
	end

	if DEBUG.showDebug and DEBUG.showGameDebugRender then
		states.renderDebug()
	end

	if DEBUG.showDebug and DEBUG.showPerformanceBreakdown  and DEBUG.performanceGroup == "sections" then
		debug.enterPerformanceType("totalWaitTime")
	end
end

function debug.onClose()
	if next(debugRuntime.logs) then
		for index, log in pairs(debugRuntime.logs) do
			local path = daisy.getUserFolderPath(FOLDERS.log)
			table.save(log, path..index..EXTENSIONS.log)
		end
	end
	
	if next(debugRuntime.completeLog) then
		local path = daisy.getUserFolderPath(FOLDERS.log)
		table.save(debugRuntime.completeLog, path.."complete"..EXTENSIONS.log)
	end
end

function debug.keyPressed(key)
	
	debugRuntime.lastKey = key
	
	if DEBUG.consoleEnabled then
		return true
	end
	
	if key == 8 then
		daisy.clearPrint()
	end
	
	if DEBUG.allowTabPause then
		if key == 9 then
			debugRuntime.pause = not debugRuntime.pause
			if not debugRuntime.pause then
				app.setWindowBlurSleep()
			end
			return true
		end
	end
	
	if DEBUG.enableDebugKeys then
		if key == DEBUG.enableShortcutsKey then
			DEBUG.enableShortcuts = not DEBUG.enableShortcuts
			return true
		end
	
	
		if DEBUG.enableShortcuts then
			for index, opt in pairs(DEBUG.options) do
				local keyindex  = index
				if daisy.isKeyPressed(16) then
					if keyindex > string.totalKeys then
						keyindex = keyindex - string.totalKeys
					else
						keyindex = nil
					end
				end
				if keyindex then
					if string.indexToNumberAndLowerCaseKey(keyindex) == key then
						if opt.type == "toggle" then
							DEBUG[opt.entry] = not DEBUG[opt.entry]
							if DEBUG[opt.entry] then
								debug.print(1, "toggles", opt.entry.. " on", 190, 240, 255)
							else
								debug.print(1, "toggles", opt.entry.. " off", 128, 64, 64)
							end
						elseif opt.type == "cycle" then
							for index, v in ipairs(opt.cycle) do
								if DEBUG[opt.entry] == v then
									DEBUG[opt.entry] = opt.cycle[((index)%(#opt.cycle)) + 1]
									break
								end
							end	
						end
						return true
					end
				end
			end
		end

	
		for index, k in pairs(DEBUG.keyShortcuts) do
			if k.key == key then
				if k.type == "toggle" then
					DEBUG[k.entry] = not DEBUG[k.entry]
					return true
				end
			end
		end
	end
	
	return false
end

function debug.keyCharacter(byte)

	debugRuntime.lastChar = byte;
	
	if DEBUG.consoleEnabled and app.developerMode then
		if byte == 8 then -- Backspace
			if string.len(DEBUG.consoleBuffer) > 0 then
				DEBUG.consoleBuffer = string.sub(DEBUG.consoleBuffer, 0, string.len(DEBUG.consoleBuffer) - 1)
			end
		elseif byte == 13 or byte == 27 then -- Enter/Esc
			if string.len(DEBUG.consoleBuffer) > 0 then
				print("Running " .. DEBUG.consoleBuffer)
				local status, err = pcall(loadstring(DEBUG.consoleBuffer))
				
				if not status then
					print("Console error: " .. tostring(err))
				end
			end
			DEBUG.consoleBuffer = nil
			DEBUG.consoleEnabled = false
		else -- Regular character
			DEBUG.consoleBuffer = DEBUG.consoleBuffer .. string.char(byte)
		end
		return true
	else
		if byte == 96 and app.developerMode then -- Console key
			DEBUG.consoleEnabled = true -- Start intercepting all input
			DEBUG.consoleBuffer = ""
			return true
		end
	end
end