local _, env = ...;
local FadeIn, FadeOut = env.db.Alpha.FadeIn, env.db.Alpha.FadeOut;

-------------------------------------------
---		Watch bar container
-------------------------------------------
local WBC = env.bar.WatchBarContainer

local XP_BAR_TEXTURE_NORMAL   = ([[Interface\AddOns\ConsolePort_Bar\Textures\XPBar]])
local XP_BAR_TEXTURE_INVERTED = ([[Interface\AddOns\ConsolePort_Bar\Textures\XPBar_Inverted]])

WBC.BGLeft = WBC:CreateTexture(nil, 'BACKGROUND')
WBC.BGLeft:SetPoint('TOPLEFT')
WBC.BGLeft:SetPoint('BOTTOMRIGHT', WBC, 'BOTTOM', 0, 0)
WBC.BGLeft:SetColorTexture(0, 0, 0, 1)
CPAPI.SetGradient(WBC.BGLeft, 'HORIZONTAL', 0, 0, 0, 0, 0, 0, 0, 1)

WBC.BGRight = WBC:CreateTexture(nil, 'BACKGROUND')
WBC.BGRight:SetPoint('TOPRIGHT')
WBC.BGRight:SetPoint('BOTTOMLEFT', WBC, 'BOTTOM', 0, 0)
WBC.BGRight:SetColorTexture(0, 0, 0, 1)
CPAPI.SetGradient(WBC.BGRight, 'HORIZONTAL', 0, 0, 0, 1, 0, 0, 0, 0)

local MAX_BARS_VISIBLE = 2

function WBC:SetTextLocked(isLocked)
	if ( self.textLocked ~= isLocked ) then
		self.textLocked = isLocked
		self:UpdateBarVisibility() 
	end
end

function WBC:GetNumberVisibleBars()
	local numVisBars = 0 
	for i, bar in ipairs(self.bars) do
		if (bar:ShouldBeVisible()) then
			numVisBars = numVisBars + 1
		end
	end	
	return math.min(MAX_BARS_VISIBLE, numVisBars) 
end

function WBC:IsTextLocked()
	return self.textLocked
end	

function WBC:UpdateBarVisibility()
	for i, bar in ipairs(self.bars) do
		if(bar:ShouldBeVisible()) then
			bar:UpdateTextVisibility()
		end
	end	
end

function WBC:SetBarAnimation(Animation)
	for i, bar in ipairs(self.bars) do
		bar.StatusBar:SetDeferAnimationCallback(Animation) 
	end
end

function WBC:UpdateBarTicks()
	for i, bar in ipairs(self.bars) do
		if(bar:ShouldBeVisible()) then
			bar:UpdateTick()
		end
	end
end

function WBC:ShowVisibleBarText()
	for i, bar in ipairs(self.bars) do
		if(bar:ShouldBeVisible()) then
			bar:ShowText()
		end
	end
end

function WBC:HideVisibleBarText()
	for i, bar in ipairs(self.bars) do
		if(bar:ShouldBeVisible()) then
			bar:HideText()
		end
	end
end

function WBC:SetBarSize(largeSize)
	self.largeSize = largeSize 
	self:UpdateBarsShown() 
end

function WBC:UpdateBarsShown()
	local visibleBars = {}
	for i, bar in ipairs(self.bars) do
		if ( bar:ShouldBeVisible() ) then
			table.insert(visibleBars, bar)
		end
	end
		
	table.sort(visibleBars, function(left, right) return left:GetPriority() < right:GetPriority() end)
	self:LayoutBars(visibleBars) 
end

function WBC:HideStatusBars()
	self.SingleBarSmall:Hide() 
	self.SingleBarLarge:Hide()
	self.SingleBarSmallUpper:Hide()
	self.SingleBarLargeUpper:Hide()
	for i, bar in ipairs(self.bars) do
		bar:Hide() 
	end
end

function WBC:SetInitialBarSize()
	self.barHeight = self.SingleBarLarge:GetHeight()
end 

function WBC:GetInitialBarHeight()
	return self.barHeight 
end

-- Sets the bar size depending on whether the bottom right multi-bar is shown. 
-- If the multi-bar is shown, a different texture needs to be displayed that is smaller. 
function WBC:SetDoubleBarSize(bar, width)
	local textureHeight = self:GetInitialBarHeight() 
	local statusBarHeight = textureHeight - 0
	if( self.largeSize ) then 
		self.SingleBarLargeUpper:SetSize(width, statusBarHeight) 
		self.SingleBarLargeUpper:SetPoint('CENTER', bar, 0, 4)
		self.SingleBarLargeUpper:Show()
		
		self.SingleBarLarge:SetSize(width, statusBarHeight) 
		self.SingleBarLarge:SetPoint('CENTER', bar, 0, -9)
		self.SingleBarLarge:Show() 
	else		
		self.SingleBarSmallUpper:SetSize(width, statusBarHeight) 
		self.SingleBarSmallUpper:SetPoint('CENTER', bar, 0, 4)
		self.SingleBarSmallUpper:Show() 
		
		self.SingleBarSmall:SetSize(width, statusBarHeight) 
		self.SingleBarSmall:SetPoint('CENTER', bar, 0, -9)
		self.SingleBarSmall:Show() 
	end

	local progressWidth = width - self:GetEndCapWidth() * 2
	bar.StatusBar:SetSize(progressWidth, statusBarHeight)
	bar:SetSize(progressWidth, statusBarHeight)
end

--Same functionality as previous function except shows only one bar. 
function WBC:SetSingleBarSize(bar, width) 
	local textureHeight = self:GetInitialBarHeight()
	if( self.largeSize ) then  
		self.SingleBarLarge:SetSize(width, textureHeight) 
		self.SingleBarLarge:SetPoint('CENTER', bar, 0, 0)
		self.SingleBarLarge:Show() 
	else
		self.SingleBarSmall:SetSize(width, textureHeight) 
		self.SingleBarSmall:SetPoint('CENTER', bar, 0, 0)
		self.SingleBarSmall:Show() 
	end
	local progressWidth = width - self:GetEndCapWidth() * 2
	bar.StatusBar:SetSize(progressWidth, textureHeight)
	bar:SetSize(progressWidth, textureHeight)
end

function WBC:LayoutBar(bar, barWidth, isTopBar, isDouble)
	bar:Update()
	bar:Show()
	bar:ClearAllPoints()
	
	if ( isDouble ) then
		if ( isTopBar ) then
			bar:SetPoint('BOTTOM', self:GetParent(), 0, 18)
			bar.StatusBar.BarTexture:SetTexture(XP_BAR_TEXTURE_INVERTED)
		else
			bar:SetPoint('BOTTOM', self:GetParent(), 0, 0)
			bar.StatusBar.BarTexture:SetTexture(XP_BAR_TEXTURE_NORMAL)
		end
		self:SetDoubleBarSize(bar, barWidth)
	else 
		bar:SetPoint('BOTTOM', self:GetParent(), 0, 0)
		bar.StatusBar.BarTexture:SetTexture(XP_BAR_TEXTURE_NORMAL)
		self:SetSingleBarSize(bar, barWidth)
	end
end

function WBC:SetMainBarColor(r, g, b)
	if self.mainBar then
		self.mainBar:SetBarColorRaw(r, g, b)
	end
end

function WBC:LayoutBars(visBars)
	local width = self:GetWidth()
	self:HideStatusBars()

	local TOP_BAR, IS_DOUBLE = true, true
	if ( #visBars > 1 ) then
		self:LayoutBar(visBars[1], width, not TOP_BAR, IS_DOUBLE)
		self:LayoutBar(visBars[2], width, TOP_BAR, IS_DOUBLE)
	elseif( #visBars == 1 ) then 
		self:LayoutBar(visBars[1], width, TOP_BAR, not IS_DOUBLE)
	end
	self.mainBar = visBars and visBars[1]
	self:UpdateBarTicks()
end


function WBC:OnLoad()
	self.bars = {}

	for _, event in ipairs({
		'UPDATE_FACTION',
		'ENABLE_XP_GAIN',
		'DISABLE_XP_GAIN',
		'CVAR_UPDATE',
		'UPDATE_EXPANSION_LEVEL',
		'PLAYER_ENTERING_WORLD',
		'HONOR_XP_UPDATE',
		'ZONE_CHANGED',
		'ZONE_CHANGED_NEW_AREA',
		'UNIT_INVENTORY_CHANGED',
		'ARTIFACT_XP_UPDATE',
	}) do 
		pcall(self.RegisterEvent, self, event)
	end

	self:RegisterUnitEvent('UNIT_LEVEL', 'player')
	self:SetInitialBarSize()
	self:UpdateBarsShown()

	self:SetScript('OnShow', self.OnShow)
	self:SetScript('OnEvent', self.OnEvent)
end

function WBC:OnEvent(event)
	if ( event == 'CVAR_UPDATE' ) then
		self:UpdateBarVisibility()
	end	
	self:UpdateBarsShown() 
end

function WBC:OnShow()
	if env.cfg and env.cfg.watchbars then
		FadeIn(self, 0.2, self:GetAlpha(), 1)
	else
		self:SetAlpha(0)
	end
end

function WBC:GetEndCapWidth()
	return self.endCapWidth
end

function WBC:SetEndCapWidth(width)
	self.endCapWidth = width
end

local function BarColorOverride(self)
	if (env.cfg and env.cfg.expRGB) and (WBC.mainBar == self) then
		self:SetBarColorRaw(unpack(env.cfg.expRGB))
	end
end

local function BarColorRaw(self, r, g, b, a)
	self.StatusBar.BarTexture:SetVertexColor(r, g, b, a)
end

function WBC:AddBarFromTemplate(frameType, template, showPredicate, getPriority)
	local bar = CreateFrame(frameType, nil, self, template)
	local statusBar = bar.StatusBar;
	table.insert(self.bars, bar)
	statusBar.Background:Hide()
	statusBar.BarTexture:SetTexture(XP_BAR_TEXTURE_NORMAL)
	statusBar.BarTexture:SetSnapToPixelGrid(true)
	statusBar.BarTexture:SetTexelSnappingBias(0)
	bar.SetBarColorRaw = bar.SetBarColor or BarColorRaw;
	bar.ShouldBeVisible = bar.ShouldBeVisible or showPredicate or nop;
	bar.GetPriority = bar.GetPriority or getPriority;

	bar:HookScript('OnEnter', function()
		FadeIn(self, 0.2, self:GetAlpha(), 1)
	end)

	bar:HookScript('OnLeave', function()
		if (env.cfg and not env.cfg.watchbars) or not env.cfg then
			FadeOut(self, 0.2, self:GetAlpha(), 0)
		end
	end)

	bar:HookScript('OnShow', BarColorOverride)
	if bar.SetBarColor then
		hooksecurefunc(bar, 'SetBarColor', BarColorOverride)
	end

	self:UpdateBarsShown()
	return bar
end

WBC:OnLoad()

if CPAPI.IsRetailVersion then
	-- See FrameXML\StatusTrackingManager.lua
	local BarsEnum = {
		None       = -1,
		Reputation = 1,
		Honor      = 2,
		Artifact   = 3,
		Experience = 4,
		Azerite    = 5,
	}
	local BarPriorities = {
		[BarsEnum.Azerite]    = 0;
		[BarsEnum.Reputation] = 1;
		[BarsEnum.Honor]      = 2;
		[BarsEnum.Artifact]   = 3;
		[BarsEnum.Experience] = 4;
	}

	local CanShowBar, GetBarPriority = StatusTrackingManagerMixin.CanShowBar, StatusTrackingManagerMixin.GetBarPriority;
	local __ = function(f, ...) return GenerateClosure(f, WBC, ...) end;

	WBC:AddBarFromTemplate('FRAME', 'CP_ReputationStatusBarTemplate', __(CanShowBar, BarsEnum.Reputation), __(GetBarPriority, BarPriorities[BarsEnum.Reputation]))
	WBC:AddBarFromTemplate('FRAME', 'HonorStatusBarTemplate',         __(CanShowBar, BarsEnum.Honor),      __(GetBarPriority, BarPriorities[BarsEnum.Honor]))
	WBC:AddBarFromTemplate('FRAME', 'ArtifactStatusBarTemplate',      __(CanShowBar, BarsEnum.Artifact),   __(GetBarPriority, BarPriorities[BarsEnum.Artifact]))
	WBC:AddBarFromTemplate('FRAME', 'AzeriteBarTemplate',             __(CanShowBar, BarsEnum.Azerite),    __(GetBarPriority, BarPriorities[BarsEnum.Azerite]))
	WBC:AddBarFromTemplate('FRAME', 'CP_ExpStatusBarTemplate',        __(CanShowBar, BarsEnum.Experience), __(GetBarPriority, BarPriorities[BarsEnum.Experience]))

else
	WBC:AddBarFromTemplate('FRAME', 'CP_ReputationStatusBarTemplate')
	WBC:AddBarFromTemplate('FRAME', 'CP_ExpStatusBarTemplate')
	--xpBar.ExhaustionLevelFillBar:SetTexture([[Interface\AddOns\ConsolePortBar\Textures\XPBar]])
end