CURRENT_ACTIONBAR_PAGE = 1;
NUM_ACTIONBAR_PAGES = 6;
NUM_ACTIONBAR_BUTTONS = 12;
ATTACK_BUTTON_FLASH_TIME = 0.4;

BOTTOMLEFT_ACTIONBAR_PAGE = 6;
BOTTOMRIGHT_ACTIONBAR_PAGE = 5;
LEFT_ACTIONBAR_PAGE = 4;
RIGHT_ACTIONBAR_PAGE = 3;
RANGE_INDICATOR = "●";

-- Table of actionbar pages and whether they're viewable or not
VIEWABLE_ACTION_BAR_PAGES = {1, 1, 1, 1, 1, 1};

function ActionButtonDown(id)
	local button;
	if BonusActionBarFrame:IsShown() then
		button = getglobal("BonusActionButton"..id);
	else
		button = getglobal("ActionButton"..id);
	end
	if button:GetButtonState() == "NORMAL" then
		button:SetButtonState("PUSHED");
	end
end

function ActionButtonUp(id)
	local button;
	if BonusActionBarFrame:IsShown() then
		button = getglobal("BonusActionButton"..id);
	else
		button = getglobal("ActionButton"..id);
	end
	if button:GetButtonState() == "PUSHED" then
		button:SetButtonState("NORMAL");
		SecureActionButton_OnClick(button, "LeftButton");
		ActionButton_UpdateState(button);
	end
end

function ActionBar_PageUp()
	local nextPage;
	for i=GetActionBarPage() + 1, NUM_ACTIONBAR_PAGES do
		if VIEWABLE_ACTION_BAR_PAGES[i] then
			nextPage = i;
			break;
		end
	end

	if not nextPage then
		nextPage = 1;
	end
	ChangeActionBarPage(nextPage);
end

function ActionBar_PageDown()
	local prevPage;
	for i=GetActionBarPage() - 1, 1, -1 do
		if VIEWABLE_ACTION_BAR_PAGES[i] then
			prevPage = i;
			break;
		end
	end
	
	if not prevPage then
		for i=NUM_ACTIONBAR_PAGES, 1, -1 do
			if VIEWABLE_ACTION_BAR_PAGES[i] then
				prevPage = i;
				break;
			end
		end
	end
	ChangeActionBarPage(prevPage);
end

function ActionButton_OnLoad (self)
	self.flashing = 0;
	self.flashtime = 0;
	self:SetAttribute("showgrid", 0);
	self:SetAttribute("type", "action");
	self:SetAttribute("checkselfcast", true);
	self:SetAttribute("checkfocuscast", true);
	self:SetAttribute("useparent-unit", true);
	self:SetAttribute("useparent-actionpage", true);
	self:RegisterForDrag("LeftButton", "RightButton");
	self:RegisterForClicks("AnyUp");
	self:RegisterEvent("PLAYER_ENTERING_WORLD");
	self:RegisterEvent("ACTIONBAR_SHOWGRID");
	self:RegisterEvent("ACTIONBAR_HIDEGRID");
	self:RegisterEvent("ACTIONBAR_PAGE_CHANGED");
	self:RegisterEvent("ACTIONBAR_SLOT_CHANGED");
	self:RegisterEvent("UPDATE_BINDINGS");
	ActionButton_UpdateAction(self);
	ActionButton_UpdateHotkeys(self, self.buttonType);
end

function ActionButton_UpdateHotkeys (self, actionButtonType)
    if not actionButtonType then
        actionButtonType = "ACTIONBUTTON";
    end

    local hotkey = getglobal(self:GetName().."HotKey");
    local key = GetBindingKey(actionButtonType..self:GetID()) or
                GetBindingKey("CLICK "..self:GetName()..":LeftButton");

	local text = GetBindingText(key, "KEY_", 1);
    if text == "" then
        hotkey:SetText(RANGE_INDICATOR);
        hotkey:SetPoint("TOPLEFT", self, "TOPLEFT", 1, -2);
        hotkey:Hide();
    else
        hotkey:SetText(text);
        hotkey:SetPoint("TOPLEFT", self, "TOPLEFT", -2, -2);
        hotkey:Show();
    end
end

function ActionButton_CalculateAction (self, button)
	if not button then
		button = SecureButton_GetEffectiveButton(self);
	end
	if self:GetID() > 0 then
		local page = SecureButton_GetModifiedAttribute(self, "actionpage", button);
		if not page then
			page = GetActionBarPage();
			if self.isBonus and (page == 1 or self.alwaysBonus) then
				local offset = GetBonusBarOffset();
				if offset == 0 and BonusActionBarFrame and BonusActionBarFrame.lastBonusBar then
					offset = BonusActionBarFrame.lastBonusBar;
				end
				page = NUM_ACTIONBAR_PAGES + offset;
			end
		end
		return (self:GetID() + ((page - 1) * NUM_ACTIONBAR_BUTTONS));
	else
		return SecureButton_GetModifiedAttribute(self, "action", button) or 1;
	end
end

function ActionButton_UpdateAction (self)
	local action = ActionButton_CalculateAction(self);
	if action ~= self.action then
		self.action = action;
		ActionButton_Update(self);
	end
end

function ActionButton_Update (self)
	-- Special case code for bonus bar buttons
	-- Prevents the button from updating if the bonusbar is still in an animation transition
	if self.isBonus and self.inTransition then
		self.needsUpdate = true;
		ActionButton_UpdateUsable(self);
		return;
	end
	
	local name = self:GetName();

	local action = self.action;
	local icon = getglobal(name.."Icon");
	local buttonCooldown = getglobal(name.."Cooldown");
	local texture = GetActionTexture(action);	
	
	if HasAction(action) then
		if not self.eventsRegistered then
			self:RegisterEvent("ACTIONBAR_UPDATE_STATE");
			self:RegisterEvent("ACTIONBAR_UPDATE_USABLE");
			self:RegisterEvent("ACTIONBAR_UPDATE_COOLDOWN");
			self:RegisterEvent("UPDATE_INVENTORY_ALERTS");
			self:RegisterEvent("PLAYER_TARGET_CHANGED");
			self:RegisterEvent("TRADE_SKILL_SHOW");
			self:RegisterEvent("TRADE_SKILL_CLOSE");
			self:RegisterEvent("PLAYER_ENTER_COMBAT");
			self:RegisterEvent("PLAYER_LEAVE_COMBAT");
			self:RegisterEvent("START_AUTOREPEAT_SPELL");
			self:RegisterEvent("STOP_AUTOREPEAT_SPELL");
			self:RegisterEvent("UNIT_ENTERED_VEHICLE");
			self:RegisterEvent("UNIT_EXITED_VEHICLE");
			self:RegisterEvent("COMPANION_UPDATE");
			self.eventsRegistered = true;
		end

		if not self:GetAttribute("statehidden") then
			self:Show();
		end
		ActionButton_UpdateState(self);
		ActionButton_UpdateUsable(self);
		ActionButton_UpdateCooldown(self);
		ActionButton_UpdateFlash(self);
	else
		if self.eventsRegistered then
			self:UnregisterEvent("ACTIONBAR_UPDATE_STATE");
			self:UnregisterEvent("ACTIONBAR_UPDATE_USABLE");
			self:UnregisterEvent("ACTIONBAR_UPDATE_COOLDOWN");
			self:UnregisterEvent("UPDATE_INVENTORY_ALERTS");
			self:UnregisterEvent("PLAYER_TARGET_CHANGED");
			self:UnregisterEvent("TRADE_SKILL_SHOW");
			self:UnregisterEvent("TRADE_SKILL_CLOSE");
			self:UnregisterEvent("PLAYER_ENTER_COMBAT");
			self:UnregisterEvent("PLAYER_LEAVE_COMBAT");
			self:UnregisterEvent("START_AUTOREPEAT_SPELL");
			self:UnregisterEvent("STOP_AUTOREPEAT_SPELL");
			self:UnregisterEvent("UNIT_ENTERED_VEHICLE");
			self:UnregisterEvent("UNIT_EXITED_VEHICLE");
			self:UnregisterEvent("COMPANION_UPDATE");
			self.eventsRegistered = nil;
		end

		if self:GetAttribute("showgrid") == 0 then
			self:Hide();
		else
			buttonCooldown:Hide();
		end
	end

	-- Add a green border if button is an equipped item
	local border = getglobal(name.."Border");
	if IsEquippedAction(action) then
		border:SetVertexColor(0, 1.0, 0, 0.35);
		border:Show();
	else
		border:Hide();
	end

	-- Update Macro Text
	local macroName = getglobal(name.."Name");
	if not IsConsumableAction(action) and not IsStackableAction(action) then
		macroName:SetText(GetActionText(action));
	else
		macroName:SetText("");
	end

	-- Update icon and hotkey text
	if texture then
		icon:SetTexture(texture);
		icon:Show();
		self.rangeTimer = -1;
		self:SetNormalTexture("Interface\\Buttons\\UI-Quickslot2");
	else
		icon:Hide();
		buttonCooldown:Hide();
		self.rangeTimer = nil;
		self:SetNormalTexture("Interface\\Buttons\\UI-Quickslot");
		local hotkey = getglobal(name.."HotKey");
        if hotkey:GetText() == RANGE_INDICATOR then
			hotkey:Hide();
		else
			hotkey:SetVertexColor(0.6, 0.6, 0.6);
		end
	end
	ActionButton_UpdateCount(self);	
	
	-- Update tooltip
	if GameTooltip:GetOwner() == self then
		ActionButton_SetTooltip(self);
	end

	self.feedback_action = action;
end

function ActionButton_ShowGrid (button)
	assert(button);
	
	if issecure() then
		button:SetAttribute("showgrid", button:GetAttribute("showgrid") + 1);
	end
	
	getglobal(button:GetName().."NormalTexture"):SetVertexColor(1.0, 1.0, 1.0, 0.5);

	if button:GetAttribute("showgrid") >= 1 and not button:GetAttribute("statehidden") then
		button:Show();
	end
end

function ActionButton_HideGrid (button)	
	assert(button);
	
	local showgrid = button:GetAttribute("showgrid");
	
	if issecure() then
		if showgrid > 0 then
			button:SetAttribute("showgrid", showgrid - 1);
		end
	end
	
	if button:GetAttribute("showgrid") == 0 and not HasAction(button.action) then
		button:Hide();
	end
end

function ActionButton_UpdateState (button)
	assert(button);
	
	local action = button.action;
	if IsCurrentAction(action) or IsAutoRepeatAction(action) then
		button:SetChecked(1);
	else
		button:SetChecked(0);
	end
end

function ActionButton_UpdateUsable (self)
	local name = self:GetName();
	local icon = getglobal(name.."Icon");
	local normalTexture = getglobal(name.."NormalTexture");
	local isUsable, notEnoughMana = IsUsableAction(self.action);
	if isUsable then
		icon:SetVertexColor(1.0, 1.0, 1.0);
		normalTexture:SetVertexColor(1.0, 1.0, 1.0);
	elseif notEnoughMana then
		icon:SetVertexColor(0.5, 0.5, 1.0);
		normalTexture:SetVertexColor(0.5, 0.5, 1.0);
	else
		icon:SetVertexColor(0.4, 0.4, 0.4);
		normalTexture:SetVertexColor(1.0, 1.0, 1.0);
	end
end

function ActionButton_UpdateCount (self)
	local text = getglobal(self:GetName().."Count");
	local action = self.action;
	if IsConsumableAction(action) or IsStackableAction(action) then
		text:SetText(GetActionCount(action));
	else
		text:SetText("");
	end
end

function ActionButton_UpdateCooldown (self)
	local cooldown = getglobal(self:GetName().."Cooldown");
	local start, duration, enable = GetActionCooldown(self.action);
	CooldownFrame_SetTimer(cooldown, start, duration, enable);
end

function ActionButton_OnEvent (self, event, ...)
	local arg1 = ...;
	if event == "ACTIONBAR_SLOT_CHANGED" then
		if arg1 == 0 or arg1 == self.action then
			ActionButton_Update(self);
		end
		return;
	end
	if event == "PLAYER_ENTERING_WORLD" then
		ActionButton_Update(self);
		return;
	end
	if event == "ACTIONBAR_PAGE_CHANGED" or event == "UPDATE_BONUS_ACTIONBAR" then
		ActionButton_UpdateAction(self);
		return;
	end
	if event == "ACTIONBAR_SHOWGRID" then
		ActionButton_ShowGrid(self);
		return;
	end
	if event == "ACTIONBAR_HIDEGRID" then
		ActionButton_HideGrid(self);
		return;
	end
	if event == "UPDATE_BINDINGS" then
		ActionButton_UpdateHotkeys(self, self.buttonType);
		return;
	end

	-- All event handlers below this line are only set when the button has an action

	if event == "PLAYER_TARGET_CHANGED" then
		self.rangeTimer = -1;
	elseif ( (event == "ACTIONBAR_UPDATE_STATE") or
		((event == "UNIT_ENTERED_VEHICLE" or event == "UNIT_EXITED_VEHICLE") and (arg1 == "player")) or
		((event == "COMPANION_UPDATE") and (arg1 == "MOUNT")) ) then
		ActionButton_UpdateState(self);
	elseif event == "ACTIONBAR_UPDATE_USABLE" then
		ActionButton_UpdateUsable(self);
	elseif event == "ACTIONBAR_UPDATE_COOLDOWN" then
		ActionButton_UpdateCooldown(self);
	elseif event == "TRADE_SKILL_SHOW" or event == "TRADE_SKILL_CLOSE" then
		ActionButton_UpdateState(self);
	elseif event == "PLAYER_ENTER_COMBAT" then
		if IsAttackAction(self.action) then
			ActionButton_StartFlash(self);
		end
	elseif event == "PLAYER_LEAVE_COMBAT" then
		if IsAttackAction(self.action) then
			ActionButton_StopFlash(self);
		end
	elseif event == "START_AUTOREPEAT_SPELL" then
		if IsAutoRepeatAction(self.action) then
			ActionButton_StartFlash(self);
		end
	elseif event == "STOP_AUTOREPEAT_SPELL" then
		if ActionButton_IsFlashing(self) and not IsAttackAction(self.action) then
			ActionButton_StopFlash(self);
		end
	end
end

function ActionButton_SetTooltip (self)
	if GetCVar("UberTooltips") == "1" then
		GameTooltip_SetDefaultAnchor(GameTooltip, self);
	else
		local parent = self:GetParent();
		if parent == MultiBarBottomRight or parent == MultiBarRight or parent == MultiBarLeft then
			GameTooltip:SetOwner(self, "ANCHOR_LEFT");
		else
			GameTooltip:SetOwner(self, "ANCHOR_RIGHT");
		end
	end
	if GameTooltip:SetAction(self.action) then
		self.UpdateTooltip = ActionButton_SetTooltip;
	else
		self.UpdateTooltip = nil;
	end
end

function ActionButton_OnUpdate (self, elapsed)
	if ActionButton_IsFlashing(self) then
		local flashtime = self.flashtime;
		flashtime = flashtime - elapsed;
		
		if flashtime <= 0 then
			local overtime = -flashtime;
			if overtime >= ATTACK_BUTTON_FLASH_TIME then
				overtime = 0;
			end
			flashtime = ATTACK_BUTTON_FLASH_TIME - overtime;

			local flashTexture = getglobal(self:GetName().."Flash");
			if flashTexture:IsShown() then
				flashTexture:Hide();
			else
				flashTexture:Show();
			end
		end
		
		self.flashtime = flashtime;
	end
	
	-- Handle range indicator
	local rangeTimer = self.rangeTimer;
	if rangeTimer then
		rangeTimer = rangeTimer - elapsed;

		if rangeTimer <= 0 then
			local count = getglobal(self:GetName().."HotKey");
			local valid = IsActionInRange(self.action);
			if count:GetText() == RANGE_INDICATOR then
				if valid == 0 then
					count:Show();
					count:SetVertexColor(1.0, 0.1, 0.1);
				elseif valid == 1 then
					count:Show();
					count:SetVertexColor(0.6, 0.6, 0.6);
				else
					count:Hide();
				end
			else
				if valid == 0 then
					count:SetVertexColor(1.0, 0.1, 0.1);
				else
					count:SetVertexColor(0.6, 0.6, 0.6);
				end
			end
			rangeTimer = TOOLTIP_UPDATE_TIME;
		end
		
		self.rangeTimer = rangeTimer;
	end
end

function ActionButton_GetPagedID (self)
    return self.action;
end

function ActionButton_UpdateFlash (self)
	local action = self.action;
	if (IsAttackAction(action) and IsCurrentAction(action)) or IsAutoRepeatAction(action) then
		ActionButton_StartFlash(self);
	else
		ActionButton_StopFlash(self);
	end
end

function ActionButton_StartFlash (self)
	self.flashing = 1;
	self.flashtime = 0;
	ActionButton_UpdateState(self);
end

function ActionButton_StopFlash (self)
	self.flashing = 0;
	getglobal(self:GetName().."Flash"):Hide();
	ActionButton_UpdateState (self);
end

function ActionButton_IsFlashing (self)
	if self.flashing == 1 then
		return 1;
	end
	
	return nil;
end
