local E, L, V, P, G = unpack(ElvUI)
local EFL = E:NewModule("EnhancedFriendsList", "AceHook-3.0")
local EP = LibStub("LibElvUIPlugin-1.0")
local LSM = LibStub("LibSharedMedia-3.0", true)
local addonName = ...

local unpack, pairs, ipairs = unpack, pairs, ipairs
local format = format

local GetFriendInfo = GetFriendInfo
local GetQuestDifficultyColor = GetQuestDifficultyColor
local CLASS_ICON_TCOORDS = CLASS_ICON_TCOORDS
local FRIENDS_BUTTON_TYPE_WOW = FRIENDS_BUTTON_TYPE_WOW
local LOCALIZED_CLASS_NAMES_FEMALE = LOCALIZED_CLASS_NAMES_FEMALE
local LOCALIZED_CLASS_NAMES_MALE = LOCALIZED_CLASS_NAMES_MALE
local RAID_CLASS_COLORS = RAID_CLASS_COLORS

local StatusIcons = {
	Default = {
		Online = FRIENDS_TEXTURE_ONLINE,
		Offline = FRIENDS_TEXTURE_OFFLINE,
		DND = FRIENDS_TEXTURE_DND,
		AFK = FRIENDS_TEXTURE_AFK
	},
	Square = {
		Online = "Interface\\AddOns\\ElvUI_EnhancedFriendsList\\Media\\Textures\\Square\\Online",
		Offline = "Interface\\AddOns\\ElvUI_EnhancedFriendsList\\Media\\Textures\\Square\\Offline",
		DND	= "Interface\\AddOns\\ElvUI_EnhancedFriendsList\\Media\\Textures\\Square\\DND",
		AFK	= "Interface\\AddOns\\ElvUI_EnhancedFriendsList\\Media\\Textures\\Square\\AFK"
	},
	D3 = {
		Online = "Interface\\AddOns\\ElvUI_EnhancedFriendsList\\Media\\Textures\\D3\\Online",
		Offline = "Interface\\AddOns\\ElvUI_EnhancedFriendsList\\Media\\Textures\\D3\\Offline",
		DND = "Interface\\AddOns\\ElvUI_EnhancedFriendsList\\Media\\Textures\\D3\\DND",
		AFK = "Interface\\AddOns\\ElvUI_EnhancedFriendsList\\Media\\Textures\\D3\\AFK"
	}
}

local function GetLevelDiffColorHex(level, offline)
	if level ~= 0 then
		local color = GetQuestDifficultyColor(level)
		return offline and format("|cFF%02x%02x%02x", color.r*160, color.g*160, color.b*160) or format("|cFF%02x%02x%02x", color.r*255, color.g*255, color.b*255)
	else
		return offline and E:RGBToHex(0.49, 0.52, 0.54) or "|cFFFFFFFF"
	end
end

local localizedTable = {}
for k, v in pairs(LOCALIZED_CLASS_NAMES_MALE) do
	localizedTable[v] = k
end

for k, v in pairs(LOCALIZED_CLASS_NAMES_FEMALE) do
	localizedTable[v] = k
end

local function GetClassColorHex(class, offline)
	class = localizedTable[class]

	local color = CUSTOM_CLASS_COLORS and CUSTOM_CLASS_COLORS[class] or RAID_CLASS_COLORS[class]
	if color then
		return offline and format("|cff%02x%02x%02x", color.r*160, color.g*160, color.b*160) or format("|cff%02x%02x%02x", color.r*255, color.g*255, color.b*255)
	else
		return offline and E:RGBToHex(0.49, 0.52, 0.54) or "|cFFFFFFFF"
	end
end

local function HexToRGB(hex)
	if not hex then return nil end

	local rhex, ghex, bhex = string.sub(hex, 5, 6), string.sub(hex, 7, 8), string.sub(hex, 9, 10)
	return {r = tonumber(rhex, 16)/225, g = tonumber(ghex, 16)/225, b = tonumber(bhex, 16)/225}
end

function EFL:Update()
	for i = 1, #FriendsFrameFriendsScrollFrame.buttons do
		local button = FriendsFrameFriendsScrollFrame.buttons[i]

		self:Configure_Background(button)
		self:Configure_Status(button)
		self:Configure_IconFrame(button)

		button.name:SetFont(LSM:Fetch("font", E.db.enhanceFriendsList.nameFont), E.db.enhanceFriendsList.nameFontSize, E.db.enhanceFriendsList.nameFontOutline)
		button.info:SetFont(LSM:Fetch("font", E.db.enhanceFriendsList.zoneFont), E.db.enhanceFriendsList.zoneFontSize, E.db.enhanceFriendsList.zoneFontOutline)
	end
end

-- Status
function EFL:Update_Status(button)
	if not E.db.enhanceFriendsList.showStatusIcon then return end

	if button.TYPE == "Online" then
		button.status:SetTexture(StatusIcons[E.db.enhanceFriendsList.statusIcons][(button.statusType == CHAT_FLAG_DND and "DND" or button.statusType == CHAT_FLAG_AFK and "AFK" or "Online")])
	else
		button.status:SetTexture(StatusIcons[E.db.enhanceFriendsList.statusIcons].Offline)
	end
end

function EFL:Configure_Status(button)
	if E.db.enhanceFriendsList.showStatusIcon then
		button.status:Show()
	else
		button.status:Hide()
	end
end

-- Name
function EFL:Update_Name(button)
	local isOffline = button.TYPE == "Offline" or false

	if not button.TYPE or not self.db[button.TYPE] then
		button.TYPE = "Offline" -- bandaid
	end

	local enhancedName = (self.db[button.TYPE].enhancedName and GetClassColorHex(button.class, isOffline)..button.nameText.."|r" or button.nameText)
	local enhancedLevel = self.db[button.TYPE].level and button.levelText and format(self.db[button.TYPE].levelText and (self.db[button.TYPE].shortLevel and L["SHORT_LEVEL_TEMPLATE"] or L["LEVEL_TEMPLATE"]) or "%s", self.db[button.TYPE].levelColor and GetLevelDiffColorHex(button.levelText, isOffline)..button.levelText.."|r" or button.levelText).." " or ""
	local enhancedClass = self.db[button.TYPE].classText and button.class or ""
	button.name:SetText(enhancedName..((enhancedLevel ~= "" or enhancedClass ~= "") and (self.db[button.TYPE].enhancedName and " - " or ", ") or "")..enhancedLevel..enhancedClass)

	local nameColor = self.db[button.TYPE].enhancedName and (self.db[button.TYPE].colorizeNameOnly and (isOffline and FRIENDS_GRAY_COLOR or HIGHLIGHT_FONT_COLOR) or HexToRGB(GetClassColorHex(button.class, isOffline))) or (isOffline and FRIENDS_GRAY_COLOR or FRIENDS_WOW_NAME_COLOR)
	button.name:SetTextColor(nameColor.r, nameColor.g, nameColor.b)

	local infoText
	if isOffline then
		if button.lastSeen then
			infoText = (self.db[button.TYPE].zoneText and button.area and button.area..(self.db[button.TYPE].lastSeen and " - " or "") or "")..(self.db[button.TYPE].lastSeen and L["Last seen"].." "..FriendsFrame_GetLastOnline(button.lastSeen) or "")
		else
			infoText = self.db[button.TYPE].zoneText and button.area or ""
		end

		button.info:SetTextColor(0.49, 0.52, 0.54)
	else
		infoText = self.db[button.TYPE].zoneText and button.area or ""

		local playerZone = GetRealZoneText()
		if self.db[button.TYPE].enhancedZone then
			if self.db[button.TYPE].sameZone then
				if infoText == playerZone then
					button.info:SetTextColor(self.db[button.TYPE].sameZoneColor.r, self.db[button.TYPE].sameZoneColor.g, self.db[button.TYPE].sameZoneColor.b)
				else
					button.info:SetTextColor(self.db[button.TYPE].enhancedZoneColor.r, self.db[button.TYPE].enhancedZoneColor.g, self.db[button.TYPE].enhancedZoneColor.b)
				end
			else
				button.info:SetTextColor(self.db[button.TYPE].enhancedZoneColor.r, self.db[button.TYPE].enhancedZoneColor.g, self.db[button.TYPE].enhancedZoneColor.b)
			end
		else
			if self.db[button.TYPE].sameZone then
				if infoText == playerZone then
					button.info:SetTextColor(self.db[button.TYPE].sameZoneColor.r, self.db[button.TYPE].sameZoneColor.g, self.db[button.TYPE].sameZoneColor.b)
				else
					button.info:SetTextColor(0.49, 0.52, 0.54)
				end
			else
				button.info:SetTextColor(0.49, 0.52, 0.54)
			end
		end
	end
	button.info:SetText(infoText)

	button.name:ClearAllPoints()
	if button.iconFrame:IsShown() then
		button.name:Point("LEFT", button.iconFrame, "RIGHT", 3, infoText ~= "" and 7 or 0)
	else
		if E.db.enhanceFriendsList.showStatusIcon then
			button.name:Point("TOPLEFT", 22, infoText ~= "" and -3 or -10)
		else
			button.name:Point("TOPLEFT", 3, infoText ~= "" and -3 or -10)
		end
	end
end

-- IconFrame
function EFL:Update_IconFrame(button)
	if E.db.enhanceFriendsList[button.TYPE].classIcon then
		local classFileName = localizedTable[button.class]
		if classFileName then
			button.iconFrame:Show()

			button.iconFrame.texture:SetTexCoord(unpack(CLASS_ICON_TCOORDS[classFileName]))
			button.iconFrame:SetAlpha(button.TYPE == "Online" and 1 or 0.6)

			if E.db.enhanceFriendsList.Online.classIconStatusColor then
				if button.TYPE == "Online" then
					if button.statusType == "" then
						if E.PixelMode then
							button.iconFrame:SetBackdropBorderColor(0, 0, 0, 1)
						else
							button.iconFrame:SetBackdropBorderColor(0, 0, 0, 0)
						end
					elseif button.statusType == CHAT_FLAG_AFK then
						button.iconFrame:SetBackdropBorderColor(1, 1, 0)
					elseif button.statusType == CHAT_FLAG_DND then
						button.iconFrame:SetBackdropBorderColor(1, 0, 0)
					end
				else
					if E.PixelMode then
						button.iconFrame:SetBackdropBorderColor(0, 0, 0, 1)
					else
						button.iconFrame:SetBackdropBorderColor(0, 0, 0, 0)
					end
				end
			else
				button.iconFrame:SetBackdropBorderColor(unpack(E["media"].bordercolor))
			end
		else
			button.iconFrame:Hide()
		end
	elseif button.iconFrame:IsShown() then
		button.iconFrame:Hide()
	end
end

function EFL:Configure_IconFrame(button)
	button.iconFrame:ClearAllPoints()
	if E.db.enhanceFriendsList.showStatusIcon then
		button.iconFrame:Point("LEFT", 22, 0)
	else
		button.iconFrame:Point("LEFT", 3, 0)
	end
end

function EFL:Construct_IconFrame(button)
	button.iconFrame = CreateFrame("Frame", "$parentIconFrame", button)
	button.iconFrame:Size(26)
	button.iconFrame:SetTemplate("Default")

	button.iconFrame.texture = button.iconFrame:CreateTexture()
	button.iconFrame.texture:SetAllPoints()
	button.iconFrame.texture:SetTexture("Interface\\WorldStateFrame\\Icons-Classes")
	button.iconFrame:Hide()
end

-- Background
function EFL:Update_Background(button)
	if not E.db.enhanceFriendsList.showBackground then return end

	if button.TYPE == "Online" then
		button.backgroundLeft:SetGradientAlpha("Horizontal", 1,0.824,0,0.05, 1,0.824,0,0)
		button.backgroundRight:SetGradientAlpha("Horizontal", 1,0.824,0,0, 1,0.824,0,0.05)
	else
		button.backgroundLeft:SetGradientAlpha("Horizontal", 0.588,0.588,0.588,0.05, 0.588,0.588,0.588,0)
		button.backgroundRight:SetGradientAlpha("Horizontal", 0.588,0.588,0.588,0, 0.588,0.588,0.588,0.05)
	end
end

function EFL:Configure_Background(button)
	if E.db.enhanceFriendsList.showBackground then
		button.backgroundLeft:Show()
		button.backgroundRight:Show()
	else
		button.backgroundLeft:Hide()
		button.backgroundRight:Hide()
	end
end

function EFL:Construct_Background(button)
	button.backgroundLeft = button:CreateTexture(nil, "BACKGROUND")
	button.backgroundLeft:SetWidth(button:GetWidth() / 2)
	button.backgroundLeft:SetHeight(32)
	button.backgroundLeft:SetPoint("LEFT", button, "CENTER")
	button.backgroundLeft:SetTexture(E.media.blankTex)
	button.backgroundLeft:SetGradientAlpha("Horizontal", 1,0.824,0.0,0.05, 1,0.824,0.0,0)

	button.backgroundRight = button:CreateTexture(nil, "BACKGROUND")
	button.backgroundRight:SetWidth(button:GetWidth() / 2)
	button.backgroundRight:SetHeight(32)
	button.backgroundRight:SetPoint("RIGHT", button, "CENTER")
	button.backgroundRight:SetTexture(E.media.blankTex)
	button.backgroundRight:SetGradientAlpha("Horizontal", 1,0.824,0.0,0, 1,0.824,0.0,0.05)
end

-- Highlight
function EFL:Update_Highlight(button)
	if button.TYPE == "Online" then
		if button.statusType == "" then
			button.highlightLeft:SetGradientAlpha("Horizontal", 0.243,0.570,1,0.35, 0.243,0.570,1,0)
			button.highlightRight:SetGradientAlpha("Horizontal", 0.243,0.570,1,0, 0.243,0.570,1,0.35)
		elseif button.statusType == CHAT_FLAG_AFK then
			button.highlightLeft:SetGradientAlpha("Horizontal", 1,1,0,0.35, 1,1,0,0)
			button.highlightRight:SetGradientAlpha("Horizontal", 1,1,0,0, 1,1,0,0.35)
		elseif button.statusType == CHAT_FLAG_DND then
			button.highlightLeft:SetGradientAlpha("Horizontal", 1,0,0,0.35, 1,0,0,0)
			button.highlightRight:SetGradientAlpha("Horizontal", 1,0,0,0, 1,0,0,0.35)
		end
	else
		button.highlightLeft:SetGradientAlpha("Horizontal", 0.486,0.518,0.541,0.35, 0.486,0.518,0.541,0)
		button.highlightRight:SetGradientAlpha("Horizontal", 0.486,0.518,0.541,0, 0.486,0.518,0.541,0.35)
	end
end

function EFL:Construct_Highlight(button)
	button.highlightLeft = button:CreateTexture(nil, "HIGHLIGHT")
	button.highlightLeft:SetWidth(button:GetWidth() / 2)
	button.highlightLeft:SetHeight(32)
	button.highlightLeft:SetPoint("LEFT", button, "CENTER")
	button.highlightLeft:SetTexture(E.media.blankTex)
	button.highlightLeft:SetGradientAlpha("Horizontal", 0.243,0.570,1,0.35, 0.243,0.570,1,0)

	button.highlightRight = button:CreateTexture(nil, "HIGHLIGHT")
	button.highlightRight:SetWidth(button:GetWidth() / 2)
	button.highlightRight:SetHeight(32)
	button.highlightRight:SetPoint("RIGHT", button, "CENTER")
	button.highlightRight:SetTexture(E.media.blankTex)
	button.highlightRight:SetGradientAlpha("Horizontal", 0.243,0.570,1,0, 0.243,0.570,1,0.35)
end

function EFL:GetLocalFriendInfo(name)
	local info = EnhancedFriendsListDB[E.myrealm][name]
	if info then
		return info[1], info[2], info[3], info[4]
	else
		return nil, nil, nil, nil
	end
end

function EFL:EnhanceFriends_SetButton(button)
	if button.buttonType == FRIENDS_BUTTON_TYPE_WOW then
		local name, level, class, area, connected, status = GetFriendInfo(button.id)
		if not name then return end

		button.nameText = name
		button.TYPE = connected and "Online" or "Offline"
		button.statusType = status

		if connected then
			if not EnhancedFriendsListDB[E.myrealm][name] then
				EnhancedFriendsListDB[E.myrealm][name] = {}
			end

			EnhancedFriendsListDB[E.myrealm][name] = {level, class, area, format("%i", time())}
		else
			local lastSeen, lastArea
			level, class, lastArea, lastSeen = self:GetLocalFriendInfo(name)
			area = lastArea or area
			button.lastSeen = lastSeen
		end

		button.levelText = level
		button.class = class
		button.area = area

		self:Update_Background(button)
		self:Update_Status(button)
		self:Update_IconFrame(button)
		self:Update_Name(button)
		self:Update_Highlight(button)
	end
end

function EFL:FriendsFrameStatusDropDown_Update()
	local status = (StatusIcons[E.db.enhanceFriendsList.statusIcons][(UnitIsDND("Player") and "DND" or UnitIsAFK("Player") and "AFK" or "Online")])
	FriendsFrameStatusDropDownStatus:SetTexture(status)
end

function EFL:FriendListUpdate()
	self.db = E.db.enhanceFriendsList

	if ElvCharacterDB.EnhancedFriendsList_Data then
		for i = 1, GetNumFriends() do
			local name, level, class, area, connected, status = GetFriendInfo(i)
			if ElvCharacterDB.EnhancedFriendsList_Data[name] then
				local data = ElvCharacterDB.EnhancedFriendsList_Data[name]
				if not EnhancedFriendsListDB[E.myrealm][name] then
					EnhancedFriendsListDB[E.myrealm][name] = {}
				end
				EnhancedFriendsListDB[E.myrealm][name] = {data.level, data.class, data.area, data.lastSeen}
			end
		end
		ElvCharacterDB.EnhancedFriendsList_Data = nil
	end

	for i = 1, #FriendsFrameFriendsScrollFrame.buttons do
		local button = FriendsFrameFriendsScrollFrame.buttons[i]

		self:Construct_IconFrame(button)

		self:Construct_Background(button)
		button.background:Hide()

		self:Construct_Highlight(button)
		button.highlight:SetVertexColor(0, 0, 0, 0)
	end

	self:Update()

	self:SecureHook("FriendsFrameStatusDropDown_Update")
	self:SecureHook(FriendsFrameFriendsScrollFrame, "buttonFunc", "EnhanceFriends_SetButton")
end

function EFL:Initialize()
	EP:RegisterPlugin(addonName, self.InsertOptions)

	if not EnhancedFriendsListDB then
		EnhancedFriendsListDB = {}
	end

	if not EnhancedFriendsListDB[E.myrealm] then
		EnhancedFriendsListDB[E.myrealm] = {}
	end

	self:FriendListUpdate()
end

local function InitializeCallback()
	EFL:Initialize()
end

E:RegisterModule(EFL:GetName(), InitializeCallback)