--[[ $Id: Bartender3_BarClass.lua 20367 2006-12-13 14:31:01Z nevcairiel $ ]]
local AceOO = AceLibrary("AceOO-2.0")
local dewdrop = AceLibrary("Dewdrop-2.0")

local L = AceLibrary("AceLocale-2.2"):new("Bartender3")

Bartender3.statemodifiers = { "ctrl", "alt", "shift" }

if not Bartender3.Class then Bartender3.Class = {} end
Bartender3.Class.Bar = AceOO.Class("AceEvent-2.0")
Bartender3.Class.ActionBar = AceOO.Class(Bartender3.Class.Bar)
Bartender3.Class.SpecialBar = AceOO.Class(Bartender3.Class.Bar, "AceHook-2.1")

function Bartender3.Class.Bar.prototype:init(id, config, statebar)
	Bartender3.Class.Bar.super.prototype.init(self)
	self.id = id
	self.config = config
	self.statebar = statebar
	self.barname = "Bar "..self.id
	if self.statebar and self.id == 1 then self.mainbar = true end
	
	self:CreateBarFrame()
	
	table.insert(Bartender3.bars, self)
	
end

function Bartender3.Class.Bar.prototype:ChangeProfile(config)
	self.config = config
end

function Bartender3.Class.Bar.prototype:CreateBarFrame()
	local name = "BT3Bar"..self.id
	local class = Bartender3.playerclass
	local frame = CreateFrame("Button", name, UIParent, self.statebar and "SecureStateDriverTemplate")
	frame:EnableMouse(false)
	frame:SetMovable(true)
	frame:RegisterForDrag("LeftButton")
	frame:RegisterForClicks("RightButtonDown", "LeftButtonUp")
	frame:SetWidth(10)
	frame:SetHeight(10)
	frame:SetBackdrop({bgFile = "Interface\\Tooltips\\UI-Tooltip-Background", tile = true, tileSize = 16, edgeFile = "Interface\\Tooltips\\UI-Tooltip-Border", edgeSize = 16, insets = {left = 5, right = 3, top = 3, bottom = 5},})
	frame:SetBackdropColor(0, 0, 0, 0)
	frame:SetBackdropBorderColor(0.5, 0.5, 0, 0)
	frame:ClearAllPoints()
	frame:SetPoint("CENTER", UIParent, "CENTER", 0, 0)
	frame.Text = frame:CreateFontString(nil, "ARTWORK")
	frame.Text:SetFontObject(GameFontNormal)
	frame.Text:SetText()
	frame.Text:Show()
	frame.Text:ClearAllPoints()
	frame.Text:SetPoint("CENTER", frame, "CENTER",0,0)
	frame:SetPoint("CENTER")
	if self.config.Hide then frame:Hide() else frame:Show() end
	self.frame = frame
end

function Bartender3.Class.Bar.prototype:UnlockFrames()
	local frame = self.frame
	frame:EnableMouse(true)
	frame:SetScript("OnEnter", function() this:SetBackdropBorderColor(0.5, 0.5, 0, 1) end)
	frame:SetScript("OnLeave", function() this:SetBackdropBorderColor(0, 0, 0, 0) end)
	frame:SetScript("OnDragStart", function()
		this:StartMoving()
		this:SetBackdropBorderColor(0, 0, 0, 0)
	end)
	frame:SetScript("OnDragStop", function()
		this:StopMovingOrSizing()
		self:SavePosition()
	end)
        frame:SetScript("OnClick", function(bself, button, down)
		if button == "RightButton" then
			self:ShowBarOptions()
		elseif button == "LeftButton" then
			self:ToggleVisibilty()
		end
	end)
	if self.config.Hide then
		frame:SetBackdropColor(1, 0, 0, 0.5)
	else
		frame:SetBackdropColor(0, 1, 0, 0.5)
	end
	frame:SetFrameLevel(3)
	frame.Text:SetText(self.barname)
	frame:Show()
	for i,v in pairs(self.buttons) do v:SetFrameLevel(2) end
end

function Bartender3.Class.Bar.prototype:LockFrames()
	local frame = self.frame
	frame:EnableMouse(false)
	frame:SetScript("OnEnter", nil)
	frame:SetScript("OnLeave", nil)
	frame:SetScript("OnDragStart", nil)
	frame:SetScript("OnDragStop", nil)
	frame:SetScript("OnClick", nil)
	frame:SetBackdropColor(0, 0, 0, 0)
	frame:SetBackdropBorderColor(0, 0, 0, 0)
	frame.Text:SetText("")
	frame:SetFrameLevel(1)
	if self.config.Hide then self.frame:Hide() else self.frame:Show() end
end

-- This function will not work in combat
function Bartender3.Class.Bar.prototype:ToggleVisibilty()
	if InCombatLockdown() then return end -- to avoid a message, you never know
	self.config.Hide = not self.config.Hide
	if self.config.Hide and Bartender3.unlock then
		self.frame:SetBackdropColor(1, 0, 0, 0.5)
	elseif self.config.Hide then
		self.frame:Hide()
	elseif Bartender3.unlock then
		self.frame:SetBackdropColor(0, 1, 0, 0.5)
	else
		self.frame:Show()
	end
end

function Bartender3.Class.Bar.prototype:RefreshLayout()
	if InCombatLockdown() then return end -- to avoid a message, you never know
	self:RefreshScale()
	self:RefreshButtonLayout()
	self:RefreshAlpha()
	self:LoadPosition()
	if self.config.Hide and not Bartender3.unlock and self.config.Enabled then self.frame:Hide() else self.frame:Show() end
end

function Bartender3.Class.Bar.prototype:LoadPosition()
	local x, y, s = self.config.PosX, self.config.PosY, self.frame:GetEffectiveScale()
	local defaultx, defaulty, defaultanchor = (Bartender3.defaultpositions[self.id].PosX) or 0, (Bartender3.defaultpositions[self.id].PosY) or 0, Bartender3.defaultpositions[self.id].Anchor or "BOTTOMLEFT"
	if x and y then
		x,y = x/s, y/s
	end
	self.frame:ClearAllPoints()
	self.frame:SetPoint("BOTTOMLEFT", UIParent, x and "BOTTOMLEFT" or defaultanchor, x or defaultx, y or defaulty)
end

function Bartender3.Class.Bar.prototype:SavePosition()
	local frame = self.frame
	local x,y = frame:GetLeft(), frame:GetBottom()
	local s = frame:GetEffectiveScale()
	x,y = x*s,y*s
	self.config.PosX = x
	self.config.PosY = y
end

function Bartender3.Class.Bar.prototype:RefreshButtonLayout()
	local buttons = #self.buttons
	local Rows = self.config.Rows
	local ButtonPerRow = math.floor(buttons / Rows + 0.5) -- just a precaution
	Rows = math.floor(buttons / ButtonPerRow + 0.5)
	local Padding = self.config.Padding
	self.frame:SetWidth(self.buttonwidth * ButtonPerRow + ((ButtonPerRow - 1) * Padding) + 8)
	self.frame:SetHeight(self.buttonheight * Rows + ((Rows - 1) * Padding) + 8)
	for i=1,buttons do
		local button = self.buttons[i]
		button:ClearAllPoints()
		if i > 1 and ((i-1) % ButtonPerRow) == 0 then
			button:SetPoint("TOPLEFT", self.buttons[i-ButtonPerRow], "BOTTOMLEFT", 0, self.specialmicromenu and -(Padding - 21) or -Padding)
		elseif i > 1 then
			button:SetPoint("TOPLEFT", self.buttons[i-1], "TOPRIGHT", Padding, 0)
		else
			button:SetPoint("TOPLEFT", self.frame, "TOPLEFT", 5, self.specialmicromenu and 18 or -3)
		end
	end
end

function Bartender3.Class.Bar.prototype:RefreshStyle()
	local style = self.config.Style
	for i,v in pairs(self.buttons) do
		Bartender3:RefreshStyle(v, self)
	end
end

function Bartender3.Class.Bar.prototype:RefreshScale()
	self.frame:SetScale(self.config.Scale)
	self:LoadPosition()
end

function Bartender3.Class.Bar.prototype:RefreshAlpha()
	for i,v in pairs(self.buttons) do
		if (v.class and HasAction(v.class:PagedID())) or not v.class then v:SetAlpha(self.config.Alpha) end
	end
end

function Bartender3.Class.Bar.prototype:InitOptions()
	self.optionstable = Bartender3:CreateBarOptions(self.id, self.barname, not self.statebar)
	if self.optionstable.args.rows then self.optionstable.args.rows.max = ( #self.buttons > 1 ) and #self.buttons or 2 end
end

-- config functions

function Bartender3.Class.Bar.prototype:SetRows(r)
	local maxbuttons = #self.buttons
	self.config.Rows = r
	self:RefreshButtonLayout()
end

function Bartender3.Class.Bar.prototype:SetPadding(r)
	self.config.Padding = r
	self:RefreshButtonLayout()
end

function Bartender3.Class.Bar.prototype:SetScale(s)
	self.config.Scale = s
	self:RefreshScale()
end

function Bartender3.Class.Bar.prototype:SetAlpha(a)
	self.config.Alpha = a
	self:RefreshAlpha()
end

function Bartender3.Class.Bar.prototype:SetStyle(s)
	self.config.Style = s
	self:RefreshStyle()
end

function Bartender3.Class.Bar.prototype:SetShowGrid(s)
	self.config.ShowGrid = s
	for i,v in pairs(self.buttonobjects) do
		local newalpha = 0
		if s then
			newalpha = self.config.Alpha / 2
		end
		if ( v.showgrid == 0 and not HasAction(v:PagedID()) ) then
			v.frame:SetAlpha(newalpha)
		end
	end
end

function Bartender3.Class.Bar.prototype:ShowBarOptions()
	if InCombatLockdown() then return end
	dewdrop:Open(self.frame, 'children', function() dewdrop:FeedAceOptionsTable(self.optionstable) end, 'cursorX', true, 'cursorY', true)
end


-- Cooldown count, based on the code from OmniCC
-- If you should mind that i use it here, please contact me and i will remove it :)
function Bartender3.Class.Bar.prototype:StartButtonCooldown(frame, start, duration, enable)
	if not frame or not frame.cooldownText then return end
	local cooldownText = frame.cooldownText
	if Bartender3.db.profile.ShowCooldown and ( start > 0 and duration > 3 and enable > 0 ) then
		cooldownText.start = start
		cooldownText.duration = duration
		cooldownText.timeToNextUpdate = 0
		cooldownText.onCooldown = true
	else
		cooldownText:SetText("")
		cooldownText.onCooldown = false
	end
end

-- call this function in your onupdate
function Bartender3.Class.Bar.prototype:UpdateButtonCooldown(frame, elapsed)
	if not frame or not frame.cooldownText then return end
	local cooldownText = frame.cooldownText
	if ( cooldownText.onCooldown ) then
		if cooldownText.timeToNextUpdate <= 0 then
			local remain = cooldownText.duration - (GetTime() - cooldownText.start)
			if floor(remain + 0.5) > 0 then
				local text, toNextUpdate, scale = BT3FormatTime(remain)
				cooldownText:SetFont(STANDARD_TEXT_FONT, 20 * scale, "OUTLINE")
				cooldownText:SetText( text )
				cooldownText.timeToNextUpdate = toNextUpdate
			else
				cooldownText:SetText("")
				cooldownText.onCooldown = false
			end
		else
			cooldownText.timeToNextUpdate = cooldownText.timeToNextUpdate - elapsed
		end
	end
end

--[[
	Action Bars
	
	ActionBar is the abstract class for all of the 10 actionbars
	self.buttons holds the actual frames
	self.buttonobjects holds the ButtonClass objects
]]

function Bartender3.Class.ActionBar.prototype:init(id, config, numbuttons)
	Bartender3.Class.ActionBar.super.prototype.init(self, id, config, true)
	self.numbuttons = numbuttons
	self.buttonwidth = 36
	self.buttonheight = 36
	self:CreateButtons()
	
	self:InitStates()
	self:InitOptions()
	
	self:RefreshStyle()
	self:UpdateSelfCast()
	self:RefreshLayout()
end

function Bartender3.Class.ActionBar.prototype:ChangeProfile(config)
	Bartender3.Class.ActionBar.super.prototype.ChangeProfile(self, config)
	
	self.numbuttons = self.config.Buttons
	self:SetButtons(self.numbuttons)
	self:UpdateStates()
	self:RefreshLayout()
end

function Bartender3.Class.ActionBar.prototype:CreateButtons()
	if self.buttons or self.buttonobjects then return end
	self.buttons, self.buttonobjects, self.keybindings = {}, {}, {}
	for i=1, self.numbuttons do
		local button = Bartender3:GetFreeButton(self)
		table.insert(self.buttons, button.frame)
		table.insert(self.buttonobjects, button)
	end
end

function Bartender3.Class.ActionBar.prototype:SetButtons(a)
	local oldbuttons = self.numbuttons
	self.numbuttons = a
	self.config.Buttons = a
	if a > oldbuttons then
		for i=(oldbuttons+1),a do
			local button = Bartender3:GetFreeButton(self)
			table.insert(self.buttons, button.frame)
			table.insert(self.buttonobjects, button)
			if Bartender3.unlock then button.frame:SetFrameLevel(2) end
		end
		self:UpdateStates()
	elseif a < oldbuttons then
		for i=(a+1), oldbuttons do
			-- fetch the last button
			local butnum = #self.buttonobjects
			-- release it
			self.buttonobjects[butnum]:ReleaseButton()
			table.remove(self.buttons)
			table.remove(self.buttonobjects)
		end
	else return end -- no change
	local maxbuttons = #self.buttons
	self.optionstable.args.rows.max = ( maxbuttons > 1 ) and maxbuttons or 2
	-- refresh rows config.
	if self.config.Rows > maxbuttons then self:SetRows(maxbuttons) else self:SetRows(self.config.Rows) end
	self:RefreshLayout()
	self:UpdateSelfCast()
end

function Bartender3.Class.ActionBar.prototype:RefreshLayout()
	Bartender3.Class.ActionBar.super.prototype.RefreshLayout(self)
	for i,v in pairs(self.buttonobjects) do
		v:UpdateButton()
	end
end

function Bartender3.Class.ActionBar.prototype:AddRightClickState(state)
	local scrc = Bartender3.db.profile.SelfCastRightClick
	local target = scrc and "player" or nil
	
	self.frame:SetAttribute("unit-S" .. state .. "2", target)
end

function Bartender3.Class.ActionBar.prototype:UpdateSelfCast()
	
	for i,v in pairs(self.buttons) do
		v:SetAttribute("checkselfcast", Bartender3.db.profile.SelfCastModifier)
	end
	
	self:UpdateStates()
	
end

function Bartender3.Class.ActionBar.prototype:SetModifierState(modifier, page)
	if not self.config.States then return end
	self.config.States[modifier] = (page==0) and nil or page
	self:RefreshStatemap()
end

function Bartender3.Class.ActionBar.prototype:SetStancePage(stance, page)
	self.config.Stances[stance] = page
	self:RefreshStatemap()
end

--[[
	Special Bars
	
	Special Bars are an abstraction layer for the MicroMenu/Bags/Stance/Pet Bar
	The table self.buttons holds the actual frames of the buttons.
]]

function Bartender3.Class.SpecialBar.prototype:init(bartype, barname, config)
	Bartender3.Class.SpecialBar.super.prototype.init(self, bartype, config, false)
	self.bartype = bartype
	self.barname = barname
	self:AssignFrames(bartype)
	
	self:InitOptions()
	
	self:RefreshLayout()
	self:RefreshStyle()
end

function Bartender3.Class.SpecialBar.prototype:ChangeProfile(config)
	Bartender3.Class.ActionBar.super.prototype.ChangeProfile(self, config)
	
	self.buttons = nil
	self:AssignFrames(self.bartype)
	self:RefreshLayout()
end

function Bartender3.Class.SpecialBar.prototype:AssignFrames(bartype)
	if self.buttons then return end
	self.buttons = {}
	if not bartype then bartype = self.bartype end
	if ( bartype == "STANCE" ) then
		self.buttonwidth = ShapeshiftButton1:GetWidth()
		self.buttonheight = ShapeshiftButton1:GetHeight()
		for i=1,10 do 
			local button = getglobal("ShapeshiftButton"..i)
			button:SetParent(self.frame)
			button:SetNormalTexture("")
			
			button.icon = getglobal("ShapeshiftButton"..i.."Icon")
			
			table.insert(self.buttons, button) 
		end
	elseif ( bartype == "PET" ) then
		self.buttonwidth = PetActionButton1:GetWidth()
		self.buttonheight = PetActionButton1:GetHeight()
		for i=1,10 do 
			local button = getglobal("PetActionButton"..i)
			button:SetParent(self.frame)
			button:SetNormalTexture("")
			
			button.icon = getglobal("PetActionButton"..i.."Icon")
			
			table.insert(self.buttons, button) 
		end
		self:PetBar_OnInit()
	elseif ( bartype == "MICROMENU" ) then
		self.buttonwidth = 29
		self.buttonheight = 37
		self.specialmicromenu = true
		table.insert(self.buttons, CharacterMicroButton)
		table.insert(self.buttons, SpellbookMicroButton)
		table.insert(self.buttons, TalentMicroButton)
		UpdateTalentButton = function() return end
		table.insert(self.buttons, QuestLogMicroButton)
		table.insert(self.buttons, SocialsMicroButton)
		table.insert(self.buttons, LFGMicroButton)
		table.insert(self.buttons, MainMenuMicroButton)
		table.insert(self.buttons, HelpMicroButton)
		
		for i,v in pairs(self.buttons) do v:SetParent(self.frame); v:Show(); end
	elseif ( bartype == "BAGS" ) then
		self.buttonwidth = MainMenuBarBackpackButton:GetWidth()
		self.buttonheight = MainMenuBarBackpackButton:GetHeight()
		
		if self.config.Keyring then
			table.insert(self.buttons, KeyRingButton)
		else
			KeyRingButton:Hide()
		end
		
		if not self.config.OneBag then
			table.insert(self.buttons, CharacterBag3Slot) 
			table.insert(self.buttons, CharacterBag2Slot) 
			table.insert(self.buttons, CharacterBag1Slot) 
			table.insert(self.buttons, CharacterBag0Slot)
		else
			CharacterBag3Slot:Hide()
			CharacterBag2Slot:Hide()
			CharacterBag1Slot:Hide()
			CharacterBag0Slot:Hide()
		end
	 	MainMenuBarBackpackButton:SetScript("OnClick", function() OpenAllBags() end)
		table.insert(self.buttons, MainMenuBarBackpackButton)
		for i,v in pairs(self.buttons) do 
			v.icon = getglobal(v:GetName().."IconTexture")
			v:SetParent(self.frame)
			if v ~= KeyRingButton then v:SetNormalTexture("") end
			v:Show()
		end
	end
end

function Bartender3.Class.SpecialBar.prototype:PetBar_OnInit()
	self:SecureHook("PetActionBar_UpdateCooldowns", function () self:PetActionBar_UpdateCooldowns() end)
	self.elapsed = 0
	self.frame:SetScript("OnUpdate", function(bself, elapsed) 
		self.elapsed = self.elapsed + elapsed
		if self.elapsed > 0.5 then
			self:Pet_OnUpdate(self.elapsed)
			self.elapsed = 0
		end
	end )
	
	for i, frame in pairs(self.buttons) do
		frame.cooldown = getglobal(frame:GetName().."Cooldown")
		frame.cooldownText = frame:CreateFontString(nil, "OVERLAY")
		frame.cooldownText:SetPoint("CENTER", frame, "CENTER", 0, 1)
		frame.cooldownText:SetFont(STANDARD_TEXT_FONT, 20, "OUTLINE")
		frame.cooldownText:SetTextColor(1, 1, 0.2)
		frame.cooldownText:Show()
		frame.cooldown:SetFrameLevel(frame.cooldown:GetFrameLevel()-1)
	end
end

function Bartender3.Class.SpecialBar.prototype:Pet_OnUpdate(elapsed)
	if self.config.Hide then return end
	for i, frame in pairs(self.buttons) do
		self:UpdateButtonCooldown(frame, elapsed)
	end
end

function Bartender3.Class.SpecialBar.prototype:PetActionBar_UpdateCooldowns()
	for i, frame in pairs(self.buttons) do
		local start, duration, enable = GetPetActionCooldown(i)
		self:StartButtonCooldown(frame, start, duration, enable)
	end
end

function Bartender3.Class.SpecialBar.prototype:ToggleKeyring()
	if self.bartype ~= "BAGS" then return end
	self.config.Keyring = not self.config.Keyring
	self.buttons = nil
	self:AssignFrames(self.bartype)
	self:RefreshLayout()
	self:RefreshStyle()
end

function Bartender3.Class.SpecialBar.prototype:ToggleOneBag()
	if self.bartype ~= "BAGS" then return end
	self.config.OneBag = not self.config.OneBag
	self.buttons = nil
	self:AssignFrames(self.bartype)
	self:RefreshLayout()
	self:RefreshStyle()
	self.optionstable.args.rows.max = ( #self.buttons > 1 ) and #self.buttons or 2
end

BINDING_HEADER_BT3ADDITIONAL = "Bartender3 Additional Bars"