------------------------------------------------
--                 CT_BarMod                  --
--                                            --
-- Intuitive yet powerful action bar addon,   --
-- featuring per-button positioning as well   --
-- as scaling while retaining the concept of  --
-- grouped buttons and action bars.           --
-- Please do not modify or otherwise          --
-- redistribute this without the consent of   --
-- the CTMod Team. Thank you.                 --
------------------------------------------------

--------------------------------------------
-- Initialization

local _G = getfenv(0);
local module = _G.CT_BarMod;

--------------------------------------------
-- Group Management

local groupList = { };
local group = { };
local groupMeta = { __index = group };
module.groupList = groupList;

local function getGroup(id)
	return groupList[id] or group:new(id);
end

local groupFrameTable;
local function groupFrameSkeleton()
	if ( not groupFrameTable ) then
		groupFrameTable = {
			["button#hidden#i:button#st:LOW"] = {
				"backdrop#tooltip#0:0:0:0.5",
				"font#v:GameFontNormalLarge#i:text",
				["onleave"] = module.hideTooltip,
				["onmousedown"] = function(self, button)
					if ( button == "LeftButton" ) then
						module:moveMovable(self.movable);
					end
				end,
				["onmouseup"] = function(self, button)
					if ( button == "LeftButton" ) then
						module:stopMovable(self.movable);
					elseif ( button == "RightButton" ) then
						self.object:rotate();
					end
				end,
				["onload"] = function(self)
					self:SetBackdropBorderColor(1, 1, 1, 0);
				end,
				["onenter"] = function(self)
					local text = "|c00FFFFFFGroup "..self.object.id.."|r\n" ..
						"Left click to drag\n" ..
						"Right click to rotate";
					module:displayTooltip(self, text);
				end
			}
		};
	end
	return "frame#s:60:30", groupFrameTable;
end

-- Group Class

function group:new(id)
	local group = { };
	local frame = module:getFrame(groupFrameSkeleton);
	local movable = "GROUP"..id;
	setmetatable(group, groupMeta);
	
	group.orientation = module:getOption("orientation"..id);
	group.frame = frame;
	group.id = id;
	group:position();
	groupList[id] = group;
	
	local button = frame.button;
	button.movable = movable;
	button.object = group;
	module:registerMovable(movable, frame);
	return group;
end

local defaultPositions = {
	"BOTTOMLEFT", "BOTTOMLEFT", 233, 97, "ACROSS",
	"BOTTOMLEFT", "BOTTOMLEFT", 744, 97, "ACROSS",
	"BOTTOMLEFT", "TOPLEFT", -10, -85, "DOWN",
	"BOTTOMRIGHT", "BOTTOMRIGHT", 10, 605, "DOWN",
	"BOTTOMRIGHT", "BOTTOMRIGHT", -35, 605, "DOWN",
	
};
function group:position()
	local id = self.id;
	local frame = self.frame;
	local anchor, relanchor, x, y, orientation = select((id-1)*5 + 1, unpack(defaultPositions));
	
	frame:ClearAllPoints();
	frame:SetPoint(anchor, UIParent, relanchor, x, y);
	self:rotate(self.orientation or orientation);
end

function group:positionButtons()
	local objects = self.objects;
	if ( not objects ) then return; end
	
	local frame, button = self.frame;
	local offset = self.spacing or 6;
	if ( self.orientation == "DOWN" ) then
		for key, value in ipairs(objects) do
			button = value.button;
			button:ClearAllPoints();
			if ( key == 1 ) then
				button:SetPoint("TOP", frame, "BOTTOM", 0, -4);
			else
				button:SetPoint("TOP", objects[key-1].button, "BOTTOM", 0, -offset);
			end
		end
	else
		for key, value in ipairs(objects) do
			button = value.button;
			button:ClearAllPoints();
			if ( key == 1 ) then
				button:SetPoint("TOP", frame, "BOTTOM", 0, -4);
			else
				button:SetPoint("LEFT", objects[key-1].button, "RIGHT", offset, 0);
			end
		end
	end
	
	self:updateButtonPosition();
end

function group:addObject(object)
	local objects = self.objects;
	if ( not objects ) then
		objects = { };
		self.objects = objects;
	end
	
	local lastObject = objects[#objects];
	local button = object.button;
	local frame = self.frame;
	
	tinsert(objects, object);
	button:SetParent(frame);
	button:ClearAllPoints();
	if ( not lastObject ) then
		button:SetPoint("TOP", self.frame, "BOTTOM", 0, -4);
		return;
	end
	
	local lastButton = lastObject.button;
	local offset = self.spacing or 6;
	if ( self.orientation == "DOWN" ) then
		button:SetPoint("TOP", lastButton, "BOTTOM", 0, -offset);
	else
		button:SetPoint("LEFT", lastButton, "RIGHT", offset, 0);
	end
	button:SetScale(self.scale or 1);
	button:SetAlpha(self.opacity or 1);
	
	self:updateButtonPosition();
end

function group:updateButtonPosition()
	local button = self.frame.button;
	local objects = self.objects;
	if ( not objects ) then return; end
	
	button:ClearAllPoints();
	button:SetPoint("TOPLEFT", objects[1].button, -11, 11);
	button:SetPoint("BOTTOMRIGHT", objects[#objects].button, 11, -11);
	
	local text = button.text;
	text:ClearAllPoints();
	if ( self.orientation == "ACROSS" ) then
		text:SetPoint("BOTTOMLEFT", button, "TOPLEFT", 10, -5);
		text:SetText("Group "..self.id);
	else
		text:SetPoint("BOTTOM", button, "TOP", 0, -5);
		text:SetText("G"..self.id);
	end
end

function group:rotate(force)
	if ( force ) then
		self.orientation = force;
	else
		if ( self.orientation == "DOWN" ) then
			self.orientation = "ACROSS";
		else
			self.orientation = "DOWN";
		end
	end
	module:setOption("orientation"..self.id, self.orientation, true);
	self:positionButtons();
end

function group:show()
	self.frame:Show();
end

function group:hide()
	self.frame:Hide();
end

function group:toggleHeader(show)
	if ( show ) then
		self.frame.button:Show();
	else
		self.frame.button:Hide();
	end
end

function group:update(type, value)
	if ( type == "barScale" ) then
		self.scale = value;
		local objects = self.objects;
		if ( objects ) then
			for key, object in ipairs(objects) do
				object.button:SetScale(value);
			end
		end
	elseif ( type == "barOpacity" ) then
		self.opacity = value;
		local objects = self.objects;
		if ( objects ) then
			for key, object in ipairs(objects) do
				object.button:SetAlpha(value);
			end
		end
	elseif ( type == "barSpacing" ) then
		self.spacing = value;
		self:positionButtons();
	elseif ( type == "showGroup" ) then
		if ( value ) then
			self:show();
		else
			self:hide();
		end
	end
end

--------------------------------------------
-- Interface

function module:addObjectToGroup(object, id)
	local group = getGroup(id);
	group:addObject(object);
end