--	ShowMMR dashboard mod by AveYo, 2023.08.22	--

if not IsServer() then return end -- local lua server instance only

if ShowMMR == nil then ShowMMR = class({}) end

function ShowMMR:Init(e)
	if GameRules then return end -- dashboard only

	-- support multiple users data on the same machine - 0 is any last user
	self.user = e.networkid:match('^%[%a:[0-5]:(%d+).*%]$') or 0
	self.data = self.data or {}; self.matches = self.matches or {}; self.history = self.history or {}

	-- mmr local data is serialized to JOY1 - JOY32 binds for up to 640 recent matches
	if self.bind == nil then self.bind = {}; for i=1,32 do table.insert(self.bind, 'JOY'.. i) end end

	-- read back per-user mmr local data from cfg/user_keys_[user]_slot3.vcfg
	local data_file = LoadKeyValues('cfg/user_keys_' .. self.user .. '_slot3.vcfg')
	if data_file ~= nil and data_file.bindings ~= nil then
		local list = {}
		for _,hotkey in ipairs(self.bind) do
			local val = data_file.bindings[hotkey]
			if val ~= nil and val:sub(11,11) == ':' and val:sub(12,12) == '[' then table.insert(list, val) end
		end
		self.data = json.decode('{' .. table.concat(list, ',') .. '}')
		vlua.tableadd(self.history, self.data)
	end

	-- read per-user matches data, if data_file was generated by ShowMMR external tool
	-- this wont get serialized so it might need a refresh every 640 matches or so ;)
	if data_file ~= nil and data_file.matches ~= nil then
		for _,v in pairs(data_file.matches) do
			self.matches[v.date] = {v.mmr, v.outcome}
		end
		vlua.tableadd(self.history, self.matches)
	end

	-- make the local vscript mmr data available to panorama ui via CustomNetTables
	local kv, i, j, limit = {}, 1, 1, 500
	for k,v in pairs(self.history) do
		kv[' ' .. k] = {v[1], v[2]}  -- must save k as a string due to CustomNetTables rounding bug
		i = i + 1  -- if there are 500 + matches do multiple runs to stay under 16384 bytes as per valve wiki
		if i > limit then
			if table.clear == nil then require 'table.clear' end
			CustomNetTables:SetTableValue('ShowMMR_history', 'kv' .. j, kv)
			j = j + 1; i = 1; table.clear(kv)
		end
	end
	CustomNetTables:SetTableValue('ShowMMR_history', 'kv', kv)

	-- cfg initialization - once per player_connect event - note that lua vm is restarted after every game
	if self.cfg == nil then
		self.cfg = {}

		Convars:RegisterCommand('cfg', function(_, key, _, val, ...)
			if key and key:match('%l[%l%d_]+') and val then
				self.cfg[key] = val
				if key == 'cfg_updated' and val == '1' then
					FireGameEvent('round_start', {round_name = 'cfg_updated', round_number = 1}) -- signal self.cfg updated
				end
			end
		end, 'pipe console keyvalue-like output to vscript cfg [recent_game_time_1=timestamp]..', 0)

		if CustomGameEventManager then
			CustomGameEventManager:RegisterListener("ShowMMR_Refresh", function(...) return ShowMMR:Refresh(...) end)
		end
	end

  --ShowMMR:Refresh(1, {round_name = 'cfg_updated'})
end

function ShowMMR:Refresh(i, e)
	-- e event received from panorama dashboard_background_manager
	self.mmr = tonumber(e.mmr) or -1

	-- grab recent_game_time.. values from console into self.cfg table, then signal Save function via round_start event
	SendToServerConsole('dota_game_account_client_debug | cfg; echo "cfg_updated: 1" | cfg;')
end

function ShowMMR:Save(e)
	if type(e) ~= 'table' or e.round_name ~= 'cfg_updated' then return end -- local cfg_updated event only

	-- search the local mmr table for recent_game_time.. values grabbed from console
	local time_1, time_2, time_3 = self.cfg.recent_game_time_1, self.cfg.recent_game_time_2, self.cfg.recent_game_time_3
	local find_1, find_2, find_3 = self.history[tonumber(time_1)], self.history[tonumber(time_2)], self.history[tonumber(time_3)]
	local rank_1 = self.mmr or 0
	local serialize = false

	-- add mmr and change numbers for the 1st recent game if applicable
	if time_1 ~= nil and rank_1 > 0 and (find_1 == nil or find_1[1] == 0 or find_1[2] == 0) then
		local change, t = 0, tonumber(time_1)
		if find_2 ~= nil and find_2[1] > 0 then change = rank_1 - find_2[1] end
		vlua.tableadd(self.data, {[t] = {rank_1, change}})
		CustomNetTables:SetTableValue('ShowMMR_update', ' ' .. time_1, {rank_1, change})
		print('ShowMMR recent_game_time_1:', time_1, rank_1, change)
		serialize = true
	end

	-- add change number for the 2nd recent game if applicable
	if find_2 ~= nil and find_2[1] > 0 and find_2[2] == 0 and find_3 ~= nil and find_3[1] > 1 then
		local rank_2, change, t = find_2[1], find_2[1] - find_3[1], tonumber(time_2)
		vlua.tableadd(self.data, {[t] = {rank_2, change}})
		CustomNetTables:SetTableValue('ShowMMR_update', ' ' .. time_2, {rank_2, change})
		print('ShowMMR recent_game_time_2:', time_2, rank_2, change)
		serialize = true
	end

	-- discovered recent_game_time_x can be unreliable causing the (0) changes
	local fixup = -1
	ordered = {}; for n in pairs(self.data) do table.insert(ordered, n) end
	table.sort(ordered, function(a,b) return a > b end)
	for _, v in ipairs(ordered) do
		local v1, v2 = self.data[v][1], self.data[v][2]
		if fixup == -1 and v1 ~= 0 and v2 == 0 then fixup = v print('fixup') end
		if fixup ~= -1 and v1 ~= 0 and v1 ~= self.data[fixup][1] then
			self.data[fixup][2] = self.data[fixup][1] - v1
			print('ShowMMR recent_game_time_x:', fixup, self.data[fixup][1], self.data[fixup][2])
			CustomNetTables:SetTableValue('ShowMMR_update', ' ' .. fixup, {self.data[fixup][1], self.data[fixup][2]})
			fixup = -1
			serialize = true
		end
	end

	-- serialize local mmr data to JOY1-JOY32 binds in cfg/user_keys_[userid]_slot3.vcfg
	if serialize == true then
		if table.nkeys == nil then require 'table.nkeys' end
		local remain, limit, pages, line, text = table.nkeys(self.data), table.nkeys(self.bind), 1, 0, ''
		for _, v in ipairs(ordered) do
			line = line + 1; text = text .. ',' .. v .. ':[' .. self.data[v][1] .. ',' .. self.data[v][2] .. ']'
			if line == remain or line == math.min(20, remain) then
				SendToServerConsole('bindss 3 ' .. self.bind[pages] .. ' "' .. text:sub(2) .. '";')
				remain = remain - line;  line = 0;  pages = pages + 1;  text = ''
				if pages > limit then break end
			end
		end
		SendToServerConsole('writekeybindings | grep % ^;')
	end
end

ListenToGameEvent('player_connect', Dynamic_Wrap(ShowMMR, 'Init'), ShowMMR)
ListenToGameEvent('round_start',	Dynamic_Wrap(ShowMMR, 'Save'), ShowMMR)

