-- Addon settings
local db

-- Texture settings
local textures ={
				['Star'] = "GroupLeader/Textures/star_",
				['Plus'] = "GroupLeader/Textures/plus_",
				['Circle'] = "GroupLeader/Textures/circle_",
				}
local colors =	{
				['White'] = "w.dds",
				['Black'] = "bl.dds",
				['Red'] = "r.dds",
				['Green'] = "g.dds",
				['Blue'] = "b.dds",
				}
local textureKeys = GetTableKeys(textures)
local colorKeys = GetTableKeys(colors)
local mapPinType = "GroupLeaderMap"
local compassPinType = "GroupLeaderCompass"

-- Defaults for settings
local defaults = {
    compassEnabled = true,
    mapEnabled = true,
	leaderEnabled = true,
	leaderSymbol = 'Star',
    leaderColor = 'White',
	compassMaxDistance = 1.0,
}

-- Addon data storage
local nameToUnitTagMap = {}
local playerName = nil
local trackers =	{
					['leader'] = {tag=nil, name=nil, symbol='Circle', color='Red', X=0, Y=0},
					}

-- Misc functions
-- Map group member names to unit tags in a table
local function MapNamesToUnitTags()
	nameToUnitTagMap = {}
	size = GetGroupSize()
    if size > 0 then
        for i=1, size, 1 do
            local unitTag = GetGroupUnitTagByIndex(i)
            if unitTag then
                local name = GetUnitName(unitTag)
                if name then
                    local lowername = string.lower(name)
                    nameToUnitTagMap[lowername]=unitTag
                end
            end
        end
    end
end

-- Tooltip creator
local pinTooltipCreator = {
	creator = function(pin)
		--TODO
		InformationTooltip:AddLine("Tracker")
	end,
	tooltip = InformationTooltip,
}

-- Compass and map callbacks
local function MapCallback(pinManager)
	if not db or not db.mapEnabled then
		return
	end
	for k, v in pairs(trackers) do
		if v.tag and v.name and v.symbol and v.color and not (v.X == 0 and v.Y == 0) then
			if k == 'leader' and db.leaderEnabled and v.name ~= playerName then
				pinManager:CreatePin(_G[mapPinType .. v.name], v.name, v.X, v.Y)
			else
				pinManager:CreatePin(_G[mapPinType .. v.name], v.name, v.X, v.Y)
			end
		end
	end
end

local function CompassPinCallback(pinManager)
	if COMPASS.container:IsHidden() then
		return
	end
	if not db or not db.compassEnabled then
		return
	end
	for k, v in pairs(trackers) do
		if v.tag and v.name and v.symbol and v.color and not (v.X == 0 and v.Y == 0) then
			if k == 'leader' and db.leaderEnabled and v.name ~= playerName then
				pinManager:CreatePin(compassPinType .. v.name, v.name, v.X, v.Y)
			else
				pinManager:CreatePin(compassPinType .. v.name, v.name, v.X, v.Y)
			end
		end
	end
end

-- Map specific functions
local function RefreshMapPins(pinType)
	ZO_WorldMap_RefreshCustomPinsOfType(_G[pinType])
end

local function AddMapPin(pinType, pinTypeAddCallback, pinTypeOnResizeCallback, pinLayoutData, pinTooltipCreator)
	ZO_WorldMap_AddCustomPin(pinType, pinTypeAddCallback, pinTypeOnResizeCallback, pinLayoutData, pinTooltipCreator)
	ZO_WorldMap_SetCustomPinEnabled(_G[pinType], true)
	RefreshMapPins(pinType)
end

-- Slash command handlers
local function CommandHandler(text)
	-- Display help
	if text == nil or string.len(text) <= 0 or string.lower(text) == "help" then
		ChatMessage("GroupLeader Help:")
		ChatMessage("/gl : display help.")
		ChatMessage("    aliases: /groupleader, /gl help")
		ChatMessage("/gl leader : toggle leader tracking")
		ChatMessage("/gl map : toggle map tracking")
		ChatMessage("/gl compass : toggle compass tracking")
		ChatMessage("/gl add [name] -symbol -color : add a custom tracker")
		ChatMessage("    -symbol and -color are optional")
		ChatMessage("    e.g., /gl add adein -circle -red")
		ChatMessage("/gl del [name] : delete a custom tracker")
		ChatMessage("/gl clear : clear all custom trackers")
	elseif text ~= nil and string.len(text) > 0 then
		MapNamesToUnitTags()
		local input = string.lower(text)
		local params = SplitString(input)
		local paramCount = table.getn(params)
		if params[1] == "leader" then
			db.leaderEnabled = not db.leaderEnabled
			local status = nil
			if db.leaderEnabled then
				status = "enabled"
			else
				status = "disabled"
			end
			ChatMessage("Tracking group leader " .. status)
		elseif params[1] == "map" then
			db.mapEnabled = not db.mapEnabled
			local status = nil
			if db.mapEnabled then
				status = "enabled"
			else
				status = "disabled"
			end
			ChatMessage("Map tracking " .. status)
		elseif params[1] == "compass" then
			db.compassEnabled = not db.compassEnabled
			local status = nil
			if db.compassEnabled then
				status = "enabled"
			else
				status = "disabled"
			end
			ChatMessage("Compass tracking " .. status)
		elseif params[1] == "add" then
			local name = nil
			local symbol = 'Circle'
			local color = 'Red'
			for paramCounter = 2, paramCount, 1 do
				param = params[paramCounter]
				if string.sub(param, 1, string.len("-"))=="-" then
					local paramNoDash = string.sub(param, 2, -1)
					if not symbol then
						for tex in textureKeys do
							if paramNoDash == tex then
								symbol = tex
								break
							end
						end
					end
					if not color then
						for col in colorKeys do
							if paramNoDash == col then
								color = col
								break
							end
						end
					end
				else
					if not name then
						name = param
					else
						name = name .. " " .. param
					end
				end
			end
			local unitTag = nameToUnitTagMap[name]
			if unitTag then
				for k, v in pairs(trackers) do
					if v.tag == unitTag then
						ChatMessage("Already tracking that player!")
						return
					end
				end
				local mapPinLayout = {
					level = 200,
					texture = textures[symbol] .. colors[color],
					size = 40,
				}
				local compassPinLayout = {
					maxDistance = db.compassMaxDistance,
					texture =  textures[symbol] .. colors[color],
				}
				AddMapPin(mapPinType .. name, MapCallback, nil, mapPinLayout, pinTooltipCreator)
				COMPASS_PINS:AddCustomPin(compassPinType .. name, CompassPinCallback, compassPinLayout)
				table.insert(trackers, {tag=unitTag, name=name, symbol=symbol, color=color, X=0, Y=0})
	            ChatMessage("Now tracking " .. name)
			else
	            ChatMessage("Player not found in group!")
			end
		elseif params[1] == "del" then
			local name = nil
			for paramCounter = 2, paramCounter, 1 do
				param = params[paramCounter]
				if not name then
					name = param
				else
					name = name .. " " .. param
				end
			end
			deleted = false
			for k, v in pairs(trackers) do
				if v.name == name then
					table.remove(trackers, k)
					deleted = true
				end
			end
			-- TODO: delete the map/compass pins if possible (and other places where tracking removed)
			if deleted then
				ChatMessage("Deleted " .. name .. " from tracking list")
			else
				ChatMessage("Player not found in tracking list!")
			end
		elseif params[1] == "clear" then
			ChatMessage("Clearing custom trackers")
			local trackerCount = table.getn(trackers) - 1
			for trackerCounter = 1, trackerCount, 1 do
				table.remove(trackerCounter)
			end
		else
			ChatMessage("Unknown GroupLeader command!")
		end
	end
end

-- Event handlers
-- Called when the group is disbanded
local function Disbanded()
	trackers =	{
				['leader'] = {tag=nil, name=nil, symbol=nil, color=nil, X=0, Y=0},
				}
end

-- Called when the group leader changes
local function LeaderUpdate()
	trackers['leader'].tag = GetGroupLeaderUnitTag()
end

-- Called when someone joins the group
local function MemberJoined()
    LeaderUpdate()
end

-- Called when someone leaves the group
local function MemberLeft()
    LeaderUpdate()
    MapNamesToUnitTags()
    for k, v in pairs(trackers) do
    	local newUnitTag = nameToUnitTagMap[v.name]
		if newUnitTag then
			trackers[k].tag = newUnitTag
		else
            table.remove(trackers, k)
        end
    end
end

-- Called when someone in the group connects/disconnects
local function ConnectedStatus()
    LeaderUpdate()
end

-- Refresh the compass and map with the chosen texture
local function RefreshAllPins()
    for k, v in pairs(trackers) do
    	if v.name then
			RefreshMapPins(mapPinType .. v.name)
		end
	end
    COMPASS_PINS:RefreshPins()
end

-- Called on game UI updates
function OnGroupLeaderUpdate(time)
	for k, v in pairs(trackers) do
		if v.tag then
			v.X, v.Y = GetMapPlayerPosition(v.tag)
			v.name = GetUnitName(v.tag)
	    else
	        v.X = nil
	        v.Y = nil
	        v.name = nil
		end
	end

    RefreshAllPins()
end

-- Create the settings menu
local function CreateSettings()
	local LAM = LibStub("LibAddonMenu-1.0")
	local panel = LAM:CreateControlPanel("GROUP_LEADER_SETTINGS", "Group Leader")
	LAM:AddHeader(panel, "GROUP_LEADER_SETTINGS_GENERAL", "General Settings")
	LAM:AddCheckbox(panel, "Group_Leader_Map_Enabled", "Enable Map Tracking", "If enabled, the location of the group leader (or custom targets) will be tracked on the map",
				function() return db.mapEnabled end,	--getFunc
				function()							--setFunc
					db.mapEnabled = not db.mapEnabled
				end)
	LAM:AddCheckbox(panel, "Group_Leader_Compass_Enabled", "Enable Compass Tracking", "If enabled, the location of the group leader (or custom targets) will be tracked on the compass",
				function() return db.compassEnabled end,	--getFunc
				function()							--setFunc
					db.compassEnabled = not db.compassEnabled
				end)
	LAM:AddSlider(panel, "Group_Leader_Compass_MaxDistance", "Max Distance", "The maximum distance (in normalized map units) to show the tracked players", 1, 100, 1,
		function() return db.compassMaxDistance * 100 end,
		function(maxDistance)
			db.compassMaxDistance = maxDistance / 100
			COMPASS_PINS.pinLayouts[compassPinType].maxDistance = maxDistance / 100
			COMPASS_PINS:RefreshPins()
		end)

	LAM:AddHeader(panel, "GROUP_LEADER_SETTINGS_LEADER", "Leader Settings")
	LAM:AddCheckbox(panel, "Group_Leader_Tracking_Enabled", "Enable Tracking", "If enabled, the location of the group leader will be tracked",
				function() return db.leaderEnabled end,	--getFunc
				function()							--setFunc
					db.leaderEnabled = not db.leaderEnabled
				end)
	LAM:AddDropdown(panel, "Group_Leader_Symbol", "Select Tracking Symbol", "Select the symbol to use for tracking the leader",
				textureKeys,
                function() return db.leaderSymbol end,	--getFunc
				function(text)							--setFunc
					db.leaderSymbol = text
					RefreshAllPins()
				end,	--setFunc
				false,
				nil)
	LAM:AddDropdown(panel, "Group_Leader_Color", "Select Tracking Color", "Select the color to use for the symbol used for tracking the leader",
				colorKeys,
                function() return db.leaderColor end,	--getFunc
				function(text)							--setFunc
					db.leaderColor = text
					RefreshAllPins()
				end,	--setFunc
				false,
				nil)
end

-- AddOn setup
local function AddonSetup(eventCode, addOnName)
	if addOnName ~= "GroupLeader" then
		return
	end

	nameToUnitTagMap = {}
	playerName = nil
	trackers =	{
				['leader'] = {tag=nil, name=nil, symbol='Circle', color='Red', X=0, Y=0},
				}

	-- Populate the settings DB
	Group_LeaderDB = Group_LeaderDB or {}
	for k,v in pairs(defaults) do
	    if type(Group_LeaderDB[k]) == "nil" then
	        Group_LeaderDB[k] = v
	    end
	end
	db = Group_LeaderDB

    playerName = GetUnitName('player')
	LeaderUpdate()

	trackers['leader'].symbol = db.leaderSymbol
	trackers['leader'].color = db.leaderColor

	local mapPinLayout = {
		level = 200,
		texture = textures[db.leaderSymbol] .. colors[db.leaderColor],
		size = 40,
	}
	local compassPinLayout = {
		maxDistance = db.compassMaxDistance,
		texture =  textures[db.leaderSymbol] .. colors[db.leaderColor],
	}
	AddMapPin(mapPinType .. 'leader', MapCallback, nil, mapPinLayout, pinTooltipCreator)
	COMPASS_PINS:AddCustomPin(compassPinType .. 'leader', CompassPinCallback, compassPinLayout)

	SLASH_COMMANDS["/groupleader"] = CommandHandler
	SLASH_COMMANDS["/gl"] = CommandHandler

    EVENT_MANAGER:RegisterForEvent("GroupLeaderDisbanded", EVENT_GROUP_DISBANDED, Disbanded)
    EVENT_MANAGER:RegisterForEvent("GroupLeaderLeaderUpdate", EVENT_LEADER_UPDATE, LeaderUpdate)
    EVENT_MANAGER:RegisterForEvent("GroupLeaderMemberJoined", EVENT_GROUP_MEMBER_JOINED, MemberJoined)
    EVENT_MANAGER:RegisterForEvent("GroupLeaderMemberLeft", EVENT_GROUP_MEMBER_LEFT, MemberLeft)
    EVENT_MANAGER:RegisterForEvent("GroupLeaderMemberConnected", EVENT_GROUP_MEMBER_CONNECTED_STATUS, ConnectedStatus)

	CreateSettings()
end

-- Register for addon loaded event
EVENT_MANAGER:RegisterForEvent("GroupLeader", EVENT_ADD_ON_LOADED, AddonSetup)