local CoolLine = CreateFrame("Frame", "CoolLine", UIParent)
-- Coolline 位置 for QulightUI
CoolLine:SetPoint("BOTTOM", UIParent, "BOTTOM", 0, 90)
local self = CoolLine
self:SetScript("OnEvent", function(this, event, ...)
	this[event](this, ...)
end)
local smed = LibStub("LibSharedMedia-3.0")

local _G = getfenv(0)
local pairs, ipairs = pairs, ipairs
local tinsert, tremove = tinsert, tremove
local GetTime = GetTime
local random = math.random
local strmatch = strmatch
local UnitExists, HasPetUI = UnitExists, HasPetUI
local GetSpellInfo = GetSpellInfo

local db, block
local backdrop = { edgeSize=16, } -- Left Unchanged
local section, iconsize = 0, 0
local tick0, tick1, tick3, tick10, tick30, tick120, tick300
local BOOKTYPE_SPELL, BOOKTYPE_PET = BOOKTYPE_SPELL, BOOKTYPE_PET
local spells = { [BOOKTYPE_SPELL] = { }, [BOOKTYPE_PET] = { }, }
local chargespells = { [BOOKTYPE_SPELL] = { }, [BOOKTYPE_PET] = { }, }
local frames, cooldowns, specialspells, placeholder = { }, { }, { }, { }

local SetValue, updatelook, createfs, ShowOptions, RuneCheck
local function SetValueH(this, v, just)
	this:SetPoint(just or "CENTER", self, "LEFT", v, 0)
end
local function SetValueHR(this, v, just)
	this:SetPoint(just or "CENTER", self, "LEFT", db.w - v, 0)
end
local function SetValueV(this, v, just)
	this:SetPoint(just or "CENTER", self, "BOTTOM", 0, v)
end
local function SetValueVR(this, v, just)
	this:SetPoint(just or "CENTER", self, "BOTTOM", 0, db.h - v)
end

self:RegisterEvent("ADDON_LOADED")
function CoolLine:ADDON_LOADED(a1)
	if a1 ~= "CoolLine" then return end
	self:UnregisterEvent("ADDON_LOADED")
	self.ADDON_LOADED = nil

	CoolLineDB = CoolLineDB or { }
	if CoolLineDB.perchar then
		CoolLineCharDB = CoolLineCharDB or CoolLineDB
		db = CoolLineCharDB
	else
		CoolLineCharDB = nil
		db = CoolLineDB
	end
	if db.dbinit ~= 3 then
		db.dbinit = 3
		for k, v in pairs({
			w = 360, h = 18, x = 0, y = -240,
			statusbar = "Blizzard",
			bgcolor = { r = 0, g = 0, b = 0, a = 0.5, },
			border = "Blizzard Dialog",
			bordersize = 16, -- Added defaults
			borderinset = 4, -- Added defaults
			bordercolor = { r = 1, g = 1, b = 1, a = 1, },
			font = "Friz Quadrata TT",
			fontsize = 10,
			iconplus = 4,
			fontcolor = { r = 1, g = 1, b = 1, a = 0.8, },
			spellcolor = { r = 0.8, g = 0.4, b = 0, a = 1, },
			nospellcolor = { r = 0, g = 0, b = 0, a = 1, },
			inactivealpha = 0.5,
			activealpha = 1.0,
			block = {  -- [spell or item name] = true,
				[GetItemInfo(6948) or "Hearthstone"] = true,  -- Hearthstone
			},
		}) do
			db[k] = (db[k] ~= nil and db[k]) or v
		end
	end
	block = db.block
	local _, class = UnitClass("player")
	if class == "DEATHKNIGHT" then
		local runecd = {  -- fix by NeoSyrex
			[GetSpellInfo(50977) or "Death Gate"] = 11,
			[GetSpellInfo(43265) or "Death and Decay"] = 11,
			[GetSpellInfo(48263) or "Frost Presence"] = 1,
			[GetSpellInfo(48266) or "Blood Presence"] = 1,
			[GetSpellInfo(48265) or "Unholy Presence"] = 1,
			[GetSpellInfo(42650) or "Army of the Dead"] = 11,
			[GetSpellInfo(49222) or "Bone Shield"] = 11,
			[GetSpellInfo(47476) or "Strangulate"] = 11,
			[GetSpellInfo(51052) or "Anti-Magic Zone"] = 11,
			[GetSpellInfo(63560) or "Ghoul Frenzy"] = 10,
			[GetSpellInfo(49184) or "Howling Blast"] = 8,
			[GetSpellInfo(51271) or "Unbreakable Armor"] = 11,
			[GetSpellInfo(55233) or "Vampiric Blood"] = 11,
			[GetSpellInfo(49005) or "Mark of Blood"] = 11,
			[GetSpellInfo(48982) or "Rune Tap"] = 11,
		}
		RuneCheck = function(name, duration)
			local rc = runecd[name]
			if not rc or (rc <= duration and (rc > 10 or rc >= duration)) then
				return true
			end
		end
	elseif class == "MAGE" then
		placeholder = { [125430] = 112948, }
	elseif class == "PRIEST" then
		specialspells = {
			[GetSpellInfo(87151) or "blah"] = 87151,  -- Archangel
			[GetSpellInfo(14751) or "blah"] = 14751,  -- Chakra
			[GetSpellInfo(81209) or "blah"] = 81209,  -- Chakra
			[GetSpellInfo(88684) or "blah"] = 88684,  -- Holy Word: Serenity
			[GetSpellInfo(88682) or "blah"] = 88682,  -- Holy Word: Aspire
			[GetSpellInfo(88685) or "blah"] = 88685,  -- Holy Word: Sanctuary
			[GetSpellInfo(88625) or "blah"] = 88625,  -- Holy Word: Chastise
		}
	--[[elseif class == "DRUID" then
		specialspells = {
			[GetSpellInfo(33917) or "blah"] = 33878,  -- Mangle (Bear)
			[GetSpellInfo(106830) or "blah"] = 77758,  -- Thrash (Bear)
		}]]--
	end

	SlashCmdList.COOLLINE = ShowOptions
	SLASH_COOLLINE1 = "/coolline"
	local panel = CreateFrame("Frame")
	panel.name = "CoolLine"
	panel:SetScript("OnShow", function(this)
		local t1 = this:CreateFontString(nil, "ARTWORK")
		t1:SetJustifyH("LEFT")
		t1:SetJustifyV("TOP")
		t1:SetFontObject(GameFontNormalLarge)
		t1:SetPoint("TOPLEFT", 16, -16)
		t1:SetText(this.name)

		local t2 = this:CreateFontString(nil, "ARTWORK")
		t2:SetJustifyH("LEFT")
		t2:SetJustifyV("TOP")
		t2:SetFontObject(GameFontHighlightSmall)
		t2:SetHeight(43)
		t2:SetPoint("TOPLEFT", t1, "BOTTOMLEFT", 0, -8)
		t2:SetPoint("RIGHT", this, "RIGHT", -32, 0)
		t2:SetNonSpaceWrap(true)
		t2:SetFormattedText("Notes: %s\nAuthor: %s\nVersion: %s\n"..
							"Hint: |cffffff00/coolline|r to open menu; |cffffff00/coolline SpellOrItemNameOrLink|r to add/remove filter",
							 GetAddOnMetadata("CoolLine", "Notes") or "N/A",
							 GetAddOnMetadata("CoolLine", "Author") or "N/A",
							 GetAddOnMetadata("CoolLine", "Version") or "N/A")

		local b = CreateFrame("Button", nil, this, "UIPanelButtonTemplate")
		b:SetWidth(120)
		b:SetHeight(20)
		b:SetText("Options Menu")
		b:SetScript("OnClick", ShowOptions)
		b:SetPoint("TOPLEFT", t2, "BOTTOMLEFT", -2, -8)
		this:SetScript("OnShow", nil)
	end)
	InterfaceOptions_AddCategory(panel)

	createfs = function(f, text, offset, just)
		local fs = f or self.overlay:CreateFontString(nil, "OVERLAY")
		fs:SetFont(smed:Fetch("font", db.font), db.fontsize)
		fs:SetTextColor(db.fontcolor.r, db.fontcolor.g, db.fontcolor.b, db.fontcolor.a)
		fs:SetText(text)
		fs:SetWidth(db.fontsize * 3)
		fs:SetHeight(db.fontsize + 2)
		fs:SetShadowColor(db.bgcolor.r, db.bgcolor.g, db.bgcolor.b, db.bgcolor.a)
		fs:SetShadowOffset(1, -1)
		if just then
			fs:ClearAllPoints()
			if db.vertical then
				fs:SetJustifyH("CENTER")
				just = db.reverse and ((just == "LEFT" and "TOP") or "BOTTOM") or ((just == "LEFT" and "BOTTOM") or "TOP")
			elseif db.reverse then
				just = (just == "LEFT" and "RIGHT") or "LEFT"
				offset = offset + ((just == "LEFT" and 1) or -1)
				fs:SetJustifyH(just)
			else
				offset = offset + ((just == "LEFT" and 1) or -1)
				fs:SetJustifyH(just)
			end
		else
			fs:SetJustifyH("CENTER")
		end
		SetValue(fs, offset, just)
		return fs
	end
	updatelook = function()
		self:SetWidth(db.w or 130)
		self:SetHeight(db.h or 18)
		self:SetPoint("CENTER", UIParent, "CENTER", db.x or 0, db.y or -240)

		self.bg = self.bg or self:CreateTexture(nil, "ARTWORK")
		self.bg:SetTexture(smed:Fetch("statusbar", db.statusbar))
		self.bg:SetVertexColor(db.bgcolor.r, db.bgcolor.g, db.bgcolor.b, db.bgcolor.a)
		self.bg:SetAllPoints(self)
		if db.vertical then
			self.bg:SetTexCoord(1,0, 0,0, 1,1, 0,1)
		else
			self.bg:SetTexCoord(0,1, 0,1)
		end

		self.border = self.border or CreateFrame("Frame", nil, self)
		self.border:SetPoint("TOPLEFT", -db.borderinset, db.borderinset) -- Implemented 'insets'
		self.border:SetPoint("BOTTOMRIGHT", db.borderinset, -db.borderinset) -- Implemented 'insets'
		backdrop = {
			edgeFile = smed:Fetch("border", db.border),
			edgeSize = db.bordersize,
		} -- Updated backdrop table
		self.border:SetBackdrop(backdrop)
		self.border:SetBackdropBorderColor(db.bordercolor.r, db.bordercolor.g, db.bordercolor.b, db.bordercolor.a)

		self.overlay = self.overlay or CreateFrame("Frame", nil, self.border)
		self.overlay:SetFrameLevel(24)

		section = (db.vertical and db.h or db.w) / 6
		iconsize = ((db.vertical and db.w) or db.h) + (db.iconplus or 4)
		SetValue = (db.vertical and (db.reverse and SetValueVR or SetValueV)) or (db.reverse and SetValueHR or SetValueH)

		tick0 = createfs(tick0, "0", 0, "LEFT")
		tick1 = createfs(tick1, "1", section)
		tick3 = createfs(tick3, "3", section * 2)
		tick10 = createfs(tick10, "10", section * 3)
		tick30 = createfs(tick30, "30", section * 4)
		tick120 = createfs(tick120, "2m", section * 5)
		tick300 = createfs(tick300, "6m", section * 6, "RIGHT")

		if not self.cb and (not smed:IsValid("font", db.font) or not smed:IsValid("border", db.border) or not smed:IsValid("statusbar", db.statusbar)) then
			smed.RegisterCallback(self, "LibSharedMedia_Registered", updatelook)
			self.cb = true
		end
		if db.hidepet then
			self:UnregisterEvent("UNIT_PET")
			self:UnregisterEvent("PET_BAR_UPDATE_COOLDOWN")
		else
			self:RegisterUnitEvent("UNIT_PET", "player")
			self:UNIT_PET()
		end
		if db.hidebag and db.hideinv then
			self:UnregisterEvent("BAG_UPDATE_COOLDOWN")
		else
			self:RegisterEvent("BAG_UPDATE_COOLDOWN")
			self:BAG_UPDATE_COOLDOWN()
		end
		if db.hidefail then
			self:UnregisterEvent("UNIT_SPELLCAST_FAILED")
		else
			self:RegisterUnitEvent("UNIT_SPELLCAST_FAILED", "player")
		end
		CoolLine:SetAlpha((CoolLine.unlock or #cooldowns > 0) and db.activealpha or db.inactivealpha)
		for _, frame in ipairs(cooldowns) do
			frame:SetWidth(iconsize)
			frame:SetHeight(iconsize)
		end
	end
	CoolLine.updatelook = updatelook

	if IsLoggedIn() then
		CoolLine:PLAYER_LOGIN()
	else
		self:RegisterEvent("PLAYER_LOGIN")
	end
end

--------------------------------
function CoolLine:PLAYER_LOGIN()
--------------------------------
	self.PLAYER_LOGIN = nil

	self:RegisterEvent("SPELLS_CHANGED")
	self:RegisterEvent("SPELL_UPDATE_COOLDOWN")
	self:RegisterEvent("SPELL_UPDATE_CHARGES")
	self.SPELL_UPDATE_CHARGES = self.SPELL_UPDATE_COOLDOWN
	self:SPELL_UPDATE_COOLDOWN()

	self:RegisterEvent("PET_BATTLE_OPENING_START")
	self:RegisterEvent("PET_BATTLE_CLOSE")
	self.PET_BATTLE_OPENING_START = self.Hide
	self.PET_BATTLE_CLOSE = self.Show
	if C_PetBattles.IsInBattle() then
		self:Hide()
	end

	self:RegisterUnitEvent("UNIT_ENTERED_VEHICLE", "player")
	if UnitHasVehicleUI("player") then
		self:RegisterEvent("ACTIONBAR_UPDATE_COOLDOWN")
		self:RegisterUnitEvent("UNIT_EXITED_VEHICLE", "player")
	end

	updatelook()
	self:SetAlpha((#cooldowns == 0 and db.inactivealpha) or db.activealpha)
	self:RegisterEvent("PLAYER_LEAVING_WORLD")
end

-----------------------------------------
function CoolLine:PLAYER_ENTERING_WORLD()
-----------------------------------------
	self:RegisterEvent("SPELLS_CHANGED")
	self:RegisterEvent("SPELL_UPDATE_COOLDOWN")
	self:SPELLS_CHANGED()
	self:SPELL_UPDATE_COOLDOWN()
end

----------------------------------------
function CoolLine:PLAYER_LEAVING_WORLD()
----------------------------------------
	self:RegisterEvent("PLAYER_ENTERING_WORLD")
	self:UnregisterEvent("SPELLS_CHANGED")
	self:UnregisterEvent("SPELL_UPDATE_COOLDOWN")
end

local iconback = { bgFile="Interface\\AddOns\\CoolLine\\backdrop.tga" }
local elapsed, throt, ptime, isactive = 0, 1.5, 0, false
local function ClearCooldown(f, name)
	name = name or (f and f.name)
	for index, frame in ipairs(cooldowns) do
		if frame.name == name then
			frame:Hide()
			frame.name = nil
			frame.endtime = nil
			tinsert(frames, tremove(cooldowns, index))
			break
		end
	end
end
local function SetupIcon(frame, position, tthrot, active, fl)
	throt = (throt < tthrot and throt) or tthrot
	isactive = active or isactive
	if fl then
		frame:SetFrameLevel(random(1,5) * 2 + 2)
	end
	SetValue(frame, position)
end
local function OnUpdate(this, a1, ctime, dofl)
	elapsed = elapsed + a1
	if elapsed < throt then return end
	elapsed = 0

	if #cooldowns == 0 then
		if not CoolLine.unlock then
			self:SetScript("OnUpdate", nil)
			self:SetAlpha(db.inactivealpha)
		end
		return
	end

	ctime = ctime or GetTime()
	if ctime > ptime then
		dofl, ptime = true, ctime + 0.4
	end
	isactive, throt = false, 1.5
	for index, frame in pairs(cooldowns) do
		local remain = frame.endtime - ctime
		if remain < 3 then
			if remain > 1 then
				SetupIcon(frame, section * (remain + 1) * 0.5, 0.02, true, dofl)  -- 1 + (remain - 1) / 2
			elseif remain > 0.3 then
				SetupIcon(frame, section * remain, 0, true, dofl)
			elseif remain > 0 then
				local size = iconsize * (0.5 - remain) * 5  -- iconsize + iconsize * (0.3 - remain) / 0.2
				frame:SetWidth(size)
				frame:SetHeight(size)
				SetupIcon(frame, section * remain, 0, true, dofl)
			elseif remain > -1 then
				SetupIcon(frame, 0, 0, true, dofl)
				frame:SetAlpha(1 + remain)  -- fades
			else
				throt = (throt < 0.2 and throt) or 0.2
				isactive = true
				ClearCooldown(frame)
			end
		elseif remain < 10 then
			SetupIcon(frame, section * (remain + 11) * 0.14286, remain > 4 and 0.05 or 0.02, true, dofl)  -- 2 + (remain - 3) / 7
		elseif remain < 30 then
			SetupIcon(frame, section * (remain + 50) * 0.05, 0.06, true, dofl)  -- 3 + (remain - 10) / 20
		elseif remain < 120 then
			SetupIcon(frame, section * (remain + 330) * 0.011111, 0.18, true, dofl)  -- 4 + (remain - 30) / 90
		elseif remain < 360 then
			SetupIcon(frame, section * (remain + 1080) * 0.0041667, 1.2, true, dofl)  -- 5 + (remain - 120) / 240
			frame:SetAlpha(1)
		else
			SetupIcon(frame, 6 * section, 2, false, dofl)
		end
	end
	if not isactive and not CoolLine.unlock then
		self:SetAlpha(db.inactivealpha)
	end
end
local function NewCooldown(name, icon, endtime, isplayer)
	local f
	for index, frame in pairs(cooldowns) do
		if frame.name == name and frame.isplayer == isplayer then
			f = frame
			break
		elseif frame.endtime == endtime then
			return
		end
	end
	if not f then
		f = f or tremove(frames)
		if not f then
			f = CreateFrame("Frame", nil, CoolLine.border)
			f:SetBackdrop(iconback)
			f.icon = f:CreateTexture(nil, "ARTWORK")
			f.icon:SetTexCoord(0.07, 0.93, 0.07, 0.93)
			f.icon:SetPoint("TOPLEFT", 1, -1)
			f.icon:SetPoint("BOTTOMRIGHT", -1, 1)
		end
		tinsert(cooldowns, f)
	end
	local ctime = GetTime()
	f:SetWidth(iconsize)
	f:SetHeight(iconsize)
	f:SetAlpha((endtime - ctime > 360) and 0.6 or 1)
	f.name, f.endtime, f.isplayer = name, endtime, isplayer
	f.icon:SetTexture(icon)
	local c = db[isplayer and "spellcolor" or "nospellcolor"]
	f:SetBackdropColor(c.r, c.g, c.b, c.a)
	f:Show()
	self:SetScript("OnUpdate", OnUpdate)
	self:SetAlpha(db.activealpha)
	OnUpdate(self, 2, ctime)
end
CoolLine.NewCooldown, CoolLine.ClearCooldown = NewCooldown, ClearCooldown

do  -- cache spells that have a cooldown
	local GetSpellBookItemName, GetSpellBookItemInfo, GetSpellBaseCooldown, GetSpellCharges, IsTalentSpell
	    = GetSpellBookItemName, GetSpellBookItemInfo, GetSpellBaseCooldown, GetSpellCharges, IsTalentSpell
	local N_MIN_COOLDOWN = gsub(SPELL_RECAST_TIME_MIN, "%%%.3g", "(.+)")
	local N_SEC_COOLDOWN = gsub(SPELL_RECAST_TIME_SEC, "%%%.3g", "(.+)")
	local scantip = CreateFrame("GameTooltip")
	for i = 1, 5 do
		local R = scantip:CreateFontString(nil,nil,"GameFontNormal")
		scantip:AddFontStrings(scantip:CreateFontString(nil,nil,"GameFontNormal"), R)
		scantip[i] = R
	end
	local function CacheBook(btype)
		local lastId
		local sb = spells[btype]
		local _, _, offset, numSpells = GetSpellTabInfo(2)
		for i = 1, offset + numSpells, 1 do
			local spellName = GetSpellBookItemName(i, btype)
			if not spellName then break end
			local slotType, spellId = GetSpellBookItemInfo(i, btype)
			if spellId and slotType == "FLYOUT" then
				local _, _, numSlots, isKnown = GetFlyoutInfo(spellId)
				for fi = 1, ((isKnown and numSlots) or 0), 1 do
					local flySpellId, _, _, flySpellName, _ = GetFlyoutSlotInfo(spellId, fi)
					lastId = flySpellId
					if flySpellId then
						local flycd = GetSpellBaseCooldown(flySpellId)
						if flycd and flycd > 2499 then
							sb[flySpellId] = specialspells[flySpellId] or flySpellName
						end
					end
				end
			elseif spellId and slotType ~= "FUTURESPELL" and spellId ~= lastId then
				lastId = spellId
				local _, maxCharges = GetSpellCharges(spellId)
				if maxCharges and maxCharges > 0 then
					--print(i, spellName, spellID, "CHARGES", maxCharges)
					chargespells[btype][spellId] = spellName
				else
					local spellcd = GetSpellBaseCooldown(placeholder[spellId] or spellId)
					if spellcd and spellcd > 2499 then
						sb[spellId] = spellName
						--print(i, spellName, spellID, "COOLDOWN", spellcd)
						if specialspells[spellName] then
							sb[ specialspells[spellName] ] = spellName
						end
					elseif IsTalentSpell(i, btype) then
						scantip:SetOwner(WorldFrame, "ANCHOR_NONE")
						scantip:SetSpellBookItem(i, btype)
						for j = 2, 3 do
							local text = scantip[j]:GetText()
							if text then
								local spellcd = tonumber(strmatch(text, N_SEC_COOLDOWN) or 0) * 1000
								if spellcd == 0 then
									spellcd = tonumber(strmatch(text, N_MIN_COOLDOWN) or 0) * 60000
								end
								if spellcd > 2499 then
									--print(i, spellName, spellID, "COOLDOWN", spellcd, "***")
									sb[spellId] = spellName
								end
							end
						end
						scantip:Hide()
					end
				end
			end
		end
	end
	----------------------------------
	function CoolLine:SPELLS_CHANGED()
	----------------------------------
		CacheBook(BOOKTYPE_SPELL)
		if not db.hidepet then
			CacheBook(BOOKTYPE_PET)
		end
	end
end

do  -- scans spellbook to update cooldowns, throttled since the event fires a lot
	local selap = 0
	local spellthrot = CreateFrame("Frame", nil, CoolLine)
	local GetSpellCooldown, GetSpellTexture, GetSpellCharges = GetSpellCooldown, GetSpellTexture, GetSpellCharges
	local function CheckSpellBook(btype)
		for id, name in pairs(spells[btype]) do
			local start, duration, enable = GetSpellCooldown(name)
			if enable == 1 and start > 0 and not block[name] and (not RuneCheck or RuneCheck(name, duration)) then
				if duration > 2.5 then
					local _, _, texture = GetSpellInfo(id)
					NewCooldown(name, texture, start + duration, btype == BOOKTYPE_SPELL)
				else
					for index, frame in ipairs(cooldowns) do
						if frame.name == name then
							if frame.endtime > start + duration + 0.1 then
								frame.endtime = start + duration
							end
							break
						end
					end
				end
			else
				ClearCooldown(nil, name)
			end
		end
		for id, name in pairs(chargespells[btype]) do
			local currentCharges, maxCharges, cooldownStart, cooldownDuration = GetSpellCharges(id)
			if cooldownStart and cooldownDuration and currentCharges < maxCharges and not block[name] then
				local _, _, texture = GetSpellInfo(id)
				NewCooldown(name, texture, cooldownStart + cooldownDuration, bookType == BOOKTYPE_SPELL)
			else
				ClearCooldown(nil, name)
			end
		end
	end
	spellthrot:SetScript("OnUpdate", function(this, a1)
		selap = selap + a1
		if selap < 0.3 then return end
		selap = 0
		this:Hide()
		CheckSpellBook(BOOKTYPE_SPELL)
		if not db.hidepet and HasPetUI() then
			CheckSpellBook(BOOKTYPE_PET)
		end
	end)
	spellthrot:Hide()
	-----------------------------------------
	function CoolLine:SPELL_UPDATE_COOLDOWN()
	-----------------------------------------
		spellthrot:Show()
	end
end

do  -- scans equipments and bags for item cooldowns
	local GetItemInfo = GetItemInfo
	local GetInventoryItemCooldown, GetInventoryItemTexture = GetInventoryItemCooldown, GetInventoryItemTexture
	local GetContainerItemCooldown, GetContainerItemInfo = GetContainerItemCooldown, GetContainerItemInfo
	local GetContainerNumSlots = GetContainerNumSlots
	---------------------------------------
	function CoolLine:BAG_UPDATE_COOLDOWN()
	---------------------------------------
		for i = 1, (db.hideinv and 0) or 18, 1 do
			local start, duration, enable = GetInventoryItemCooldown("player", i)
			if enable == 1 then
				local name = GetItemInfo(GetInventoryItemLink("player", i))
				if start > 0 and not block[name] then
					if duration > 3 and duration < 3601 then
						NewCooldown(name, GetInventoryItemTexture("player", i), start + duration)
					end
				else
					ClearCooldown(nil, name)
				end
			end
		end
		for i = 0, (db.hidebag and -1) or 4, 1 do
			for j = 1, GetContainerNumSlots(i), 1 do
				local start, duration, enable = GetContainerItemCooldown(i, j)
				if enable == 1 then
					local name = GetItemInfo(GetContainerItemLink(i, j))
					if start > 0 and not block[name] then
						if duration > 3 and duration < 3601 then
							NewCooldown(name, GetContainerItemInfo(i, j), start + duration)
						end
					else
						ClearCooldown(nil, name)
					end
				end
			end
		end
	end
end

-------------------------------------------
function CoolLine:PET_BAR_UPDATE_COOLDOWN()
-------------------------------------------
	for i = 1, 10, 1 do
		local start, duration, enable = GetPetActionCooldown(i)
		if enable == 1 then
			local name, _, texture = GetPetActionInfo(i)
			if name then
				if start > 0 and not block[name] then
					if duration > 3 then
						NewCooldown(name, texture, start + duration)
					end
				else
					ClearCooldown(nil, name)
				end
			end
		end
	end
end
------------------------------
function CoolLine:UNIT_PET(a1)
------------------------------
	if UnitExists("pet") and not HasPetUI() then
		self:RegisterEvent("PET_BAR_UPDATE_COOLDOWN")
	else
		self:UnregisterEvent("PET_BAR_UPDATE_COOLDOWN")
	end
end

local GetActionCooldown, HasAction = GetActionCooldown, HasAction
---------------------------------------------
function CoolLine:ACTIONBAR_UPDATE_COOLDOWN()  -- used only for vehicles
---------------------------------------------
	for i = 1, 8, 1 do
		local b = _G["OverrideActionBarButton"..i]
		if b and HasAction(b.action) then
			local start, duration, enable = GetActionCooldown(b.action)
			if enable == 1 then
				if start > 0 and not block[GetActionInfo(b.action)] then
					if duration > 3 then
						NewCooldown("vhcle"..i, GetActionTexture(b.action), start + duration)
					end
				else
					ClearCooldown(nil, "vhcle"..i)
				end
			end
		end
	end
end
------------------------------------------
function CoolLine:UNIT_ENTERED_VEHICLE()
------------------------------------------
	if not UnitHasVehicleUI("player") then return end
	self:RegisterEvent("ACTIONBAR_UPDATE_COOLDOWN")
	self:RegisterUnitEvent("UNIT_EXITED_VEHICLE", "player")
	self:ACTIONBAR_UPDATE_COOLDOWN()
end
-----------------------------------------
function CoolLine:UNIT_EXITED_VEHICLE()
-----------------------------------------
	self:UnregisterEvent("ACTIONBAR_UPDATE_COOLDOWN")
	for index, frame in ipairs(cooldowns) do
		if strmatch(frame.name, "vhcle") then
			ClearCooldown(nil, frame.name)
		end
	end
end

local failborder
----------------------------------------------------
function CoolLine:UNIT_SPELLCAST_FAILED(unit, spell)
----------------------------------------------------
	if #cooldowns == 0 then return end
	for index, frame in pairs(cooldowns) do
		if frame.name == spell then
			if frame.endtime - GetTime() > 1 then
				if not failborder then
					failborder = CreateFrame("Frame", nil, CoolLine.border)
					failborder:SetBackdrop(iconback)
					failborder:SetBackdropColor(1, 0, 0, 0.9)
					failborder:Hide()
					failborder:SetScript("OnUpdate", function(this, a1)
						this.alp = this.alp - a1
						if this.alp < 0 then return this:Hide() end
						this:SetAlpha(this.alp > 1 and 1 or this.alp)
					end)
				end
				failborder.alp = 1.2
				failborder:SetPoint("TOPLEFT", frame, "TOPLEFT", -2, 2)
				failborder:SetPoint("BOTTOMRIGHT", frame, "BOTTOMRIGHT", 2, -2)
				failborder:Show()
			end
			break
		end
	end

end


local CoolLineDD, Set
local info = { }
function ShowOptions(a1)
	if type(a1) == "string" and a1 ~= "" and a1 ~= "menu" and a1 ~= "options" and a1 ~= "help" then
		if strmatch(a1, "|H") then
			a1 = strmatch(a1, "|h%[(.+)%]|h")
		end
		if a1 then
			if block[a1] then
				block[a1] = nil
				print("|cff88ffffCool|r|cff88ff88Line|r: |cffffff00"..a1.."|r removed from filter.")
			else
				block[a1] = true
				print("|cff88ffffCool|r|cff88ff88Line|r: |cffffff00"..a1.."|r added to filter.")
			end
		end
		return
	end
	if not CoolLineDD then
		CoolLineDD = CreateFrame("Frame", "CoolLineDD", UIParent)
		CoolLineDD.displayMode = "MENU"

		Set = function(b, a1)
			if a1 == "unlock" then
				if not CoolLine.resizer then
					CoolLine:SetMovable(true)
					CoolLine:SetResizable(true)
					CoolLine:RegisterForDrag("LeftButton")
					CoolLine:SetScript("OnMouseUp", function(this, a1) if a1 == "RightButton" then ShowOptions() end end)
					CoolLine:SetScript("OnDragStart", function(this) this:StartMoving() end)
					CoolLine:SetScript("OnDragStop", function(this)
						this:StopMovingOrSizing()
						local x, y = this:GetCenter()
						local ux, uy = UIParent:GetCenter()
						db.x, db.y = floor(x - ux + 0.5), floor(y - uy + 0.5)
						this:ClearAllPoints()
						updatelook()
					end)

					CoolLine:SetMinResize(6, 6)
					CoolLine.resizer = CreateFrame("Button", nil, CoolLine.border, "UIPanelButtonTemplate")
					local resize = CoolLine.resizer
					resize:SetWidth(8)
					resize:SetHeight(8)
					resize:SetPoint("BOTTOMRIGHT", CoolLine, "BOTTOMRIGHT", 2, -2)
					resize:SetScript("OnMouseDown", function(this) CoolLine:StartSizing("BOTTOMRIGHT") end)
					resize:SetScript("OnMouseUp", function(this)
						CoolLine:StopMovingOrSizing()
						db.w, db.h = floor(CoolLine:GetWidth() + 0.5), floor(CoolLine:GetHeight() + 0.5)
						updatelook()
					end)
				end
				if not CoolLine.unlock then
					CoolLine.unlock = true
					CoolLine:EnableMouse(true)
					CoolLine.resizer:Show()
					CoolLine:SetAlpha(db.activealpha)
					print("CoolLine - drag frame to reposition or drag red corner to resize")
				else
					CoolLine.unlock = nil
					CoolLine:EnableMouse(false)
					CoolLine.resizer:Hide()
					OnUpdate(CoolLine, 2)
				end
			elseif a1 then
				if a1 == "vertical" then
					local pw, ph = db.w, db.h
					db.w, db.h = ph, pw
				elseif a1 == "resetall" then
					CoolLineCharDB, CoolLineDB = nil, nil
					return ReloadUI()
				end
				db[a1] = not db[a1]
				if a1 == "perchar" then
					if db.perchar then
						CoolLineCharDB = CoolLineCharDB or CoolLineDB
					else
						CoolLineCharDB = nil
					end
					ReloadUI()
				end
				updatelook()
			end
		end
		local function SetSelect(b, a1)
			db[a1] = tonumber(b.value) or b.value
			local level, num = strmatch(b:GetName(), "DropDownList(%d+)Button(%d+)")
			level, num = tonumber(level) or 0, tonumber(num) or 0
			for i = 2, level, 1 do
				for j = 1, UIDROPDOWNMENU_MAXBUTTONS, 1 do
					local check = _G["DropDownList"..i.."Button"..j.."Check"]
					if check and i == level and j == num then
						check:Show()
					elseif b then
						check:Hide()
					end
				end
			end
			updatelook()
		end
		local function SetColor(a1)
			local dbc = db[UIDROPDOWNMENU_MENU_VALUE]
			if not dbc then return end
			local r, g, b, a
			if a1 then
				local pv = ColorPickerFrame.previousValues
				r, g, b, a = pv.r, pv.g, pv.b, 1 - pv.opacity
			else
				r, g, b = ColorPickerFrame:GetColorRGB()
				a = 1 - OpacitySliderFrame:GetValue()
			end
			dbc.r, dbc.g, dbc.b, dbc.a = r, g, b, a
			updatelook()
		end
		local function HideCheck(b)
			if b and b.GetName and _G[b:GetName().."Check"] then
				_G[b:GetName().."Check"]:Hide()
			end
		end
		local function AddButton(lvl, text, keepshown)
			info.text = text
			info.keepShownOnClick = keepshown
			UIDropDownMenu_AddButton(info, lvl)
			wipe(info)
		end
		local function AddToggle(lvl, text, value)
			info.arg1 = value
			info.func = Set
			if value == "unlock" then
				info.checked = CoolLine.unlock
			else
				info.checked = db[value]
			end
			AddButton(lvl, text, 1)
		end
		local function AddList(lvl, text, value)
			info.value = value
			info.hasArrow = true
			info.func = HideCheck
			info.notCheckable = 1
			AddButton(lvl, text, 1)
		end
		local function AddSelect(lvl, text, arg1, value)
			info.arg1 = arg1
			info.func = SetSelect
			info.value = value
			if tonumber(value) and tonumber(db[arg1] or "blah") then
				if floor(100 * tonumber(value)) == floor(100 * tonumber(db[arg1])) then
					info.checked = true
				end
			else
				info.checked = db[arg1] == value
			end
			AddButton(lvl, text, 1)
		end
		local function AddColor(lvl, text, value)
			local dbc = db[value]
			if not dbc then return end
			info.hasColorSwatch = true
			info.hasOpacity = 1
			info.r, info.g, info.b, info.opacity = dbc.r, dbc.g, dbc.b, 1 - (dbc.a or 0)
			info.swatchFunc, info.opacityFunc, info.cancelFunc = SetColor, SetColor, SetColor
			info.value = value
			info.func = UIDropDownMenuButton_OpenColorPicker
			info.notCheckable = 1
			AddButton(lvl, text, nil)
		end
		CoolLineDD.initialize = function(self, lvl)
			if lvl == 1 then
				info.isTitle = true
				info.notCheckable = 1
				AddButton(lvl, "|cff88ffffCool|r|cff88ff88Line|r")
				AddList(lvl, "Texture", "statusbar")
				AddColor(lvl, "Texture Color", "bgcolor")
				AddList(lvl, "Border", "border")
				AddList(lvl, "Border Size", "bordersize") -- Added Options
				AddList(lvl, "Border Inset", "borderinset") -- Added Options
				AddColor(lvl, "Border Color", "bordercolor")
				AddList(lvl, "Font", "font")
				AddColor(lvl, "Font Color", "fontcolor")
				AddList(lvl, "Font Size", "fontsize")
				AddColor(lvl, "My Spell Color", "spellcolor")
				AddColor(lvl, "Item/Pet Color", "nospellcolor")
				AddList(lvl, "Inactive Opacity", "inactivealpha")
				AddList(lvl, "Active Opacity", "activealpha")
				AddList(lvl, "Icon Size", "iconplus")
				AddList(lvl, "More", "More")
				AddToggle(lvl, "Unlock", "unlock")
			elseif lvl and lvl > 1 then
				local sub = UIDROPDOWNMENU_MENU_VALUE
				if sub == "font" or sub == "statusbar" or sub == "border" then
					local t = smed:List(sub)
					local starti = 20 * (lvl - 2) + 1
					local endi = 20 * (lvl - 1)
					for i = starti, endi, 1 do
						if not t[i] then break end
						AddSelect(lvl, t[i], sub, t[i])
						if i == endi and t[i + 1] then
							AddList(lvl, "More", sub)
						end
					end
				elseif sub == "fontsize" then
					for i = 5, 12, 1 do
						AddSelect(lvl, i, "fontsize", i)
					end
					for i = 14, 28, 2 do
						AddSelect(lvl, i, "fontsize", i)
					end
				elseif sub == "bordersize" then -- Added options limits
					for i = 1, 16, 1 do
						AddSelect(lvl, i, "bordersize", i)
					end
				elseif sub == "borderinset" then -- Added options limits
					for i = -10, 10, 1 do
						AddSelect(lvl, i, "borderinset", i)
					end
				elseif sub == "inactivealpha" or sub == "activealpha" then
					for i = 0, 1, 0.1 do
						AddSelect(lvl, format("%.1f", i), sub, i)
					end
				elseif sub == "iconplus" then
					for i = 0, 24, 2 do
						AddSelect(lvl, format("+%d", i), sub, i)
					end
				elseif sub == "More" then
					AddToggle(lvl, "Vertical", "vertical")
					AddToggle(lvl, "Reverse", "reverse")
					AddToggle(lvl, "Disable Cast Fail", "hidefail")
					AddToggle(lvl, "Disable Equipped", "hideinv")
					AddToggle(lvl, "Disable Bags", "hidebag")
					AddToggle(lvl, "Disable Pet", "hidepet")
					AddToggle(lvl, "Save Per Char", "perchar")
					AddToggle(lvl, _G.RESET_TO_DEFAULT, "resetall")
				end
			end
		end
	end
	ToggleDropDownMenu(1, nil, CoolLineDD, "cursor")
end

CONFIGMODE_CALLBACKS = CONFIGMODE_CALLBACKS or {}
CONFIGMODE_CALLBACKS.CoolLine = function(action, mode)
	if action == "ON" then
		if not CoolLineDD then
			ShowOptions()
			ToggleDropDownMenu(1, nil, CoolLineDD, "cursor")
		end
		if CoolLineDD and not CoolLine.unlock then
			Set(nil, "unlock")
		end
	elseif action == "OFF" then
		if CoolLineDD and CoolLine.unlock then
			Set(nil, "unlock")
		end
	end
end
