Quantcast
--[===[ File
NAME: TitanLootType.lua
DESC:
This Titan plugin will show group type and loot type in the button text.
A roll tracker is used to show rolls in party to help make passing out loot and chests.
This is a simplistic roll helper! It is NOT intended as a loot tracker!

On right-click it will show the normal Titan options.
The left-click it will depend whether the user is group leader or loot master :
  if not leader a /roll will be done.
  if leader the tracker window will pop, giving them additional options
  - Start   : Start a new roll
  - Remind  : Nag those who have not rolled with a whisper
  - Results : State the results

Once a leader initiates a roll, the tracker will pop for other Titan users who are using LootType.
:DESC
--]===]
TitanPanelLootType                 = {} -- declare name space
local LT                           = TitanPanelLootType -- save some typing...

-- ******************************** Constants *******************************
local TITAN_LOOTTYPE_ID            = "LootType";
local _G                           = getfenv(0);
local L                            = LibStub("AceLocale-3.0"):GetLocale(TITAN_ID, true)
local TitanLootMethod              = {};
local updateTable                  = { TITAN_LOOTTYPE_ID, TITAN_PANEL_UPDATE_ALL };
TitanLootMethod["freeforall"]      = { text = L["TITAN_LOOTTYPE_FREE_FOR_ALL"] };
TitanLootMethod["roundrobin"]      = { text = L["TITAN_LOOTTYPE_ROUND_ROBIN"] };
TitanLootMethod["master"]          = { text = L["TITAN_LOOTTYPE_MASTER_LOOTER"] };
TitanLootMethod["group"]           = { text = L["TITAN_LOOTTYPE_GROUP_LOOT"] };
TitanLootMethod["needbeforegreed"] = { text = L["TITAN_LOOTTYPE_NEED_BEFORE_GREED"] };
--TitanLootMethod["personalloot"] = {text = L["TITAN_LOOTTYPE_PERSONAL"]};

local TOCNAME                      = "TitanLootType"
local Track                        = {}
Track.DB = {}

-- High level constants
Track.IconDice                     = "Interface\\Buttons\\UI-GroupLoot-Dice-Up"
--Track.IconGreed= "Interface\\Buttons\\UI-GroupLoot-Coin-Up"
Track.IconPass                     = "Interface\\Buttons\\UI-GroupLoot-Pass-Up"
Track.IconLoot                     = "Interface\\GroupFrame\\UI-Group-MasterLooter"
Track.TxtEscapePicture             = "|T%s:0|t"
Track.TxtEscapeIcon                = "|T%s:0:0:0:0:64:64:4:60:4:60|t"

-- These are encoded 'messages' this addon searches for to start and end the roll process
Track.MSGPREFIX                    = "Titan LootType Roller: " -- also frame title
Track.MSGPREFIX_START              = Track.MSGPREFIX .. ">>: "
Track.MSGPREFIX_END                = Track.MSGPREFIX .. "<<: "
Track.MSGPREFIX_CLOSE              = Track.MSGPREFIX .. "__: "


-- This will be debugged real-time so put the debug on 'switches'
-- Also useful when debugging 'solo'
LT.Debug = {
	-- Used when creating dummy player list (see flags) or solo debug to get 'self'
	-- And debug output
	on    = false,

	show  = {      -- for LootDebug messages
		events = false, -- show debug for events
		players = false, -- show debug for players
		-- nil will show regardless
	},
	flags = { -- solo debug
		force_leader = false,
		force_master = false,
		force_loot_master = false,
		-- These 3 are used when creating dummy player list WITH 'on' = true
		add_players = false,
		is_raid = false,
		is_party = false,
	},
}
--[[
The commands (use WoWLua addon) below will
- set debug depending on what you want to test
- create a player list (via GetPlayerList on start roll) to play with
AND add_players is true
	is_raid will make a 40 player list...
	is_party will make a 5 player list
	if neither makes you a group of 1
- update rolls to check various errors conditions

TitanPanelLootType.Debug.on = true
TitanPanelLootType.Debug.flags.is_raid = true
TitanPanelLootType.Debug.show.players = true
TitanPanelLootType.Debug.show.events = true

TitanPanelLootType.AddAllRollsDebug()

TitanPanelLootType.AddRollDebug("raid6", "999", "1", "1000")
--]]
-- ******************************** Variables *******************************

-- ******************************** Functions *******************************
--[[ local
NAME: LootDebug
DESC: Set the Tian bars and plugins to the selected scale then adjust other frames as needed.
VAR: msg   - message to be output
VAR: mtype - the type of message
OUT: None
--]]
local function LootDebug(msg, mtype)
	local show = false
	if mtype == nil then
		show = true                   -- just show it
	elseif mtype == "events" and LT.Debug.show.events then
		show = true
	elseif mtype == "players" and LT.Debug.show.players then
		show = true
	end

	if show then
		TitanPluginDebug(tostring(msg))
	end
end


-- Tools for the tracker
local Tool = {}
Tool.IconClassTexture = "Interface\\GLUES\\CHARACTERCREATE\\UI-CHARACTERCREATE-CLASSES"
Tool.IconClassTextureWithoutBorder = "Interface\\WorldStateFrame\\ICONS-CLASSES"
Tool.IconClassTextureCoord = CLASS_ICON_TCOORDS
Tool.IconClass = {
	["WARRIOR"] = "|TInterface\\WorldStateFrame\\ICONS-CLASSES:0:0:0:0:256:256:0:64:0:64|t",
	["MAGE"] = "|TInterface\\WorldStateFrame\\ICONS-CLASSES:0:0:0:0:256:256:64:128:0:64|t",
	["ROGUE"] = "|TInterface\\WorldStateFrame\\ICONS-CLASSES:0:0:0:0:256:256:128:192:0:64|t",
	["DRUID"] = "|TInterface\\WorldStateFrame\\ICONS-CLASSES:0:0:0:0:256:256:192:256:0:64|t",
	["HUNTER"] = "|TInterface\\WorldStateFrame\\ICONS-CLASSES:0:0:0:0:256:256:0:64:64:128|t",
	["SHAMAN"] = "|TInterface\\WorldStateFrame\\ICONS-CLASSES:0:0:0:0:256:256:64:128:64:128|t",
	["PRIEST"] = "|TInterface\\WorldStateFrame\\ICONS-CLASSES:0:0:0:0:256:256:128:192:64:128|t",
	["WARLOCK"] = "|TInterface\\WorldStateFrame\\ICONS-CLASSES:0:0:0:0:256:256:192:256:64:128|t",
	["PALADIN"] = "|TInterface\\WorldStateFrame\\ICONS-CLASSES:0:0:0:0:256:256:0:64:128:192|t",
}
Tool.IconClassBig = {
	["WARRIOR"] = "|TInterface\\WorldStateFrame\\ICONS-CLASSES:18:18:-4:4:256:256:0:64:0:64|t",
	["MAGE"] = "|TInterface\\WorldStateFrame\\ICONS-CLASSES:18:18:-4:4:256:256:64:128:0:64|t",
	["ROGUE"] = "|TInterface\\WorldStateFrame\\ICONS-CLASSES:18:18:-4:4:256:256:128:192:0:64|t",
	["DRUID"] = "|TInterface\\WorldStateFrame\\ICONS-CLASSES:18:18:-4:4:256:256:192:256:0:64|t",
	["HUNTER"] = "|TInterface\\WorldStateFrame\\ICONS-CLASSES:18:18:-4:4:256:256:0:64:64:128|t",
	["SHAMAN"] = "|TInterface\\WorldStateFrame\\ICONS-CLASSES:18:18:-4:4:256:256:64:128:64:128|t",
	["PRIEST"] = "|TInterface\\WorldStateFrame\\ICONS-CLASSES:18:18:-4:4:256:256:128:192:64:128|t",
	["WARLOCK"] = "|TInterface\\WorldStateFrame\\ICONS-CLASSES:18:18:-4:4:256:256:192:256:64:128|t",
	["PALADIN"] = "|TInterface\\WorldStateFrame\\ICONS-CLASSES:18:18:-4:4:256:256:0:64:128:192|t",
}

Tool.RaidIconNames = ICON_TAG_LIST
Tool.RaidIcon = {
	"|TInterface\\TargetingFrame\\UI-RaidTargetingIcon_1:0|t", -- [1]
	"|TInterface\\TargetingFrame\\UI-RaidTargetingIcon_2:0|t", -- [2]
	"|TInterface\\TargetingFrame\\UI-RaidTargetingIcon_3:0|t", -- [3]
	"|TInterface\\TargetingFrame\\UI-RaidTargetingIcon_4:0|t", -- [4]
	"|TInterface\\TargetingFrame\\UI-RaidTargetingIcon_5:0|t", -- [5]
	"|TInterface\\TargetingFrame\\UI-RaidTargetingIcon_6:0|t", -- [6]
	"|TInterface\\TargetingFrame\\UI-RaidTargetingIcon_7:0|t", -- [7]
	"|TInterface\\TargetingFrame\\UI-RaidTargetingIcon_8:0|t", -- [8]
}

Tool.Classes = CLASS_SORT_ORDER
Tool.ClassName = LOCALIZED_CLASS_NAMES_MALE
Tool.ClassColor = RAID_CLASS_COLORS

Tool.ButtonFontObj = {}
Tool.Font = ""
Tool.FontSize = ""
--[[ local
NAME: Tool.CreatePattern
DESC: Set the Tian bars and plugins to the selected scale then adjust other frames as needed.
VAR: pattern  - string to be parsed
VAR: maximize - do min or max substitution
OUT: None
--]]
function Tool.CreatePattern(pattern, maximize)
	pattern = string.gsub(pattern, "[%(%)%-%+%[%]]", "%%%1")
	if not maximize then
		pattern = string.gsub(pattern, "%%s", "(.-)")
	else
		pattern = string.gsub(pattern, "%%s", "(.+)")
	end
	pattern = string.gsub(pattern, "%%d", "%(%%d-%)")
	if not maximize then
		pattern = string.gsub(pattern, "%%%d%$s", "(.-)")
	else
		pattern = string.gsub(pattern, "%%%d%$s", "(.+)")
	end
	pattern = string.gsub(pattern, "%%%d$d", "%(%%d-%)")
	--pattern = string.gsub(pattern, "%[", "%|H%(%.%-%)%[")
	--pattern = string.gsub(pattern, "%]", "%]%|h")
	return pattern
end

--[[ local
NAME: Sizing routines
DESC: These allow the frame to be resized as needed.
OUT: None
--]]
---@class ResizeCursorType
---@field Texture? table
local ResizeCursor = {} ---@type Frame
local SizingStop = function(self, button)
	self:GetParent():StopMovingOrSizing()
	if self.GPI_DoStop then self.GPI_DoStop(self:GetParent()) end
end

local SizingStart = function(self, button)
	self:GetParent():StartSizing(self.GPI_SIZETYPE)
	if self.GPI_DoStart then self.GPI_DoStart(self:GetParent()) end
end

local SizingEnter = function(self)
	if not (GetCursorInfo()) then
		ResizeCursor:Show()
		ResizeCursor.Texture:SetTexture(self.GPI_Cursor)
		ResizeCursor.Texture:SetRotation(math.rad(self.GPI_Rotation), { 0.5, 0.5 })
	end
end

local SizingLeave = function(self, button)
	ResizeCursor:Hide()
end

-- Titan Loot

---@class FrameSizeBorder
---@field GPI_SIZETYPE string
---@field GPI_Cursor string
---@field GPI_Rotation string
---@field GPI_DoStart string
---@field GPI_DoStop string
local FrameSizeBorder ---@type Frame
local sizecount = 0
local CreateSizeBorder = function(frame, name, a1, x1, y1, a2, x2, y2, cursor, rot, OnStart, OnStop)
	sizecount = sizecount + 1
	FrameSizeBorder = CreateFrame("Frame", (frame:GetName() or TOCNAME .. sizecount) .. "_size_" .. name, frame)
	FrameSizeBorder:SetPoint("TOPLEFT", frame, a1, x1, y1)
	FrameSizeBorder:SetPoint("BOTTOMRIGHT", frame, a2, x2, y2)
	FrameSizeBorder.GPI_SIZETYPE = name
	FrameSizeBorder.GPI_Cursor = cursor
	FrameSizeBorder.GPI_Rotation = rot
	FrameSizeBorder.GPI_DoStart = OnStart
	FrameSizeBorder.GPI_DoStop = OnStop
	FrameSizeBorder:SetScript("OnMouseDown", SizingStart)
	FrameSizeBorder:SetScript("OnMouseUp", SizingStop)
	FrameSizeBorder:SetScript("OnEnter", SizingEnter)
	FrameSizeBorder:SetScript("OnLeave", SizingLeave)
	return FrameSizeBorder
end

local ResizeCursor_Update = function(self)
	local X, Y = GetCursorPosition()
	local Scale = self:GetEffectiveScale()
	self:SetPoint("CENTER", UIParent, "BOTTOMLEFT", X / Scale, Y / Scale)
end

function Tool.EnableSize(frame, border, OnStart, OnStop)
	if ResizeCursor == {} then
		-- use existing frame
	else
		ResizeCursor = CreateFrame("Frame", nil, UIParent)
		ResizeCursor:Hide()
		ResizeCursor:SetWidth(24)
		ResizeCursor:SetHeight(24)
		ResizeCursor:SetFrameStrata("TOOLTIP")
		ResizeCursor.Texture = ResizeCursor:CreateTexture()
		ResizeCursor.Texture:SetAllPoints()
		ResizeCursor:SetScript("OnUpdate", ResizeCursor_Update)
	end
	border = border or 8

	frame:EnableMouse(true)
	frame:SetResizable(true)

--	path = "Interface\\AddOns\\" .. TOCNAME .. "\\Resize\\"

	CreateSizeBorder(frame, "BOTTOM", "BOTTOMLEFT", border, border, "BOTTOMRIGHT", -border, 0,
		"Interface\\CURSOR\\UI-Cursor-SizeLeft", 45, OnStart, OnStop)
	CreateSizeBorder(frame, "TOP", "TOPLEFT", border, 0, "TOPRIGHT", -border, -border,
		"Interface\\CURSOR\\UI-Cursor-SizeLeft", 45, OnStart, OnStop)
	CreateSizeBorder(frame, "LEFT", "TOPLEFT", 0, -border, "BOTTOMLEFT", border, border,
		"Interface\\CURSOR\\UI-Cursor-SizeRight", 45, OnStart, OnStop)
	CreateSizeBorder(frame, "RIGHT", "TOPRIGHT", -border, -border, "BOTTOMRIGHT", 0, border,
		"Interface\\CURSOR\\UI-Cursor-SizeRight", 45, OnStart, OnStop)

	CreateSizeBorder(frame, "TOPLEFT", "TOPLEFT", 0, 0, "TOPLEFT", border, -border,
		"Interface\\CURSOR\\UI-Cursor-SizeRight", 0, OnStart, OnStop)
	CreateSizeBorder(frame, "BOTTOMLEFT", "BOTTOMLEFT", 0, 0, "BOTTOMLEFT", border, border,
		"Interface\\CURSOR\\UI-Cursor-SizeLeft", 0, OnStart, OnStop)
	CreateSizeBorder(frame, "TOPRIGHT", "TOPRIGHT", 0, 0, "TOPRIGHT", -border, -border,
		"Interface\\CURSOR\\UI-Cursor-SizeLeft", 0, OnStart, OnStop)
	CreateSizeBorder(frame, "BOTTOMRIGHT", "BOTTOMRIGHT", 0, 0, "BOTTOMRIGHT", -border, border,
		"Interface\\CURSOR\\UI-Cursor-SizeRight", 0, OnStart, OnStop)
end

--[[ local
NAME: Moving routines
DESC: These allow the frame to be moved as needed.
OUT: None
--]]
local function MovingStart(self)
	self:StartMoving()
end

local function MovingStop(self)
	self:StopMovingOrSizing()
	if self._GPIPRIVAT_MovingStopCallback then
		self._GPIPRIVAT_MovingStopCallback(self)
	end
end

function Tool.EnableMoving(frame, callback)
	frame:SetMovable(true)
	frame:EnableMouse(true)
	frame:RegisterForDrag("LeftButton")
	frame:SetScript("OnDragStart", MovingStart)
	frame:SetScript("OnDragStop", MovingStop)
	frame._GPIPRIVAT_MovingStopCallback = callback
end

function Tool.SetButtonFont(button)
	-- Just Return. This changed WAY too much...
end

--[[ local
NAME: Tool routines
DESC: Such as split, color, combine.
OUT: None
--]]
function Tool.Split(inputstr, sep)
	if sep == nil then
		sep = "%s"
	end
	local t = {}
	for str in string.gmatch(inputstr, "([^" .. sep .. "]+)") do
		if tContains(t, str) == false then
			table.insert(t, str)
		end
	end
	return t
end

function Tool.RGBtoEscape(r, g, b, a)
	if type(r) == "table" then
		a = r.a
		g = r.g
		b = r.b
		r = r.r
	end

	r = r ~= nil and r <= 1 and r >= 0 and r or 1
	g = g ~= nil and g <= 1 and g >= 0 and g or 1
	b = b ~= nil and b <= 1 and b >= 0 and b or 1
	a = a ~= nil and a <= 1 and a >= 0 and a or 1
	return string.format("|c%02x%02x%02x%02x", a * 255, r * 255, g * 255, b * 255)
	--	return ""
end

function Tool.Combine(t, sep, first, last)
	if type(t) ~= "table" then return "" end
	sep = sep or " "
	first = first or 1
	last = last or #t

	local ret = ""
	for i = first, last do
		ret = ret .. sep .. tostring(t[i])
	end
	return string.sub(ret, string.len(sep) + 1)
end

-- Routines for the 'track' rolls feature
--[[ local
NAME: IsRoll
DESC: Determine if the roll is valid.
VAR: roll - string
VAR: low  - low end
VAR: high - high end
OUT: valid_roll - true / false
OUT: valid_bounds - true / false
OUT: ro - numeric roll
OUT: lo - numeric low
OUT: hi - numeric high
--]]
local function IsRoll(roll, low, high)
	local lo = tonumber(low)
	local hi = tonumber(high)
	local ro = tonumber(roll)

	local valid_roll = false
	local valid_bounds = false

	if (lo == 1 and hi == 100) then
		valid_bounds = true
	end
	if (ro >= 0) and (ro <= 100) then -- allows a pass as valid
		valid_roll = true
	end

	if valid_bounds then
		-- all is good
	else
		-- for now roll is invalid, treat as a pass
		valid_roll = false
		ro = 0
	end

	local str = " >>"
		.. " vr'" .. tostring(valid_roll) .. "'"
		.. " vb'" .. tostring(valid_bounds) .. "'"
		.. " r'" .. tostring(ro) .. "'"
		.. " l'" .. tostring(lo) .. "'"
		.. " h'" .. tostring(hi) .. "'"
	LootDebug(str, "players")

	return valid_roll, valid_bounds, ro, lo, hi
end
--[[ local
NAME: GetPlayer
DESC: Get name, class, guild rank (if in same guild) a the given player.
VAR: id - id of player
OUT: name - name of the player
OUT: rank - guild rank
OUT: englishClass - English class name
--]]
local function GetPlayer(id)
	local name = GetUnitName(id)
	local localizedClass, englishClass, classIndex = UnitClass(id)

	local rank = ""
	if IsInGuild() and UnitIsInMyGuild(id) then
		rank = "<" .. GuildControlGetRankName(C_GuildInfo.GetGuildRankOrder(UnitGUID(id))) .. ">"
	else
		local guildName, guildRankName, guildRankIndex, realm = GetGuildInfo(id)
		if guildName and guildRankName then
			rank = "<" .. guildName .. " / " .. guildRankName .. ">"
		end
	end

	return name, rank, englishClass
end
--[[ local
NAME: IsLead
DESC: Determine whether the player is the leader of the group.
VAR: None
OUT: true / false
--]]
local function IsLead()
	--[[
lootmethod, masterlooterPartyID, masterlooterRaidID = GetLootMethod()
lootmethod
String (LootMethod) - One of 'freeforall', 'roundrobin', 'master', 'group', 'needbeforegreed'. Appears to be 'freeforall' if you are not grouped.: At least as of 7.3 the possible return values appear to be "freeforall", "master", "group" and "personalloot". "roundrobin" and "needbeforegreed" appear to be deprecated.
masterlooterPartyID
Number - Returns 0 if player is the mater looter, 1-4 if party member is master looter (corresponding to party1-4) and nil if the master looter isn't in the player's party or master looting is not used.
masterlooterRaidID
Number - Returns index of the master looter in the raid (corresponding to a raidX unit), or nil if the player is not in a raid or master looting is not used.

raidIndex = UnitInRaid("unit")
raidIndex of "unit" if he is in your raid group, otherwise nil.

isTrue = UnitInParty("arg1")
inGroup = IsInGroup([groupType])

isTrue = UnitIsGroupLeader("unit"[, groupType])
isLeader = IsRaidLeader()

isLeader = UnitIsGroupLeader("unit" or "player name") -- may need in later patches?
--]]
	--[[
	if IsInRaid() and IsRaidLeader() then
		return true
	end
	if IsInGroup() and UnitIsGroupLeader("player") then
		return true
	end
--]]
	if UnitIsGroupLeader("player") then
		return true
	end
	-- The way this flows is both leader AND master looter will have the extra buttons
	local lootmethod, masterlooterPartyID, masterlooterRaidID = GetLootMethod()
	if lootmethod == "master" then
		if IsInRaid() and (masterlooterRaidID == UnitInRaid("player")) then
			return true
		end
		if IsInGroup() and (masterlooterPartyID == UnitInParty("player")) then
			return true
		end
	end
	if LT.Debug.flags.force_leader then
		return true
	end
	if LT.Debug.flags.force_master then
		return true
	end
	return false
end

--[[ local
NAME: Track.GetPlayerList
DESC: Collect name, class, and guild rank (if in same guild) of players in the group.
This routine will generate group lists (5 or 40) for debug based on debug flags.
:DESC
VAR: unsort		- if true sort by class then name
OUT: ret		- indexed table of players in  the group
OUT: retName	- table of player names pointing into ret
--]]
local function OutPlayer(player)
	if LT.Debug.on then --
		if player then
			TitanPluginDebug("GetPlayerList:"
				.. " p'" .. (player.name or "nyl") .. "'"
				.. " r'" .. (player.rank or "nyl") .. "'"
				.. " c'" .. (player.class or "nyl") .. "'"
			)
		end
	end
end
function Track.GetPlayerList(unsort)
	local count, start
	local prefix
	local ret = {}
	local retName = {}

	if IsInRaid() or LT.Debug.flags.is_raid then
		prefix = "raid"
		count = MAX_RAID_MEMBERS
		start = 1
	elseif IsInGroup() or LT.Debug.flags.is_group then
		prefix = "party"
		count = MAX_PARTY_MEMBERS
		start = 0
	else
		prefix = "solo"
		count = 0
		start = 0
	end

	if LT.Debug.on then -- safety...
		if LT.Debug.flags.add_players then
			-- player list has already been created, just return
		elseif prefix == "solo" then
			local name, rank, englishClass = GetPlayer("player")
			if name ~= nil then
				local entry = {
					["name"] = name,
					["rank"] = rank,
					["class"] = englishClass,
				}
				tinsert(ret, entry)
				retName[name] = entry
			end
		else
			local class = 0
			for index = start, count do
				local guildName, guildRankName, guildRankIndex, realm
				local id
				if index > 0 then
					id = prefix .. index
					if class >= 9 then
						class = 1
					else
						class = class + 1
					end
				else
					id = "player"
				end
				-- handle name and class
				local name = id
				local localizedClass, englishClass, classIndex
				if index == 0 then -- get the real player info
					localizedClass, englishClass, classIndex = UnitClass(id)
				elseif class == 1 then
					englishClass = "WARRIOR"
				elseif class == 2 then
					englishClass = "PALADIN"
				elseif class == 3 then
					englishClass = "HUNTER"
				elseif class == 4 then
					englishClass = "ROGUE"
				elseif class == 5 then
					englishClass = "PRIEST"
				elseif class == 6 then
					englishClass = "MAGE"
				elseif class == 7 then
					englishClass = "WARLOCK"
				elseif class == 8 then
					englishClass = "DRUID"
				elseif class == 9 then
					englishClass = "WARRIOR"
				end

				-- Guild section assume this works... :)
				local rank = ""

				if name ~= nil then
					local entry = {
						["name"] = name,
						["rank"] = rank,
						["class"] = englishClass,
					}
					tinsert(ret, entry)
					retName[name] = entry
					OutPlayer(entry)
				end
			end
		end
	else
		-- normal operation
		for index = start, count do
			local guildName, guildRankName, guildRankIndex, realm
			local id
			if index > 0 then
				id = prefix .. index
			else
				id = "player"
			end
			if UnitInParty(id) then
				local name, rank, englishClass = GetPlayer(id)

				if name ~= nil then
					local entry = {
						["name"] = name,
						["rank"] = rank,
						["class"] = englishClass,
					}
					tinsert(ret, entry)
					retName[name] = entry
					OutPlayer(entry)
				end
			end
		end
	end

	if unsort then
		sort(ret, function(a, b) return (a.class < b.class or (a.class == b.class and a.name < b.name)) end)
	end

	return ret, retName
end

--[[ local
NAME: Track.GetAutoChannel
DESC: Select the channel type depending on the  type of group the player is in.
:DESC
VAR: None
OUT: channel - String for SendChatMessage
--]]
function Track.GetAutoChannel()
	-- Return an appropriate channel in order of preference: /raid, /p, /s
	local channel
	if IsInRaid() then
		channel = "RAID"
	elseif IsInGroup() then
		channel = "PARTY"
	else
		channel = "SAY"
	end
	return channel
end

--[[ local
NAME: Track.AddChat
DESC: Send the message to the channel type depending on the type of group the player is in.
:DESC
VAR: msg - string message to send
OUT: None
--]]
function Track.AddChat(msg)
	if msg ~= nil and msg ~= "" then
		if IsInGroup() or IsInRaid() then
			SendChatMessage(msg, Track.GetAutoChannel())
		else
			DEFAULT_CHAT_FRAME:AddMessage(msg, Track.DB.ColorChat.r, Track.DB.ColorChat.g, Track.DB.ColorChat.b,
				Track.DB.ColorChat.a)
		end
	end
end

--[[ local
NAME: Track.SaveAnchors
DESC: Save where the window is and its size.
:DESC
VAR: None
OUT: None
--]]
function Track.SaveAnchors()
	Track.DB.X = TitanPanelLootTypeMainWindow:GetLeft()
	Track.DB.Y = TitanPanelLootTypeMainWindow:GetTop()
	Track.DB.Width = TitanPanelLootTypeMainWindow:GetWidth()
	Track.DB.Height = TitanPanelLootTypeMainWindow:GetHeight()
end

--[[ local
NAME: Track.ShowWindow
DESC:
- Enable buttons
- Show window
- Update the player list with any rolls
:DESC
VAR: None
OUT: None
--]]
function Track.ShowWindow()
	if IsLead() then
		TitanPanelLootTypeFrameClearButton:Enable()
		TitanPanelLootTypeFrameAnnounceButton:Enable()
		TitanPanelLootTypeFrameNotRolledButton:Enable()
	else
		TitanPanelLootTypeFrameClearButton:Disable()
		TitanPanelLootTypeFrameAnnounceButton:Disable()
		TitanPanelLootTypeFrameNotRolledButton:Disable()
	end
	TitanPanelLootTypeMainWindow:Show()
	Track.UpdateRollList()
end

--[[ local
NAME: Track.ResetWindow
DESC:
- Reset window to default position and size
:DESC
VAR: None
OUT: None
--]]
function Track.ResetWindow()
	TitanPanelLootTypeMainWindow:ClearAllPoints()
	TitanPanelLootTypeMainWindow:SetPoint("Center", UIParent, "Center", 0, 0)
	TitanPanelLootTypeMainWindow:SetWidth(200)
	TitanPanelLootTypeMainWindow:SetHeight(200)
	Track.SaveAnchors()
	Track.ShowWindow()
end

--[[ local
NAME: Track.HideWindow
DESC:
- Hide the main tracker window
:DESC
VAR: None
OUT: None
--]]
function Track.HideWindow()
	TitanPanelLootTypeMainWindow:Hide()
end

--[[ local
NAME: Track.Init
DESC:
- Initialize variables
- Get last position and size of main frame
- Register for needed events
:DESC
VAR: None
OUT: None
--]]
function Track.Init()
	-- Add color
	Track.DB.ColorChat     = { a = 1, r = 1, g = 1, b = 1 }
	Track.DB.ColorNormal   = { a = 1, r = 1, g = 1, b = 1 }
	Track.DB.ColorCheat    = { a = 1, r = 1, g = .8, b = .8 }
	Track.DB.ColorGuild    = { a = 1, r = .2, g = 1, b = .2 }
	Track.DB.ColorInfo     = { a = 1, r = .6, g = .6, b = .6 }
	Track.DB.ColorScroll   = { a = 1, r = .8, g = .8, b = .8 }

	-- For now, hard code some options
	Track.DB.ClearOnStart  = true
	Track.DB.OpenOnStart   = true
	Track.DB.ShowNotRolled = true
	Track.DB.ShowGuildRank = true

	Track.DB.tie_breaker   = 0

	Track.rollArray        = {}
	Track.rollNames        = {}

	Track.allRolled        = false

	--	TitanPanelLootTypeMainWindow:SetMinResize(194,170)
	TitanPanelLootTypeMainWindow:SetResizeBounds(225, 250, 225, 250)
	local x, y, w, h = Track.DB.X, Track.DB.Y, Track.DB.Width, Track.DB.Height
	if not x or not y or not w or not h then
		Track.SaveAnchors()
	else
		TitanPanelLootTypeMainWindow:ClearAllPoints()
		TitanPanelLootTypeMainWindow:SetPoint("TOPLEFT", UIParent, "BOTTOMLEFT", x, y)
		TitanPanelLootTypeMainWindow:SetWidth(w)
		TitanPanelLootTypeMainWindow:SetHeight(h)
	end

	-- using strings from GlobalStrings.lua
	Track.PatternRoll = Tool.CreatePattern(RANDOM_ROLL_RESULT)

	local media = LibStub("LibSharedMedia-3.0")
	local newfont = media:Fetch("font", TitanPanelGetVar("FontName"))
	Tool.Font = newfont
	Tool.FontSize = TitanPanelGetVar("FontSize")

	--	TitanPanelLootTypeMainWindowTitle:SetFont(Tool.Font, Tool.FontSize)
	TitanPanelLootTypeMainWindowTitle:SetText(
		string.format(Track.TxtEscapePicture, Track.IconDice)
		.. " " .. Track.MSGPREFIX
	)
	Tool.SetButtonFont(TitanPanelLootTypeFrameRollButton) -- sets all buttons...

	TitanPanelLootTypeFrameRollButton:SetText(string.format(Track.TxtEscapePicture, Track.IconDice) ..
	L["TITAN_LOOTTYPE_TRACKER_BTNROLL"])
	TitanPanelLootTypeFramePassButton:SetText(string.format(Track.TxtEscapePicture, Track.IconPass) ..
	L["TITAN_LOOTTYPE_TRACKER_BTNPASS"])

	TitanPanelLootTypeFrameAnnounceButton:SetText(L["TITAN_LOOTTYPE_TRACKER_BTNANNOUNCE"])
	TitanPanelLootTypeFrameNotRolledButton:SetText(L["TITAN_LOOTTYPE_TRACKER_BTNNOTROLLED"])
	TitanPanelLootType.ResizeButtons()
	TitanPanelLootTypeFrameClearButton:SetText(L["TITAN_LOOTTYPE_TRACKER_BTNCLEAR"])

	Tool.EnableSize(TitanPanelLootTypeMainWindow, 8, nil, Track.SaveAnchors)
	Tool.EnableMoving(TitanPanelLootTypeMainWindow, Track.SaveAnchors)
	TitanPanelLootTypeFrame:Show()

	local str = ".Init"
		.. " register for events"
	LootDebug(str, "events")
	-- Should be ready for events we are interested in
	TitanPanelLootTypeButton:RegisterEvent("CHAT_MSG_SYSTEM", TitanPanelLootTypeButton_OnEvent)

	TitanPanelLootTypeButton:RegisterEvent("CHAT_MSG_PARTY", TitanPanelLootTypeButton_OnEvent)
	TitanPanelLootTypeButton:RegisterEvent("CHAT_MSG_PARTY_LEADER", TitanPanelLootTypeButton_OnEvent)
	TitanPanelLootTypeButton:RegisterEvent("CHAT_MSG_RAID", TitanPanelLootTypeButton_OnEvent)
	TitanPanelLootTypeButton:RegisterEvent("CHAT_MSG_RAID_LEADER", TitanPanelLootTypeButton_OnEvent)
end

--[[ local
NAME: Track.Close
DESC:
- Stop the
- Get last position and size of main frame
- Register for needed events
:DESC
VAR: None
OUT: None
--]]
function Track.Close()
	Track.DB.RollInProcess = false
	Track.HideWindow()
	if Track.DB.ClearOnClose then
		Track.ClearRolls()
	end
end

--[[ local
NAME: Track.Event_CHAT_MSG_SYSTEM
DESC:
- Monitor system chat for player rolls; add the roll when one is found
:DESC
VAR: arg1 - the message sent
OUT: None
--]]
function Track.Event_CHAT_MSG_SYSTEM(arg1)
	local str = "Event_CHAT_MSG_SYSTEM"
		.. " m'" .. tostring(arg1) .. "'"
	LootDebug(str, "events")

	if Track.DB.RollInProcess then
		for name, roll, low, high in string.gmatch(arg1, Track.PatternRoll) do
			--print(".."..name.." "..roll.." "..low.." "..high)
			Track.AddRoll(name, roll, low, high)
		end
	end
end

--[[ local
NAME: Track.Event_Generic_CHAT_MSG
DESC:
- Monitor general chat for:
- a pass ('pass' or localized version)
- Roll start
- Roll end
:DESC
VAR: msg - the message sent
VAR: name - player that sent the message
OUT: None
--]]
function Track.Event_Generic_CHAT_MSG(msg, name)
	local str = "Event_Generic_CHAT_MSG"
		.. " '" .. tostring(name) .. "'"
		.. " m'" .. tostring(msg) .. "'"
	LootDebug(str, "events")

	-- prevent 'random' rolls from popping windows
	if Track.DB.RollInProcess then
		if msg == L["TITAN_LOOTTYPE_TRACKER_TEXTPASS"] or msg == "pass" then
			name = Tool.Split(name, "-")[1]
			Track.AddRoll(name, "0", "1", "100")
		end
	end

	-- Assume a 'leader' requested a roll so pop the window
	if string.sub(msg, 1, string.len(Track.MSGPREFIX_START)) == Track.MSGPREFIX_START then
		Track.DB.RollInProcess = true
		if Track.DB.ClearOnStart then
			Track.ClearRolls()
		end
		if Track.DB.OpenOnStart then
			Track.ShowWindow()
		end
	end
	-- Check for the 'end' of the roll
	if string.sub(msg, 1, string.len(Track.MSGPREFIX_END)) == Track.MSGPREFIX_END then
		Track.DB.RollInProcess = false
		if Track.DB.ClearOnStart then
			Track.ClearRolls()
		end
	end
	-- Check for the 'close' of the roll
	if string.sub(msg, 1, string.len(Track.MSGPREFIX_CLOSE)) == Track.MSGPREFIX_CLOSE then
		Track.Close()
	end
end

--[[ local
NAME: Track.AddRoll
DESC:
Process the roll the player made
- A pass has a roll of "0"
- Only the first roll is marked as valid / invalid
- The number of rolls is saved (Count)
- A running number (tie_breaker) is added to ensure the first arrived of a tie wins; stored in Place
- Always show the main frame to ensure it pops, no harm if already shown
:DESC
VAR: name - player that sent the roll
VAR: roll - player roll - string
VAR: low - low value player used - string
VAR: high - high player used - string
OUT: None
--]]
function Track.AddRoll(name, roll, low, high)
	local act = "nyl"
	local valid = false

	local valid_ro, valid_bounds, ro, lo, hi = IsRoll(roll, low, high)             -- numeric
	if Track.rollNames[name] == nil then
		Track.DB.tie_breaker = Track.DB.tie_breaker + 1                            -- only count the first roll for each player
		act = "insert"
		Track.rollNames[name] = Track.rollNames[name] and Track.rollNames[name] + 1 or 1 -- mark player as having rolled
		table.insert(Track.rollArray, {
			Name = name,
			Roll = ro,
			Low = lo,
			High = hi,
			Count = Track.rollNames[name],
			Place = Track.DB.tie_breaker,
			Valid_roll = valid_ro,
			Valid_bounds = valid_bounds,
		})
		local str = "AddRoll >>"
			.. " '" .. tostring(act) .. "'"
			.. " '" .. tostring(name) .. "'"
			.. " r'" .. tostring(roll) .. "'"
			.. " l'" .. tostring(low) .. "'"
			.. " h'" .. tostring(high) .. "'"
			.. " #'" .. tostring(Track.rollNames[name]) .. "'"
		LootDebug(str, "players")
	else
		-- check for re-rolls. >1 if rolled before
		act = "update"
		Track.rollNames[name] = Track.rollNames[name] and Track.rollNames[name] + 1 or
		1                                                                          -- mark player as having rolled again
		for i, p in ipairs(Track.rollArray) do
			if p.Name == name then
				-- Only first roll is valid, Ignore additional rolls but count them for display
				p.Count = Track.rollNames[name]
				local str = "AddRoll >>"
					.. " '" .. tostring(act) .. "'"
					.. " '" .. tostring(name) .. "'"
					.. " r'" .. tostring(roll) .. "'"
					.. " l*'" .. tostring(low) .. "'"
					.. " h*'" .. tostring(high) .. "'"
					.. " #*'" .. tostring(Track.rollNames[name]) .. "'"
				LootDebug(str, "players")
			end
		end
	end
	Track.ShowWindow()
end

--[[ local
NAME: Track.SortRolls
DESC:
Sort ascending by name then place
:DESC
VAR: a - player a roll info
VAR: b - player b roll info
OUT: true / false
--]]
function Track.SortRolls(a, b)
	--	return a.Roll < b.Roll
	if a.Roll ~= b.Roll then
		return a.Roll < b.Roll
	elseif a.Roll == b.Roll then
		return a.Place > b.Place
	end
end

--[[ local
NAME: Track.SortRollsRev
DESC:
Sort descending by name then place
:DESC
VAR: a - player a roll info
VAR: b - player b roll info
OUT: true / false
--]]
function Track.SortRollsRev(a, b)
	--	return a.Roll > b.Roll
	if a.Roll ~= b.Roll then
		return a.Roll > b.Roll
	elseif a.Roll == b.Roll then
		return a.Place < b.Place
	end
end

--[[ local
NAME: Track.FormatRollText
DESC:
Format the given roll for display
:DESC
VAR: roll - numeric
VAR: party - player list
VAR: partyName - given player list entry
OUT: String to use
--]]
function Track.FormatRollText(roll, party, partyName)
	local colorTied = Tool.RGBtoEscape(Track.DB.ColorNormal)
	local colorCheat = ((roll.Low ~= 1 or roll.High ~= 100)
		or (roll.Count > 1)) and Tool.RGBtoEscape(Track.DB.ColorCheat) or colorTied
	local txtRange = (not roll.Valid_bounds) and format(" (%d-%d)", roll.Low, roll.High) or ""

	local colorName
	local iconClass
	local colorRank = Tool.RGBtoEscape(Track.DB.ColorGuild)
	local rank = ""

	if partyName[roll.Name] and partyName[roll.Name].class then
		colorName = "|c" .. RAID_CLASS_COLORS[partyName[roll.Name].class].colorStr
		iconClass = Tool.IconClass[partyName[roll.Name].class]
	end
	if colorName == nil or Track.DB.ColorName == false then colorName = colorCheat end
	if iconClass == nil or Track.DB.ShowClassIcon == false then iconClass = "" end
	if Track.DB.ColorName == false then colorRank = colorCheat end

	if Track.DB.ShowGuildRank and partyName[roll.Name] and partyName[roll.Name].rank then
		rank = " " .. partyName[roll.Name].rank
	end

	local txtCount = roll.Count > 1 and format(" [%d]", roll.Count) or ""

	return "|Hplayer:" .. roll.Name .. "|h" ..
		string.format("%3d", roll.Roll) .. ": " ..
		iconClass .. colorName .. roll.Name .. colorRank .. rank .. "|r " ..
		colorCheat .. txtRange .. "|r " ..
		colorCheat .. txtCount .. "|h" ..
		--			colorCheat..roll.Place.."|h"..
		"\n"
end

--[[ local
NAME: Track.UpdateRollList
DESC:
Create the player list for display including rolls, 'cheats', guild rank (if in same guild)
Rolls are above the line; no rolls yet are below the line
:DESC
VAR: None
OUT: None
--]]
function Track.UpdateRollList()
	local rollText = ""

	local party, partyName = Track.GetPlayerList()

	table.sort(Track.rollArray, Track.SortRolls)

	-- format and print rolls, check for ties
	for i, roll in pairs(Track.rollArray) do
		rollText = Track.FormatRollText(roll, party, partyName) .. rollText
	end

	--if IsInGroup() or IsInRaid() then
	rollText = rollText .. Tool.RGBtoEscape(Track.DB.ColorInfo) .. L["TITAN_LOOTTYPE_TRACKER_TXTLINE"] .. "\n"
	local gtxt = Tool.RGBtoEscape(Track.DB.ColorInfo)
	local missClasses = {}
	Track.allRolled = true
	for i, p in ipairs(party) do
		if Track.rollNames[p.name] == nil or Track.rollNames[p.name] == 0 then
			local iconClass = Tool.IconClass[partyName[p.name].class]
			local rank = ""
			if iconClass == nil or Track.DB.ShowClassIcon == false then
				iconClass = ""
			else
				missClasses[partyName[p.name].class] = missClasses[partyName[p.name].class] and
				missClasses[partyName[p.name].class] + 1 or 1
			end
			if Track.DB.ShowGuildRank and partyName[p.name] and partyName[p.name].rank then
				rank = " " .. partyName[p.name].rank
			end
			gtxt = gtxt .. "|Hplayer:" .. p.name .. "|h" .. iconClass .. p.name .. rank .. "|h\n"
			Track.allRolled = false
		end
	end

	local ctxt = ""
	if IsInRaid() then
		local isHorde = (UnitFactionGroup("player")) == "Horde"
		for i, class in pairs(Tool.Classes) do
			--for class,count in pairs(missClasses) do
			if not (isHorde and class == "PALADIN") and not (not isHorde and class == "SHAMAN") then
				ctxt = ctxt .. Tool.IconClass[class] .. (missClasses[class] or 0) .. " "
			end
		end
		if ctxt ~= "" then ctxt = ctxt .. "\n" .. L["TxtLine"] .. "\n" end
	end
	if LT.Debug.on then --
		TitanPluginDebug("UpdateRollList"
			.. " '" .. (rollText or "nyl") .. "'"
			.. " '" .. (ctxt or "nyl") .. "'"
			.. " '" .. (gtxt or "nyl") .. "'"
		)
	end

	rollText = rollText .. ctxt .. gtxt
	LootDebug(rollText, "rolls")

	--end

	--	RollTrackerRollText:SetFont(Tool.Font, Tool.FontSize)
	RollTrackerRollText:SetText(rollText)

	--	TitanPanelLootTypeFrameStatusText:SetFont(Tool.Font, Tool.FontSize)
	TitanPanelLootTypeFrameStatusText:SetText(string.format(L["TITAN_LOOTTYPE_TRACKER_MSGNBROLLS"],
		table.getn(Track.rollArray)))

	--	TitanPanelLootTypeFrameClearButton:SetFont(Tool.Font, Tool.FontSize)
	TitanPanelLootTypeFrameClearButton:SetText(L["TITAN_LOOTTYPE_TRACKER_BTNCLEAR"])
end

--[[ local
NAME: Track.ClearRolls
DESC:
Clear the player list of any rolls rolls
:DESC
VAR: None
OUT: None
--]]
function Track.ClearRolls()
	if #Track.rollArray > 0 then
		Track.rollArray = {}
		Track.rollNames = {}
	end

	Track.DB.tie_breaker = 0
	Track.UpdateRollList()
end

--[[ local
NAME: Track.NotRolled
DESC:
- Send a nag message via whisper to players who have not rolled
- Send a message to group that reminders were sent
- Only an IsLead can do this!
:DESC
VAR: None
OUT: None
--]]
function Track.NotRolled()
	if IsLead() then
		local party, partyName = Track.GetPlayerList()
		local names = ""

		local group = ""
		if IsInRaid() then
			group = L["TITAN_LOOTTYPE_TRACKER_RAIDPASS"]
		elseif IsInGroup() then
			group = L["TITAN_LOOTTYPE_TRACKER_PARTYPASS"]
		else
			group = ""
		end

		for i, p in ipairs(party) do
			if Track.rollNames[p.name] == nil or Track.rollNames[p.name] == 0 then
				SendChatMessage(Track.MSGPREFIX .. L["TITAN_LOOTTYPE_TRACKER_NOTROLLEDNAG"] .. group, WHISPER, nil,
					p.name)
				names = "send"
			end
		end

		if names == "send" then
			Track.AddChat(Track.MSGPREFIX .. L["TITAN_LOOTTYPE_TRACKER_MSGNOTROLLED"])
		end
	end
end

--[[ local
NAME: Track.StartRoll
DESC:
- Pop the main frame
- Clear all rolls for a new set
- Start a new roll process
- Send a message to the group of a new roll
- Only an IsLead can do this!
:DESC
VAR: None
OUT: None
--]]
function Track.StartRoll()
	Track.ShowWindow()
	Track.ClearRolls()
	Track.DB.RollInProcess = true
	Track.AddChat(Track.MSGPREFIX_START ..
	"{circle} " .. string.format(L["TITAN_LOOTTYPE_TRACKER_MSGSTART"], L["TITAN_LOOTTYPE_TRACKER_TEXTPASS"]))
	Track.AddChat(L["TITAN_LOOTTYPE_TRACKER_MSGBAR"])
end

--[[ local
NAME: Track.RollAnnounce
DESC:
- Send a message to the group of the winner
- Stop the roll process so new rolls are not processed
- Only an IsLead can do this!
:DESC
VAR: None
OUT: None
--]]
function Track.RollAnnounce()
	local winName = ""
	local winRoll = 0
	local addPrefix = ""
	local msg = ""
	local list = {}

	table.sort(Track.rollArray, Track.SortRollsRev)

	for i, roll in pairs(Track.rollArray) do
		local str = "AddRoll >>"
			.. " '" .. roll.Name .. "'"
			.. " r'" .. tostring(roll.Roll) .. "'"
		LootDebug(str, "players")
		if roll.Valid_roll then
			winName = roll.Name
			winRoll = roll.Roll
			break -- sort breaks ties, grab first one
		end
	end

	if winName == "" then
		msg = Track.MSGPREFIX_END .. addPrefix .. "{circle} No winners"
	else
		msg = Track.MSGPREFIX_END ..
		addPrefix .. "{circle} " .. string.format(L["TITAN_LOOTTYPE_TRACKER_MSGANNOUNCE"], winName, winRoll)
	end

	Track.AddChat(msg)

	Track.DB.RollInProcess = false
end

--[[ TitanPanelLootType
NAME: TitanPanelLootType.ResizeButtons
DESC: Determine the size of the buttons so they fill the line on the main frame.
VAR: None
OUT: None
--]]
function TitanPanelLootType.ResizeButtons()
	local w = TitanPanelLootTypeFrameHelperButton:GetWidth()
	TitanPanelLootTypeFrameRollButton:SetWidth(w / 2)
	TitanPanelLootTypeFramePassButton:SetWidth(w / 2)

	if Track.DB.ShowNotRolled then
		TitanPanelLootTypeFrameAnnounceButton:SetWidth(w / 3)
		TitanPanelLootTypeFrameClearButton:SetWidth(w / 3)
		TitanPanelLootTypeFrameNotRolledButton:Show()
	else
		TitanPanelLootTypeFrameAnnounceButton:SetWidth(w / 2)
		TitanPanelLootTypeFrameClearButton:SetWidth(w / 2)
		TitanPanelLootTypeFrameNotRolledButton:Hide()
	end
end

--[[ TitanPanelLootType
NAME: TitanPanelLootType.BtnClose
DESC: On click of the main frame 'X':
Close the main frame; clear rolls; end current roll process
VAR: None
OUT: None
--]]
function TitanPanelLootType.BtnClose()
	Track.Close()

	if IsLead() then
		Track.AddChat(Track.MSGPREFIX_CLOSE .. L["TITAN_LOOTTYPE_TRACKER_MSGCLOSING"])
	end
end

--[[ TitanPanelLootType
NAME: TitanPanelLootType.BtnRoll
DESC: On click of the main frame Roll: Send the roll to be processed
VAR: None
OUT: None
--]]
function TitanPanelLootType.BtnRoll()
	RandomRoll(1, 100)
end

--[[ TitanPanelLootType
NAME: TitanPanelLootType.BtnPass
DESC: On click of the main frame Pass: Send the pass to be processed
VAR: None
OUT: None
--]]
function TitanPanelLootType.BtnPass()
	Track.AddChat(L["TITAN_LOOTTYPE_TRACKER_TEXTPASS"])
	--[[
	Note: When in solo debug mode, this just does a /say pass
	which is NOT picked up by an event. This only monitors party & raid chat...
	--]]
end

--[[ TitanPanelLootType
NAME: TitanPanelLootType.BtnClearRolls
DESC: On click of the main frame Clear:
- Clear all rolls
- Start a new roll process
- Only an IsLead can do this!
  This handled by enable / disable of buttons when the main frame is created.
:DESC
VAR: None
OUT: None
--]]
function TitanPanelLootType.BtnClearRolls()
	if #Track.rollArray > 0 then
		Track.ClearRolls()
		if Track.DB.CloseOnClear then
			Track.HideWindow()
		end
	end
	Track.StartRoll()
end

--[[ TitanPanelLootType
NAME: TitanPanelLootType.BtnAnnounce
DESC: On click of the main frame "start":
- Start a new roll process
- Only an IsLead can do this!
  This handled by enable / disable of buttons when the main frame is created.
:DESC
VAR: None
OUT: None
--]]
function TitanPanelLootType.BtnAnnounce(button)
	Track.RollAnnounce()
	if Track.DB.ClearOnAnnounce then
		Track.ClearRolls()
	end
	if Track.DB.CloseOnAnnounce then
		Track.HideWindow()
	end
end

--[[ TitanPanelLootType
NAME: TitanPanelLootType.BtnNotRolled
DESC: On click of the main frame "remind":
- Call the routine to nag those who have not rolled
- Only an IsLead can do this!
  This handled by enable / disable of buttons when the main frame is created.
:DESC
VAR: None
OUT: None
--]]
function TitanPanelLootType.BtnNotRolled()
	Track.NotRolled()
end

-- Debug!!!
function TitanPanelLootType.AddAllRollsDebug() -- rolls for all players
	local party, partyName = Track.GetPlayerList()

	-- walk the player list, adding rolls
	for i, p in ipairs(party) do
		-- 0 (zero) allows a 'pass'
		Track.AddRoll(p.name, tostring(random(0, 100)), "1", "100")
	end
	-- Now show the results
	Track.UpdateRollList()
end

function TitanPanelLootType.AddRollDebug(...) -- single roll
	Track.AddRoll(...)
end

--[[ TitanPanelLootType
NAME: TitanPanelLootTypeButton_OnLoad
DESC: On load of this addon:
- Get Titan registry values
- Get addon saved variable values
:DESC
VAR: None
OUT: None
--]]
function TitanPanelLootTypeButton_OnLoad(self)
	self.registry = {
		id = TITAN_LOOTTYPE_ID,
		category = "Built-ins",
		version = TITAN_VERSION,
		menuText = L["TITAN_LOOTTYPE_MENU_TEXT"],
		buttonTextFunction = "TitanPanelLootTypeButton_GetButtonText",
		tooltipTitle = L["TITAN_LOOTTYPE_TOOLTIP"],
		tooltipTextFunction = "TitanPanelLootTypeButton_GetTooltipText",
		icon = "Interface\\AddOns\\TitanLootType\\TitanLootType",
		iconWidth = 16,
		controlVariables = {
			ShowIcon = true,
			ShowLabelText = true,
			ShowRegularText = false,
			ShowColoredText = false,
			DisplayOnRightSide = true,
		},
		savedVariables = {
			ShowIcon = 1,
			ShowLabelText = 1,
			RandomRoll = 100,
			DB = {},
			DisplayOnRightSide = false,
			--			ShowDungeonDiff = false,
			--			DungeonDiffType = "AUTO",
		}
	};

	self:RegisterEvent("PLAYER_ENTERING_WORLD", TitanPanelLootTypeButton_OnEvent)
	self:RegisterEvent("GROUP_ROSTER_UPDATE");
	self:RegisterEvent("RAID_ROSTER_UPDATE");
	self:RegisterEvent("PARTY_LOOT_METHOD_CHANGED");
	self:RegisterEvent("CHAT_MSG_SYSTEM");
end

--[[ TitanPanelLootType
NAME: TitanPanelLootTypeButton_OnEvent
DESC: Parse events registered to plugin and act on them
VAR: None
OUT: None
--]]
function TitanPanelLootTypeButton_OnEvent(self, event, ...)
	LootDebug(event, "events") -- could generate a lot of messages...

	if (event == "CHAT_MSG_SYSTEM") then
		Track.Event_CHAT_MSG_SYSTEM(...)
	end

	if (event == "CHAT_MSG_PARTY") then
		Track.Event_Generic_CHAT_MSG(...)
	end

	if (event == "CHAT_MSG_PARTY_LEADER") then
		Track.Event_Generic_CHAT_MSG(...)
	end

	if (event == "CHAT_MSG_RAID") then
		Track.Event_Generic_CHAT_MSG(...)
	end

	if (event == "CHAT_MSG_RAID_LEADER") then
		Track.Event_Generic_CHAT_MSG(...)
	end

	if (event == "PLAYER_ENTERING_WORLD") then
		Track.Init()
	end

	TitanPanelPluginHandle_OnUpdate(updateTable)
end

--[[ TitanPanelLootType
NAME: TitanPanelLootTypeButton_GetButtonText
DESC: Determine loot type and then display on button
VAR: None
OUT: None
--]]
function TitanPanelLootTypeButton_GetButtonText(id)
	local lootTypeText, lootThreshold, color

	--	if (GetNumSubgroupMembers() > 0) or (GetNumGroupMembers() > 0) then

	if IsInRaid() or IsInGroup() then
		lootTypeText = TitanLootMethod[GetLootMethod()].text;
		lootThreshold = GetLootThreshold();
		color = _G["ITEM_QUALITY_COLORS"][lootThreshold];
	else
		lootTypeText = _G["SOLO"];
		color = _G["GRAY_FONT_COLOR"];
	end
	return L["TITAN_LOOTTYPE_BUTTON_LABEL"], TitanUtils_GetColoredText(lootTypeText, color);
end

--[[ TitanPanelLootType
NAME: TitanPanelLootTypeButton_GetTooltipText
DESC:
Prepare the tool tip text. The tool tip is determined by whether the player is in a group or not.
:DESC
VAR: None
OUT: None
--]]
function TitanPanelLootTypeButton_GetTooltipText()
	--	if (GetNumSubgroupMembers() > 0) or (GetNumGroupMembers() > 0) then
	if IsInRaid() or IsInGroup() then
		local lootTypeText = TitanLootMethod[GetLootMethod()].text;
		local lootThreshold = GetLootThreshold();
		local itemQualityDesc = _G["ITEM_QUALITY" .. lootThreshold .. "_DESC"];
		local color = _G["ITEM_QUALITY_COLORS"][lootThreshold];
		return "" ..
			_G["LOOT_METHOD"] .. ": \t" .. TitanUtils_GetHighlightText(lootTypeText) .. "\n" ..
			_G["LOOT_THRESHOLD"] .. ": \t" .. TitanUtils_GetColoredText(itemQualityDesc, color) .. "\n" ..
			TitanUtils_GetGreenText(L["TITAN_LOOTTYPE_TOOLTIP_HINT1"]) .. "\n"
		--			..TitanUtils_GetGreenText(L["TITAN_LOOTTYPE_TOOLTIP_HINT2"])
	else
		return --TitanUtils_GetNormalText(_G["ERR_NOT_IN_GROUP"]).."\n"..
			TitanUtils_GetGreenText(L["TITAN_LOOTTYPE_TOOLTIP_HINT1"]) .. "\n"
		--			TitanUtils_GetGreenText(L["TITAN_LOOTTYPE_TOOLTIP_HINT2"]);
	end
end

--[[ TitanPanelLootType
NAME: TitanPanelRightClickMenu_PrepareLootTypeMenu
DESC:
Display rightclick menu options.
:DESC
VAR: None
OUT: None
--]]
function TitanPanelRightClickMenu_PrepareLootTypeMenu()
	local info = {};

	TitanPanelRightClickMenu_AddTitle(TitanPlugins[TITAN_LOOTTYPE_ID].menuText);
	info = {};
	info.notCheckable = true

	TitanPanelRightClickMenu_AddToggleIcon(TITAN_LOOTTYPE_ID);
	TitanPanelRightClickMenu_AddToggleLabelText(TITAN_LOOTTYPE_ID);
	TitanPanelRightClickMenu_AddToggleRightSide(TITAN_LOOTTYPE_ID);
	TitanPanelRightClickMenu_AddSpacer();
	TitanPanelRightClickMenu_AddCommand(L["TITAN_PANEL_MENU_HIDE"], TITAN_LOOTTYPE_ID, TITAN_PANEL_MENU_FUNC_HIDE);
end

--[[ TitanPanelLootType
NAME: TitanPanelLootTypeButton_OnClick
DESC:
On left click:
If isLead then start a new roll
If not isLead then do a roll

:DESC
VAR: None
OUT: None
--]]
function TitanPanelLootTypeButton_OnClick(self, button)
	if button == "LeftButton" then
		if IsLead() then
			TitanPanelLootType.BtnClearRolls()
		else
			RandomRoll(1, 100)
		end
	end
end