local theme, material = MG_Scoreboard.Themes[MG_Scoreboard.Theme], MG_Scoreboard.Materials

local players = {}
local players_cnt = 0
local admin_count = 0
local max_players = 0
local host_name = ""

function MG_Scoreboard.CreateIconHeader(str, font, x, y, icon, align)
	MG_Scoreboard.DrawText(str, font, x, y, theme.white, align and align or TEXT_ALIGN_CENTER)
	if icon then
		MG_Scoreboard.DrawIcon(icon, x - 27, y - 3)
	end
end

local frame
function MG_Scoreboard.CreateDialogueBox(ply, tbl)
	if IsValid(frame) then
		frame:Remove()
	end
	local title = tbl.title
	local reason = tbl.reason
	local time = tbl.time
	local args = tbl.cmd
	local func = tbl.func
	local reasontext = tbl.reasontext or MG_Scoreboard.Language.Input
	local timetext = tbl.timetext or MG_Scoreboard.Language.Time
	local hideicon = tbl.hideicon or false
	frame = vgui.Create("DFrame")
	frame:SetSize(325, time and reason and 170 or 119)
	frame:SetTitle("")
	frame:ShowCloseButton(false)
	frame:SetDraggable(false)
	frame:Center()
	frame:MakePopup()
	frame:SetDrawOnTop(true)
	title = string.Replace(title, "#name", ply:Name())
	frame.Paint = function(self, w, h)
		if !IsValid(ply) then
			frame:Remove()
			return
		end
		MG_Scoreboard.DrawRect(0, 0, w, h, theme.background)
		MG_Scoreboard.DrawRect(0, 0, w, 25, theme.interface_seperator)
		MG_Scoreboard.DrawRect(0, 26, w, 2, theme.line_seperator)
		if !hideicon then
			MG_Scoreboard.DrawIcon(material["punish_tag"], -3, -3)
		end
		if reason then
			MG_Scoreboard.DrawText(reasontext, "MG_Scoreboard_20", 10, 31, theme.interface_headers, TEXT_ALIGN_LEFT)
		end
		if time then
			MG_Scoreboard.DrawText(timetext, "MG_Scoreboard_20", 10, reason and 81 or 31, theme.interface_headers, TEXT_ALIGN_LEFT)
		end
	end
	local header = vgui.Create("DLabel", frame)
	header:SetPos(hideicon and 5 or 25, 0)
	header:SetSize(hideicon and 295 or 275, 25)
	header:SetFont("MG_Scoreboard_20")
	header:SetTextColor(theme.title_color)
	header:SetText(title)
	local closebutton = vgui.Create("DLabel", frame)
	closebutton:SetSize(21, 19)
	closebutton:SetPos(frame:GetWide() - 24, 2)
	closebutton:SetText(" x")
	closebutton:SetFont("MG_Scoreboard_24")
	closebutton:SetTextColor(Color(224, 118, 118, 120))
	closebutton:SetMouseInputEnabled(true)
	closebutton:SetCursor("hand")
	closebutton.DoClick = function()
		frame:Close()
	end
	local confirm = MG_Scoreboard.CreateButton(frame, frame:GetWide() / 2 - 55, time and reason and frame:GetTall() / 2 + 47 or frame:GetTall() - 35, 110, 30, "MG_Scoreboard_19", MG_Scoreboard.Language.Confirm)
	local reason_input, time_input
	if reason then
		reason_input = vgui.Create("DTextEntry", frame)
		reason_input:SetFont("MG_Scoreboard_18")
		reason_input:SetSize(300, 25)
		reason_input:SetValue("")
		reason_input:SetPos(10, 52)
		reason_input.Paint = function(self, w, h)
			MG_Scoreboard.DrawRect(0, 0, w, h, theme.white)
			self:DrawTextEntryText(theme.dark_grey, theme.grey, theme.grey)
		end
		reason_input.OnEnter = function()
			confirm:DoClick()
		end
	end
	if time then
		time_input = vgui.Create("DTextEntry", frame)
		time_input:SetFont("MG_Scoreboard_18")
		time_input:SetSize(300, 25)
		time_input:SetValue(0)
		time_input.Untouched = true
		time_input:SetPos(10, reason and 102 or 52)
		time_input:SetNumeric(true)
		time_input.Paint = function(self, w, h)
			MG_Scoreboard.DrawRect(0, 0, w, h, theme.white)
			self:DrawTextEntryText(theme.dark_grey, theme.grey, theme.grey)
		end
		local oldMousePressed = time_input.OnMousePressed
		time_input.OnMousePressed = function(self,code)
			oldMousePressed(self, code)
			if time_input.Untouched then
				self:SetValue("")
			end
		end
		time_input.OnEnter = function()
			confirm:DoClick()
		end
	end
	confirm.Paint = function(self, w, h)
		MG_Scoreboard.DrawRect(0, 0, w, h, theme.confirm_button)
		if self:IsHovered() then
			MG_Scoreboard.DrawOutline(0, 0, w, h, 2, color_white)
		end
	end
	confirm.DoClick = function()
		if !IsValid(ply) then return end
		local r, t = reason and reason_input:GetValue(), time and tonumber(time_input:GetValue())
		if !r then
			r = ""
		end
		if time and (!t or !isnumber(t)) then
			LocalPlayer():ChatPrint(MG_Scoreboard.Language.Error)
			return
		end
		if args then
			local target = tbl.ulx and "$"..ply:UserID() or ply:UserID()
			LocalPlayer():ConCommand(reason and time and string.format("%s %s %i \"%s\"", args, target, t, r) or reason and string.format("%s %s \"%s\"", args, target, r) or time and string.format("%s %s %i", args, target, t))
		elseif func then
			func(ply, t or r, r or t)
		end
		frame:Close()
	end
end

local function AddNewLine(zone, wide, tall)
	local newline = vgui.Create("DPanel")
	newline:SetSize(wide)
	if tall then
		newline:SetTall(tall)
	end
	newline.Paint = function()
	end
	zone:AddItem(newline)
end

function MG_Scoreboard.AddPlayerInfo(local_ply, pan, ply)
	if !IsValid(ply) then return end
	if IsValid(pan.PlayerInfo) then
		pan.PlayerInfo:Remove()
	end
	local wide = pan:GetWide()
	local zone = vgui.Create("DPanelList")
	pan.PlayerInfo = zone
	zone:SetSize(wide, pan:GetTall())
	zone:SetPadding(5)
	zone:SetSpacing(5)
	zone:EnableHorizontal(true)
	zone.Paint = function(self, w, h)
		MG_Scoreboard.DrawRect(0, 0, w, h, theme.other_grey)
	end
	pan:SetContents(zone)
	local curtime, is_admin, is_sadmin, new_line = CurTime(), local_ply:IsAdmin(), local_ply:IsSuperAdmin(), false
	local ply_info, cnt, second = {}, 0, 0
	for k, v in ipairs(MG_Scoreboard.PlayerInfo) do
		if v.admin and !is_admin then continue end
		if v.sadmin and !is_sadmin then continue end
		local visible = v.visible
		if visible and !visible(ply) then continue end
		cnt = cnt + 1
		table.insert(ply_info, v)
	end
	for k, v in ipairs(ply_info) do
		local name = v.name
		local func = v.func
		local txt = name..": "
		local lbl = vgui.Create("DLabel")
		lbl:SetSize(400, 20)
		lbl:SetFont("MG_Scoreboard_20")
		lbl:SetTextInset(7, 0)
		lbl:SetText(txt..func(ply))
		lbl:SetTextColor(color_white)
		lbl:SetTooltip(string.format(MG_Scoreboard.Language.Copy, name))
		local last_update = curtime + 1
		lbl.Think = function(self)
			if !IsValid(ply) then return end
			local curtime = CurTime()
			if last_update > curtime then return end
			last_update = curtime + 1
			local func_run = func(ply)
			if func_run then
				self:SetText(txt..func_run)
			else
				self:Remove()
			end
		end
		lbl.DoClick = function(self)
			if !IsValid(ply) then return end
			local result = func(ply)
			SetClipboardText(result)
			ply:ChatPrint(string.format(MG_Scoreboard.Language.Copied, name, result, ply:Name()))
		end
		local clr = Color(4, 4, 4, 100)
		lbl.Paint = function(self, w, h)
			MG_Scoreboard.DrawRect(0, 0, w, h, clr)
			if self:IsHovered() then
				MG_Scoreboard.DrawOutline(0, 0, w, h, 2, color_white)
			end
		end
		zone:AddItem(lbl)
		if wide > 810 then
			second = second + 1
			if second == 2 and k != cnt then
				second = 0
				AddNewLine(zone, wide)
			end
		end
		new_line = true
	end
	if new_line then
		AddNewLine(zone, wide)
	end
	for _, v in ipairs(MG_Scoreboard.Buttons) do
		if v.admin and !is_admin then continue end
		if v.sadmin and !is_sadmin then continue end
		local visible = v.visible
		if visible and !visible(ply) then continue end
		if v.newline then
			AddNewLine(zone, wide)
		end
		local name = v.name
		surface.SetFont("MG_Scoreboard_20")
		local size_x = surface.GetTextSize(name)
		size_x = math.max(100, size_x + 15)
		local btn = MG_Scoreboard.CreateButton(zone, 0, 0, size_x, 23, "MG_Scoreboard_20", v.name)
		local clr = Color(4, 4, 4, 200)
		btn.Paint = function(self, w, h)
			MG_Scoreboard.DrawRect(0, 0, w, h, clr)
			if self:IsHovered() then
				MG_Scoreboard.DrawOutline(0, 0, w, h, 2, color_white)
			end
		end
		btn.DoClick = function()
			if !IsValid(ply) then return end
			local title = v.title
			if !title and v.func then
				v.func(ply)
				return
			end
			if title then
				MG_Scoreboard.CreateDialogueBox(ply, v)
			elseif v.cmd then
				local target = v.ulx and "$"..ply:UserID() or ply:UserID()
				local_ply:ConCommand(v.cmd.." "..target)
			end
		end
		zone:AddItem(btn)
	end
end

local cats_to_close = {}
local scoreboard_team_color = {}
function MG_Scoreboard.AddPlayer(local_ply, player_list, ply, scrw, scrh)
	local pan = vgui.Create("DCollapsibleCategory", player_list)
	pan:SetAnimTime(0.1)
	pan:SetExpanded(0)
	pan:SetLabel("")
	pan:SetSize(player_list:GetWide() - 25, 30)
	pan:GetChildren()[1]:SetTall(30)
	pan:Dock(TOP)
	pan:DockMargin(0, 0, 0, 1)
	pan.Paint = function(self, w, h)
		if !IsValid(ply) then self:Remove() return end
		local y = 3
		local clr_job = ply:Team()
		scoreboard_team_color[clr_job] = scoreboard_team_color[clr_job] or team.GetColor(clr_job)
		local clr = scoreboard_team_color[clr_job]
		MG_Scoreboard.DrawRect(0, 0, w, h, clr)
		if ply == local_ply or ply:GetFriendStatus() == "friend" then
			clr = Color(255, 255, 255, math.max(0, 35 * math.sin(CurTime() * 3)))
			MG_Scoreboard.DrawRect(0, 0, w, h, clr)
		end
		MG_Scoreboard.DrawText(ply:Name(), "MG_Scoreboard_24", 35, y, theme.player_name, TEXT_ALIGN_LEFT)
		MG_Scoreboard.DrawText(ply:getDarkRPVar("job"), "MG_Scoreboard_24", scrw * 0.4 + 5, y, theme.player_job)
		local wanted = ply:getDarkRPVar("wanted")
		if wanted then
			local sin = math.sin(CurTime() * 3) * 255
			MG_Scoreboard.DrawText(MG_Scoreboard.Language.Wanted, "MG_Scoreboard_24", scrw * 0.62, y, Color(255, sin, sin))
		end
		MG_Scoreboard.DrawText(string.format("%i / %i", ply:Frags(), ply:Deaths()), "MG_Scoreboard_24", w - 200, y, theme.player_kd)
		MG_Scoreboard.DrawText(ply:Ping().." ms", "MG_Scoreboard_24", w - 80, y, theme.player_ping)
	end
	pan.OnToggle = function(self)
		if self.IgnoreToggle then
			self.IgnoreToggle = nil
			return
		end
		for _, v in pairs(cats_to_close) do
			if !IsValid(v) or self == v then continue end
			if v:GetExpanded() then
				v.IgnoreToggle = true
				v:Toggle()
			end
		end
	end
	local oldToggle = pan.Toggle
	pan.Toggle = function(self)
		if !self:GetExpanded() then
			MG_Scoreboard.AddPlayerInfo(local_ply, pan, ply)
		end
		oldToggle(self)
	end
	local oldRightClick = pan.Header.DoRightClick
	pan.Header.DoRightClick = function(self)
		local menu_contents = {}
		local is_admin, is_superadmin = local_ply:IsAdmin(), local_ply:IsSuperAdmin()
		for k, v in ipairs(MG_Scoreboard.RightClickMenu) do
			if v.admin and !is_admin then continue end
			if v.sadmin and !is_superadmin then continue end
			table.insert(menu_contents, v)
		end
		if #menu_contents > 0 then
			mg_dmenu = DermaMenu()
			for k, v in ipairs(menu_contents) do
				if v.newline then
					mg_dmenu:AddSpacer()
				end
				local parent = mg_dmenu:AddOption(v.name, function()
					if !IsValid(ply) then return end
					if v.func then
						v.func(ply)
					else
						local_ply:ConCommand(v.cmd.." \""..ply:Name().."\"")
					end
				end)
				local icon = v.icon
				if icon then
					parent:SetIcon(icon)
				end
			end
			mg_dmenu:Open()
		end
		oldRightClick(self)
	end
	local avatar = vgui.Create("AvatarImage", pan)
	avatar:SetSize(24, 24)
	avatar:SetPos(4, 3)
	avatar:SetPlayer(ply)
	avatar:SetMouseInputEnabled(false)
	local av_button = vgui.Create("DButton", pan)
	av_button:SetSize(24, 24)
	av_button:SetText("")
	av_button.Paint = function()
	end
	av_button.DoClick = function()
		if !IsValid(ply) then return end
		ply:ShowProfile()
	end
	local mute = vgui.Create("DImageButton", pan)
	mute:SetSize(22, 22)
	mute:SetPos(player_list:GetWide() - 30, 4)
	mute:SetImage(ply:IsMuted() and "mg_scoreboard/mute.png" or "mg_scoreboard/unmute.png")
	mute:SetColor(theme.white)
	mute.DoClick = function()
		if !IsValid(ply) then return end
		if ply:IsMuted() then
			ply:SetMuted(false)
		else
			ply:SetMuted(true)
		end
		mute:SetImage(ply:IsMuted() and "mg_scoreboard/mute.png" or "mg_scoreboard/unmute.png")
	end
	table.insert(cats_to_close, pan)
end

function MG_Scoreboard.UpdatePlayers(mg_scoreboard, local_ply)
	players = player.GetAll()
	players_cnt = 0
	admin_count = 0
	for i=#players, 1, -1 do
		local ply = players[i]
		if ply:GetMGVar("scoreboard_hide") and !local_ply:IsSuperAdmin() and local_ply != ply then
			table.remove(players, i)
			continue
		end
		players_cnt = players_cnt + 1
		if ply:IsAdmin() then
			admin_count = admin_count + 1
		end
	end
	max_players = game.MaxPlayers()
	host_name = GetHostName()
	timer.Create("MG_Scoreboard_UpdatePlayers", 1, 1, function()
		if IsValid(mg_scoreboard) and mg_scoreboard:IsVisible() then
			MG_Scoreboard.UpdatePlayers(mg_scoreboard, local_ply)
		end
	end)
end

local sort_cvar = CreateClientConVar("mg_scoreboard_sortmethod", 1, true, false)

local sorts = {
	"Kategorie ↑",
	"Kategorie ↓",
	"Name ↑",
	"Name ↓",
	"Job ↑",
	"Job ↓",
	"K/D ↑",
	"K/D ↓",
	"Keine Sortierung"
}

local categories_names = {}
local categories_ordered = {}
local categories_jobs = {}
timer.Simple(0, function()
	for _, v in ipairs(DarkRP.getCategories()["jobs"]) do
		if #v.members > 0 then
			categories_names[v.name] = (v.sortOrder or 1)
			table.insert(categories_ordered, v.name)
			for _, m in ipairs(v.members) do
				categories_jobs[v.name] = categories_jobs[v.name] or {}
				categories_jobs[v.name][m.team] = (m.sortOrder or 1) or math.huge
			end
		end
	end
end)

function MG_Scoreboard.SortPlayers(mg_scoreboard, local_ply, scrw, scrh)
	local player_list = mg_scoreboard.PlayerList
	player_list:Clear()
	local sort = math.floor(sort_cvar:GetInt())
	if sort == 1 or sort == 2 then
		for _, v in ipairs(players) do
			local job_tbl = v:getJobTable()
			if job_tbl then
				local cat = job_tbl.category
				local p1 = categories_names[cat]
				local p2 = categories_jobs[cat] and categories_jobs[cat][job_tbl.team]
				if cat and p1 and p2 then
					v.MG_Scoreboard_SortOrder = (p1 * 100) + categories_jobs[cat][job_tbl.team]
				else
					v.MG_Scoreboard_SortOrder = math.huge
				end
			else
				v.MG_Scoreboard_SortOrder = math.huge
			end
		end
		if sort == 1 then
			table.sort(players, function(a, b) return a.MG_Scoreboard_SortOrder < b.MG_Scoreboard_SortOrder end)
		else
			table.sort(players, function(a, b) return a.MG_Scoreboard_SortOrder > b.MG_Scoreboard_SortOrder end)
		end
	elseif sort == 3 then
		table.sort(players, function(a, b) return a:Name() > b:Name() end)
	elseif sort == 4 then
		table.sort(players, function(a, b) return a:Name() < b:Name() end)
	elseif sort == 5 then
		table.sort(players, function(a, b) return team.GetName(a:Team()) < team.GetName(b:Team()) end)
	elseif sort == 6 then
		table.sort(players, function(a, b) return team.GetName(a:Team()) > team.GetName(b:Team()) end)
	elseif sort == 7 then
		table.sort(players, function(a, b) return a:Frags() > b:Frags() end)
	elseif sort == 8 then
		table.sort(players, function(a, b) return a:Frags() < b:Frags() end)
	end
	for _, v in ipairs(players) do
		MG_Scoreboard.AddPlayer(local_ply, player_list, v, scrw, scrh)
	end
end

local function CreateSortButton(mg_scoreboard, x, y, num, scrw, scrh, local_ply)
	if mg_scoreboard.ButtonsCreated then return end
	local button = vgui.Create("DComboBox", mg_scoreboard)
	button:SetSize(200, 25)
	button:SetPos(x, y)
	button:SetText("Sortieren nach...")
	button:SetSortItems(false)
	button.OnSelect = function(self, value, data)
		RunConsoleCommand("mg_scoreboard_sortmethod", value)
		timer.Simple(0, function()
			if !IsValid(mg_scoreboard) then return end
			MG_Scoreboard.UpdatePlayers(mg_scoreboard, local_ply)
			MG_Scoreboard.SortPlayers(mg_scoreboard, local_ply, scrw, scrh)
		end)
	end
	local sort = sort_cvar:GetInt()
	local sort_after = sorts[sort]
	if sort_after then
		button:SetText("Sortieren nach: "..sort_after)
	else
		button:SetText("Sortieren nach: Keine Sortierung")
	end
	for k, v in ipairs(sorts) do
		button:AddChoice("Sortieren nach: "..v, k)
	end
end

function MG_Scoreboard.CreateHeader(w, h, scrw, scrh, local_ply)
	local row_y = 43
	MG_Scoreboard.DrawRect(5, 40, w - 10, 2, theme.line_seperator)
	MG_Scoreboard.DrawRect(5, 70, w - 10, 2, theme.line_seperator)
	MG_Scoreboard.CreateIconHeader(MG_Scoreboard.Language.Name, "MG_Scoreboard_26", 50, row_y, material["name_tag"], TEXT_ALIGN_LEFT)
	CreateSortButton(mg_scoreboard, 5, 5, 0, scrw, scrh, local_ply)
	MG_Scoreboard.CreateIconHeader(MG_Scoreboard.Language.Job, "MG_Scoreboard_26", scrw * 0.4, row_y, material["job_tag"], TEXT_ALIGN_LEFT)
	MG_Scoreboard.CreateIconHeader(MG_Scoreboard.Language.KD, "MG_Scoreboard_26", w - 210, row_y, material["gun_tag"], TEXT_ALIGN_LEFT)
	MG_Scoreboard.CreateIconHeader(MG_Scoreboard.Language.Ping, "MG_Scoreboard_26", w - 95, row_y, material["ping_tag"], TEXT_ALIGN_LEFT)
	mg_scoreboard.ButtonsCreated = true
end

function MG_Scoreboard.CreateScoreboard()
	local local_ply = LocalPlayer()
	if !IsValid(local_ply) then return end
	local scrw, scrh = ScrW(), ScrH()
	cats_to_close = {}
	gui.EnableScreenClicker(true)
	if !IsValid(mg_scoreboard) then
		mg_scoreboard = vgui.Create("DFrame")
		mg_scoreboard:SetSize(scrw * 0.9, scrh * 0.9)
		mg_scoreboard:Center()
		mg_scoreboard:SetTitle("")
		mg_scoreboard:ShowCloseButton(false)
		mg_scoreboard:SetDraggable(false)
		mg_scoreboard.Paint = function(self, w, h)
			MG_Scoreboard.DrawRect(0, 0, w, h, theme.background)
			MG_Scoreboard.DrawText(host_name, "MG_Scoreboard_38", w / 2, -1, theme.title_color)
			MG_Scoreboard.CreateHeader(w, h, scrw, scrh, local_ply)
			MG_Scoreboard.DrawText(string.format(MG_Scoreboard.Language.Players, players_cnt, max_players), "MG_Scoreboard_26", w / 2, h - 47, theme.title_color)
			MG_Scoreboard.DrawText(string.format(MG_Scoreboard.Language.Admins, admin_count), "MG_Scoreboard_26", w / 2, h - 27, theme.title_color)
			MG_Scoreboard.DrawRect(20, h - 50, w - 40, 2, theme.line_seperator)
		end
		mg_scoreboard.OnScreenSizeChanged = function(self)
			self:Remove()
		end
		local scroll = vgui.Create("DScrollPanel", mg_scoreboard)
		scroll:SetPos(5, 80)
		scroll:SetSize(mg_scoreboard:GetWide(), mg_scoreboard:GetTall() - 135)
		MG_Scoreboard.PaintBar(scroll, theme.white)
		local player_list = vgui.Create("DListLayout", scroll)
		player_list:SetSize(scroll:GetWide() - 10, scroll:GetTall())
		mg_scoreboard.PlayerList = player_list
	else
		mg_scoreboard:SetVisible(true)
	end
	MG_Scoreboard.UpdatePlayers(mg_scoreboard, local_ply)
	MG_Scoreboard.SortPlayers(mg_scoreboard, local_ply, scrw, scrh)
end

function MG_Scoreboard.ShowScoreboard()
	MG_Scoreboard.CreateScoreboard()
	hook.Run("MG_ScoreboardOpen")
	return true
end
hook.Add("ScoreboardShow", "MG_Scoreboard_ShowScoreboard", MG_Scoreboard.ShowScoreboard)

function MG_Scoreboard.HideScoreboard()
	if IsValid(mg_scoreboard) then 
		gui.EnableScreenClicker(false) 
		mg_scoreboard:SetVisible(false)
		CloseDermaMenus()
		hook.Run("MG_ScoreboardClose")
	end
	return true
end
hook.Add("ScoreboardHide", "MG_Scoreboard_HideScoreboard", MG_Scoreboard.HideScoreboard)

concommand.Add("mg_scoreboard_reload", function()
	if IsValid(mg_scoreboard) then 
		mg_scoreboard:Remove()
	end
end)