local _G = getfenv(0)

-- if the Debug library is available then use it
if AceLibrary:HasInstance("AceDebug-2.0") then
	Skinner = AceLibrary("AceAddon-2.0"):new("AceEvent-2.0", "AceDB-2.0", "AceConsole-2.0", "AceHook-2.1", "AceDebug-2.0", "FuBarPlugin-2.0")
--	Skinner:SetDebugging(true)
else
	Skinner = AceLibrary("AceAddon-2.0"):new("AceEvent-2.0", "AceDB-2.0", "AceConsole-2.0", "AceHook-2.1", "FuBarPlugin-2.0")
	function Skinner:Debug() end
end

local L = AceLibrary("AceLocale-2.2"):new("Skinner")
local Tablet = AceLibrary("Tablet-2.0")

-- Add SurfaceLib support
local Surface = AceLibrary("Surface-1.0")
Surface:Register("Glaze", "Interface\\AddOns\\Skinner\\textures\\glaze.tga")

-- specify where debug messages go
Skinner.debugFrame = ChatFrame5

-- FuBar setup
Skinner.hasIcon = "Interface\\Icons\\INV_Holiday_Christmas_Wrapping_01"
Skinner.defaultMinimapPosition = 285
Skinner.cannotDetachTooltip = true
Skinner.hasNoColor = true
Skinner.clickableTooltip = false
Skinner.hideMenuTitle = true
Skinner.hideWithoutStandby = true

function Skinner:OnInitialize()
--	self:Debug("OnInitialize")
	
	self:RegisterDB("SkinnerDB")

	self:Defaults()
	self:Options()
	
	self:RegisterChatCommand({"/Skinner", "/skin"}, self.options)
	self.OnMenuRequest = self.options

	-- Heading and Body Text colours
	HTr, HTg, HTb = self.db.profile.HeadText.r, self.db.profile.HeadText.g, self.db.profile.HeadText.b
	BTr, BTg, BTb = self.db.profile.BodyText.r, self.db.profile.BodyText.g, self.db.profile.BodyText.b

	-- Frame multipliers
	FxMult, FyMult = 0.9, 0.84
	-- Frame Tab multipliers
	FTxMult, FTyMult = 0.5, 0.75
	-- EditBox regions to keep
	ebRegions = {1, 2, 3, 4, 5}

	-- Gradient settings
	MinR, MinG, MinB, MinA = self.db.profile.GradientMin.r, self.db.profile.GradientMin.g, self.db.profile.GradientMin.b, self.db.profile.GradientMin.a
	MaxR, MaxG, MaxB, MaxA = self.db.profile.GradientMax.r, self.db.profile.GradientMax.g, self.db.profile.GradientMax.b, self.db.profile.GradientMax.a
	orientation = self.db.profile.Gradient.rotate and "HORIZONTAL" or "VERTICAL" 
	gradientOn = self.db.profile.Gradient.invert and {orientation, MaxR, MaxG, MaxB, MaxA, MinR, MinG, MinB, MinA} or {orientation, MinR, MinG, MinB, MinA, MaxR, MaxG, MaxB, MaxA}  
	gradientOff = {orientation, 0, 0, 0, 1, 0, 0, 0, 1}
	gradientTab = {orientation, 1, 1, 1, 1, 1, 1, 1, 1}
	gradientCBar = {orientation, .25, .25, .55, 1, 0, 0, 0, 1}

	backdrop = {
		bgFile = "Interface\\ChatFrame\\ChatFrameBackground", tile = true, tileSize = 16,
		edgeFile = "Interface\\Tooltips\\UI-Tooltip-Border", edgeSize = 16,
		insets = {left = 4, right = 4, top = 4, bottom = 4},
	}

end

function Skinner:OnEnable()
--	self:Debug("OnEnable")

	self.initialized = {}

	self:RegisterEvent("ADDON_LOADED")
	self:RegisterEvent("AUCTION_HOUSE_SHOW")
	
	self:ScheduleEvent(self.BlizzardFrames, self.db.profile.Delay.Init, self)
	self:ScheduleEvent(self.AddonFrames, self.db.profile.Delay.Init + self.db.profile.Delay.Addons + 0.1, self)

end

function Skinner:applySkin(frame, header, bba, ba, fh, bd)

	if not frame then return end

	local self = Skinner
	frame:SetBackdrop(bd or backdrop)
	frame:SetBackdropBorderColor(self.db.profile.BackdropBorder.r or .5, self.db.profile.BackdropBorder.g or .5, self.db.profile.BackdropBorder.b or .5, bba or self.db.profile.BackdropBorder.a or 1)
	frame:SetBackdropColor(self.db.profile.Backdrop.r or 0, self.db.profile.Backdrop.g or 0, self.db.profile.Backdrop.b or 0, ba or self.db.profile.Backdrop.a or .9)

	if not frame.tfade then frame.tfade = frame:CreateTexture(nil, "BORDER") end
	frame.tfade:SetTexture("Interface\\ChatFrame\\ChatFrameBackground")

	if self.db.profile.FadeHeight.enable and (self.db.profile.FadeHeight.force or not fh) then
	-- set the Fade Height if not already passed to this function or 'forced'
	-- making sure that it isn't greater than the frame height
		fh = self.db.profile.FadeHeight.value <= math.ceil(frame:GetHeight()) and self.db.profile.FadeHeight.value or math.ceil(frame:GetHeight())
	end
-- self:Debug("aS - Frame, Fade Height: [%s, %s]", frame:GetName(), fh)

	frame.tfade:SetPoint("TOPLEFT", frame, "TOPLEFT", 4, -4)
	if fh then frame.tfade:SetPoint("BOTTOMRIGHT", frame, "TOPRIGHT", -4, -(fh - 4))
	else frame.tfade:SetPoint("BOTTOMRIGHT", frame, "BOTTOMRIGHT", -4, 4) end

	frame.tfade:SetBlendMode("ADD")
	frame.tfade:SetGradientAlpha(unpack(self.db.profile.Gradient.enable and gradientOn or gradientOff))

	if header and _G[frame:GetName().."Header"] then
		_G[frame:GetName().."Header"]:Hide()
		_G[frame:GetName().."Header"]:SetPoint("TOP", frame, "TOP", 0, 7)
	end

end

function Skinner:glazeStatusBar(frame, fi)

	if not frame then return end

	if frame:GetFrameType() ~= "StatusBar" then return end
	frame:SetStatusBarTexture(Surface:Fetch(self.db.profile.SBTexture))
	
	if fi then
		if not frame.bg then frame.bg = CreateFrame("StatusBar",nil,frame) end
		frame.bg:SetPoint("TOPLEFT", frame, "TOPLEFT", fi, -fi)
		frame.bg:SetPoint("BOTTOMRIGHT", frame, "BOTTOMRIGHT", -fi, fi)
		frame.bg:SetFrameStrata( frame:GetFrameStrata() )
		frame.bg:SetFrameLevel( frame:GetFrameLevel() - 1 )
		frame.bg:SetStatusBarTexture(Surface:Fetch(self.db.profile.SBTexture))
		frame.bg:SetStatusBarColor(0,0.5,0.5,0.5)
	end
end

function Skinner:skinTooltip(frame)
	if not self.db.profile.Tooltips.skin then return end

	if not frame then return end

	if not frame.tfade then frame.tfade = frame:CreateTexture(nil, "BORDER") end
	frame.tfade:SetTexture("Interface\\ChatFrame\\ChatFrameBackground")

	frame.tfade:SetPoint("TOPLEFT", frame, "TOPLEFT",1,-1)
	frame.tfade:SetPoint("BOTTOMRIGHT", frame, "BOTTOMRIGHT",-1,1)

	frame.tfade:SetBlendMode("ADD")
	frame.tfade:SetGradientAlpha(unpack(self.db.profile.Gradient.enable and gradientOn or gradientOff))

	if self.db.profile.Tooltips.style == 1 then
		frame.tfade:SetPoint("TOPLEFT", frame, "TOPLEFT", 6, -6)
		frame.tfade:SetPoint("BOTTOMRIGHT", frame, "TOPRIGHT", -6, -27)
	elseif self.db.profile.Tooltips.style == 2 then
		frame.tfade:SetPoint("TOPLEFT", frame, "TOPLEFT", 4, -4)
		frame.tfade:SetPoint("BOTTOMRIGHT", frame, "BOTTOMRIGHT", -4, 4)
	else
		frame.tfade:SetPoint("TOPLEFT", frame, "TOPLEFT", 4, -4)
		frame.tfade:SetPoint("BOTTOMRIGHT", frame, "TOPRIGHT", -4, -frame:GetHeight() + 4)
		frame:SetBackdropBorderColor(self.db.profile.BackdropBorder.r, self.db.profile.BackdropBorder.g, self.db.profile.BackdropBorder.b, self.db.profile.BackdropBorder.a)
		frame:SetBackdropColor(self.db.profile.Backdrop.r, self.db.profile.Backdrop.g, self.db.profile.Backdrop.b, ba or self.db.profile.Backdrop.a)
	end

end

function Skinner:removeRegions(frame, regions)

	if not frame then return end

-- self:Debug("removeRegions: [%s]", frame:GetName() or "???")

	for k, v in pairs({ frame:GetRegions() }) do
		-- if we have a list, hide the regions in that list
		-- otherwise, hide all regions of the frame
		if regions then
			for _, r in pairs(regions) do
				if k == r then v:SetAlpha(0) break end
			end
		else
--			self:Debug("remove region: [%s, %s]", k, v:GetName())
			v:SetAlpha(0)
		end
	end

end

function Skinner:keepRegions(frame, regions)

	if not frame then return end

-- 	self:Debug("keepRegions: [%s]", frame:GetName() or "???")

	for k, v in pairs({ frame:GetRegions() }) do
		-- if we have a list, hide the regions not in that list
		local keep = nil
		if regions then
			for _, r in pairs(regions) do
				if k == r then keep = true break end
			end
		end
		if not keep then
--			 self:Debug("remove region: [%s, %s]", k, v:GetName())
			v:SetAlpha(0)
		end
	end

end

function Skinner:skinScrollBar(scrollFrame, sbPrefix)

	if not scrollFrame then return end
--	self:Debug("skinScrollBar: [%s]", scrollFrame:GetName())

	local scrollBar = _G[scrollFrame:GetName()..(sbPrefix or "").."ScrollBar"]
	scrollBar:SetBackdrop({
		bgFile = "Interface\\ChatFrame\\ChatFrameBackground", tile = true, tileSize = 16,
		edgeFile = "Interface\\AddOns\\Skinner\\textures\\krsnik", edgeSize = 16,
		insets = {left = 4, right = 4, top = 4, bottom = 4},
	})
	scrollBar:SetBackdropBorderColor(.2, .2, .2, 1)
	scrollBar:SetBackdropColor(.1, .1, .1, 1)

end

function Skinner:moveObject(objName, xAdj, xDiff, yAdj, yDiff, relTo)

	if not objName then return end
-- self:Debug("moveObject: [%s, %s%s, %s%s, %s]", objName:GetName(), xAdj, xDiff, yAdj, yDiff, relTo)

	local point, relativeTo, relativePoint, xOfs, yOfs = objName:GetPoint()
-- self:Debug("GetPoint: [%s, %s, %s, %s, %s]", point, relativeTo:GetName(), relativePoint, xOfs, yOfs)

-- Workaround for yOfs crash when using bar addons
	if not yOfs then
		return
	end

	-- apply the adjustment
	if xAdj == nil then xOffset = xOfs else xOffset = (xAdj == "+" and xOfs + xDiff or xOfs - xDiff) end
	if yAdj == nil then yOffset = yOfs else yOffset = (yAdj == "+" and yOfs + yDiff or yOfs - yDiff) end

	if relTo == nil and relativeTo:GetName() then relTo = relativeTo:GetName() end

	objName:ClearAllPoints()
	objName:SetPoint(point, relTo, relativePoint, xOffset, yOffset)

end

function Skinner:setActiveTab(tabName)

	if not tabName then return end

--	self:Debug("setActiveTab : [%s]", tabName:GetName())

	tabName.tfade:SetTexture("Interface\\ChatFrame\\ChatFrameBackground")
	tabName.tfade:SetGradientAlpha(unpack(self.db.profile.Gradient.enable and gradientOn or gradientOff))

end

function Skinner:setInactiveTab(tabName)

	if not tabName then return end

--	self:Debug("setInactiveTab : [%s]", tabName:GetName())

	tabName.tfade:SetTexture("Interface\\AddOns\\Skinner\\textures\\inactive")
	tabName.tfade:SetGradientAlpha(unpack(self.db.profile.Gradient.enable and gradientOn or gradientTab))

end

function Skinner:skinCastingBar(frame)

	if not frame then return end

	if not frame.tfade then frame.tfade = frame:CreateTexture(nil, "BORDER") end
	frame.tfade:SetTexture("Interface\\ChatFrame\\ChatFrameBackground")

	frame.tfade:SetPoint("TOPLEFT", frame, "TOPLEFT", -1, 1)
	frame.tfade:SetPoint("BOTTOMRIGHT", frame, "BOTTOMRIGHT", 1, -1)

	frame.tfade:SetBlendMode("BLEND")
	frame.tfade:SetGradientAlpha(unpack(gradientCBar))

end

function Skinner:shrinkBag(frame, bpMF)

	if not frame then return end

	frameName = frame:GetName()

	local bgTop = _G[frameName.."BackgroundTop"]
	if math.floor(bgTop:GetHeight()) == 256 then -- this is the backpack
--		self:Debug("Backpack found")
		if bpMF then -- is this a backpack Money Frame
			local _, _, _, _, yOfs = _G[frameName.."MoneyFrame"]:GetPoint()
--			self:Debug("Backpack Money Frame found: [%s, %s]", yOfs, math.floor(yOfs))
			if math.floor(yOfs) == -216 or math.floor(yOfs) == -217 then -- is it still in its original position
--				self:Debug("Backpack Money Frame moved")
				self:moveObject(_G[frameName.."MoneyFrame"], nil, nil, "+", 18)
			end
			frame:SetHeight(frame:GetHeight() - 20)
		else
			self:moveObject(_G[frameName.."Item1"], nil, nil, "-", 20)
			frame:SetHeight(frame:GetHeight() - 40)
		end
	end
	if math.ceil(bgTop:GetHeight()) == 94 then frame:SetHeight(frame:GetHeight() - 20) end
	if math.ceil(bgTop:GetHeight()) == 86 then frame:SetHeight(frame:GetHeight() - 20) end
	if math.ceil(bgTop:GetHeight()) == 72 then frame:SetHeight(frame:GetHeight() + 2) end -- 6, 10 or 14 slot bag

	frame:SetWidth(frame:GetWidth() - 10)
	self:moveObject(_G[frameName.."Item1"], "+", 3, nil, nil)

	-- use default fade height
	local fh = self.db.profile.ContainerFrames.fheight <= math.ceil(frame:GetHeight()) and self.db.profile.ContainerFrames.fheight or math.ceil(frame:GetHeight())	

	if self.db.profile.FadeHeight.enable and self.db.profile.FadeHeight.force then
	-- set the Fade Height
	-- making sure that it isn't greater than the frame height
		fh = self.db.profile.FadeHeight.value <= math.ceil(frame:GetHeight()) and self.db.profile.FadeHeight.value or math.ceil(frame:GetHeight())
	end
-- self:Debug("sB - Frame, Fade Height: [%s, %s]", frame:GetName(), fh)

	if fh then frame.tfade:SetPoint("BOTTOMRIGHT", frame, "TOPRIGHT", -4, -fh) end

end

function Skinner:isVersion(addonName, verNoReqd, actualVerNo)
-- self:Debug("isVersion: [%s, %s, %s]", addonName, verNoReqd, actualVerNo)

	local hasMatched = false

	if type(verNoReqd) == "table" then
		for k, v in pairs(verNoReqd) do
			if v == actualVerNo then
				hasMatched = true
				break
			end
		end
	else
		if verNoReqd == actualVerNo then hasMatched = true end
	end

	if not hasMatched then self:CustomPrint(1, 0.25, 0.25, nil, nil, " ", "This version of", addonName, "is unsupported") end

	return hasMatched

end

function Skinner:skinEditBox(frame, regions, noSkin, noHeight)

	if not frame then return end

	local kRegions = { unpack(ebRegions) }
	if regions then
		for k, v in pairs(regions) do
			table.insert(kRegions, v)
		end
	end

	self:keepRegions(frame, kRegions)
	local l, r, t, b = frame:GetTextInsets()
	frame:SetTextInsets(l + 5, r + 5, t, b)
	if not (noHeight or frame:IsMultiLine()) then frame:SetHeight(26) end
	frame:SetWidth(frame:GetWidth() + 5)

	if not noSkin then
		frame:SetBackdrop({
			bgFile = "Interface\\ChatFrame\\ChatFrameBackground", tile = true, tileSize = 16,
			edgeFile = "Interface\\AddOns\\Skinner\\textures\\krsnik", edgeSize = 16,
			insets = {left = 4, right = 4, top = 4, bottom = 4},
		})
		frame:SetBackdropBorderColor(.2, .2, .2, 1)
		frame:SetBackdropColor(.1, .1, .1, 1)
	end

end

function Skinner:skinDropDown(frame)

	if not frame then return end
	
	local ddtl = _G[frame:GetName().."Left"]
	local ddtm = _G[frame:GetName().."Middle"]
	local ddtr = _G[frame:GetName().."Right"]
	local ddt = _G[frame:GetName().."Text"]
	local ddb = _G[frame:GetName().."Button"]
	ddtl:SetAlpha(0)
	ddtr:SetAlpha(0)
	ddtm:SetTexture("Interface\\AddOns\\Skinner\\textures\\inactive")
	ddtm:SetHeight(20)
	self:moveObject(ddt, "-", 7, "-", 2)
	self:moveObject(ddb, "-", 7, "-", 2)

end

function Skinner:getRegion(frame, regNo)

	if not frame then return end

	for k, v in pairs({ frame:GetRegions() }) do
		if k == regNo then return v end
	end

	return nil

end

function Skinner:getChild(frame, childNo)

	if not frame then return end

	for k, v in pairs({ frame:GetChildren() }) do
		if k == childNo then return v end
	end

	return nil

end

function Skinner:skinMoneyFrame(frame, moveGold)

	if not frame then return end

	for k, v in pairs({ "Gold", "Silver", "Copper" }) do
		local fName = _G[frame:GetName()..v]
		fName:SetWidth(fName:GetWidth() - 4)
		fName:SetHeight(fName:GetHeight() + 4)
		self:skinEditBox(fName, {9}, nil, true) -- N.B. region 9 is the icon
		if k ~= 1 or moveGold then
			self:moveObject(self:getRegion(fName, 9), "+", 10, nil, nil)
		end
		if k ~= 1 then
			fName:SetWidth(fName:GetWidth() + 5)
		end
	end

end

function Skinner:checkAddOn(addonName, LoD)
--	self:Debug("checkAddOn:[%s, %s, %s, %s]", addonName, LoD, IsAddOnLoaded(addonName), type(self[addonName]))

	-- check to see if AddonSkin is loaded when Addon is loaded
	if IsAddOnLoaded(addonName) == 1 and not LoD and type(self[addonName]) ~= "function" then
	 	if self.db.profile.Warnings then
			self:CustomPrint(1, 0, 0, nil, nil, nil, addonName, "loaded but skin not found in SkinMe directory")
		end
	end
	
	return IsAddOnLoaded(addonName) and type(self[addonName]) == "function"

end

function Skinner:OnTooltipUpdate()
	Tablet:SetHint(L["Right Click to display menu"])
end

function Skinner:updateSBTexture()

	local sbt = self.db.profile.SBTexture
	
	local sbNames = { PetPaperDollFrameExpBar, SkillDetailStatusBar, TradeSkillRankFrame, CraftRankFrame, ItemTextStatusBar }
	
	for k, v in pairs(sbNames) do
		v:SetStatusBarTexture(Surface:Fetch(sbt))
	end

	for i = 1 , NUM_FACTIONS_DISPLAYED do
		_G["ReputationBar"..i]:SetStatusBarTexture(Surface:Fetch(sbt))
	end
	for i = 1, SKILLS_TO_DISPLAY do
		_G["SkillRankFrame"..i]:SetStatusBarTexture(Surface:Fetch(sbt))
	end
	for i = 1, NUM_GROUP_LOOT_FRAMES do
		_G["GroupLootFrame"..i.."Timer"]:SetStatusBarTexture(Surface:Fetch(sbt))
	end

	if self.db.profile.MirrorTimers.glaze then 
		for i = 1, MIRRORTIMER_NUMTIMERS do
			_G["MirrorTimer"..i.."StatusBar"]:SetStatusBarTexture(Surface:Fetch(sbt))
		end 
	end

	if self.db.profile.Tooltips.glazesb then GameTooltipStatusBar:SetStatusBarTexture(Surface:Fetch(sbt)) end
	if self.db.profile.CastingBar.glaze then CastingBarFrame:SetStatusBarTexture(Surface:Fetch(sbt)) end
	if self.db.profile.MainMenuBar.glazesb then MainMenuExpBar:SetStatusBarTexture(Surface:Fetch(sbt)) end

end
