--[[ActionBar
Responsible for the functionality of an ActionBar. To add support for different types of icons, first the ActionBarIconDragDropTarget class needs to be updated.
You can then catch the event of the icon being dropped, and deal with it accordingly. Currently supported are: EntityIcons and ActionBarIcons.
]]--
ActionBar = {}

--ActionBarDefaultAction is required for handling the default action taken when an action bar icon is executed, either by clicking or hotkeys.
loadScript("ActionBarDefaultAction.lua")

--If the slot passed contains an action bar icon, then we remove it.
--@param slot The slot we want to remove any icons from.
function ActionBar:removeExistingIcon(slot)
	--If an icon exists in that slot, then delete and replace.
	if slot:getActionBarIcon() then
		slot:getActionBarIcon():setSlot(nil)
		slot:notifyIconDraggedOff(slot:getActionBarIcon())
		self.actionBarIconManager:destroyIcon(actionBarIcon)
	end
end

--Add a slot to the action bar. Handles adding slots to both horizontal and vertical action bars.
--We also handle the drop events received by the slot here.	
function ActionBar:addSlot()
	local yPosition = 0
	local xPosition = 0
	--If we have a horizontal action bar, we add a slot to the right of the drag bar.
	if self.layout == "Horiz" then
		yPosition = math.floor(self.slotcounter / self.maxSlots)
		xPosition = self.slotcounter - math.floor(self.slotcounter / self.maxSlots) * self.maxSlots
	else
		--If we have a vertical action bar, we add a slot below the drag bar.
		yPosition = self.slotcounter - math.floor(self.slotcounter / self.maxSlots) * self.maxSlots
		xPosition = math.floor(self.slotcounter / self.maxSlots)
	end

	self.slotcounter = self.slotcounter + 1
	local slot = self.actionBarIconManager:createSlot(self.iconSize)
	slot:getWindow():setPosition(CEGUI.UVector2.new(CEGUI.UDim.new(0, self.iconSize * xPosition), CEGUI.UDim.new(0, self.iconSize * yPosition)))
	slot:getWindow():setAlpha(0.6)
	--We want to present an outline of the slot to the user.
	slot:getWindow():setProperty("FrameEnabled", "true")
	slot:getWindow():setProperty("BackgroundEnabled", "true")
	slot:getWindow():setTooltipText("Drag and drop an entity from your inventory to attach it to this action bar slot.")
	self.iconContainer:addChild(slot:getWindow())

	local slotWrapper = { slot = slot }

	table.insert(self.slots, slotWrapper)

	--Handle an entity icon dropped on the action bar slot
	--We create a new action bar icon based on the entity icon
	slotWrapper.entityIconDropped = function(entityIcon)
		--If we have an existing icon in the slot, we remove it.
		self:removeExistingIcon(slotWrapper.slot)

		--Create a new ActionBarIcon from the existing entityIcon.
		local newIconWrapper = self:createActionBarIconFromEntity(entityIcon:getEntity())
		slotWrapper.slot:addActionBarIcon(newIconWrapper.actionBarIcon)
	end

	--Handle an action bar icon dropped on the action bar slot.
	slotWrapper.actionBarIconDropped = function(actionBarIcon)
		local oldSlot = actionBarIcon:getSlot()
		if oldSlot ~= slotWrapper.slot then
			slotWrapper.slot:addActionBarIcon(actionBarIcon)
			if oldSlot then
				self:removeExistingIcon(oldSlot)
			end
		end
	end

	slotWrapper.entityIconDropped_connector = slot.EventActionBarIconDropped:connect(slotWrapper.actionBarIconDropped)
	slotWrapper.entityIconDropped_connector = slot.EventEntityIconDropped:connect(slotWrapper.entityIconDropped)
	return slotWrapper
end

--Create a new action bar icon based on an entity.
function ActionBar:createActionBarIconFromEntity(entity)
	local icon = guiManager:getIconManager():getIcon(self.iconSize, entity)
	if icon then
		--We use the name to set the tooltip text
		local name = entity:getType():getName() .. " (" .. entity:getId() .. " : " .. entity:getName() .. ")"
		local actionBarIconWrapper = { actionBarIcon = nil,
									   defaultAction = nil,
									   type = "entity",
									   entityid = nil }
		self:createActionBarIcon(actionBarIconWrapper, icon)
		actionBarIconWrapper.actionBarIcon:setTooltipText(name)
		--Our command object for the default action is the entity.
		actionBarIconWrapper.defaultAction:initFromEntityIcon(entity:getId())
		actionBarIconWrapper.entityid = entity:getId()

		self.icons[entity:getId()] = actionBarIconWrapper
		return actionBarIconWrapper
	else
		return nil
	end
end



--Generic code all action bar icons require.
--@param actionBarIconWrapper Contains
--@param icon Generic icon to use for the layout.
function ActionBar:createActionBarIcon(actionBarIconWrapper, icon)
	--Create and attach an empty default action for the icon.
	actionBarIconWrapper.defaultAction = ActionBarDefaultAction.new(self.defaultActionList)
	--Based on the generic icon passed, we create an action bar icon.
	actionBarIconWrapper.actionBarIcon = self.actionBarIconManager:createIcon(icon, self.iconSize)
	actionBarIconWrapper.actionBarIcon:getImage():setProperty("InheritsAlpha", "false")
	actionBarIconWrapper.actionBarIcon:getImage():setAlpha(1.0)

	actionBarIconWrapper.mouseEnters = function()
		actionBarIconWrapper.actionBarIcon:getImage():setProperty("FrameEnabled", "true")
		return true
	end
	actionBarIconWrapper.mouseLeaves = function()
		actionBarIconWrapper.actionBarIcon:getImage():setProperty("FrameEnabled", "false")
		return true
	end

	--Execute the default action of the icon on a mouse button click.
	actionBarIconWrapper.windowClick = function()
		actionBarIconWrapper.defaultAction:executeAction()
		return true
	end
	actionBarIconWrapper.actionBarIcon:getDragContainer():subscribeEvent("MouseClick", actionBarIconWrapper.windowClick)

	actionBarIconWrapper.actionBarIcon:getDragContainer():subscribeEvent("MouseEntersSurface", actionBarIconWrapper.mouseEnters)
	actionBarIconWrapper.actionBarIcon:getDragContainer():subscribeEvent("MouseLeavesSurface", actionBarIconWrapper.mouseLeaves)
end

--Saves action bar properties such as position and icons.
function ActionBar:saveAttr()
	self.actionBarIconManager:saveValue(self.avatarId, self.name, self.name)

	self.actionBarIconManager:saveValue(self.avatarId, self.name .. "position", CEGUI.PropertyHelper.uvector2ToString(self.widget:getMainWindow():getPosition()))

	for k, v in pairs(self.slots) do
		local aBarIcon = v.slot:getActionBarIcon()
		if aBarIcon then
			for _, v2 in pairs(self.icons) do
				if aBarIcon == v2.actionBarIcon then
					self.actionBarIconManager:saveValue(self.avatarId, self.name .. k, v2.type)
					self.actionBarIconManager:saveValue(self.avatarId, self.name .. k .. "id", v2.entityid)
				end
			end
		else
			self.actionBarIconManager:eraseValue(self.avatarId, self.name .. k)
			self.actionBarIconManager:eraseValue(self.avatarId, self.name .. k .. "id")
		end
	end
end

--Loads saved properties for the action bar. 
--We call this at the initialization of the action bar.
function ActionBar:loadSavedAttributes()
	if self.actionBarIconManager:getSavedValue(self.avatarId, self.name) ~= "null" then
		local position = "" .. self.actionBarIconManager:getSavedValue(self.avatarId, self.name .. "position") .. ""

		if position ~= "" then
			self.widget:getMainWindow():setPosition(CEGUI.PropertyHelper.stringToUVector2(position))
		end
	end

	for k, v in pairs(self.slots) do
		local type = self.actionBarIconManager:getSavedValue(self.avatarId, self.name .. k)
		local id = self.actionBarIconManager:getSavedValue(self.avatarId, self.name .. k .. "id")
		if type == "entity" then
			self.entityCandidates[id] = v.slot
			--Keep track of the size, because lua doesn't have a function for getting the size of dictionaries
			--without looping through all elements.
			self.entityCandidates.size = self.entityCandidates.size + 1
		end
	end

	self.entityCandidates.AddedEntityToInventory = function(entity)
		local id = entity:getId()
		if self.entityCandidates[id] then
			local newIconWrapper = self:createActionBarIconFromEntity(entity)
			self.entityCandidates[id]:addActionBarIcon(newIconWrapper.actionBarIcon)
			self.entityCandidates[id] = nil
			self.entityCandidates.size = self.entityCandidates.size - 1
		end
		if self.entityCandidates.size == 0 then
			self.entityCandidates.AddedEntityToInventory_connector:disconnect()
		end
	end

	--Here's a timer which removes the entity listener after 10 seconds. It's currently disabled since we've refactored the timeout functionality in Eris 1.4.
	--We should however look into doing this differently than using a timer. We should instead listen to when all of the entity's inventory has been loaded after the initial sight.
	--	self.timer = Eris.Timeout.new(10000)
	--	self.timer.disconnect = function()
	--		self.entityCandidates.AddedEntityToInventory_connector:disconnect()
	--	end
	--
	--	self.timer.disconnect_connector = self.timer.Expired:connect(self.timer.disconnect)
	self.entityCandidates.AddedEntityToInventory_connector = emberOgre:getWorld():getAvatar().EventAddedEntityToInventory:connect(self.entityCandidates.AddedEntityToInventory)

end

--Build the action bar widget.
--@param widgetName Unique widget name passed by AcionBarCreator.
function ActionBar:buildCEGUIWidget(widgetName)
	self.widget = guiManager:createWidget()
	if widgetName == nil then
		widgetName = "ActionBar"
	end
	self.name = widgetName
	self.widget:loadMainSheet("ActionBar.layout", widgetName)
	self.iconContainer = self.widget:getWindow("IconContainer")

	local slotSize = (self.maxSlots * self.iconSize) + (self.maxSlots * 2)
	self.dragBar = self.widget:getWindow("TitleBar")
	--Need to disable double clicks as the TitleBar treats the parent window as a FrameWindow.
	self.dragBar:setWantsMultiClickEvents(false)

	if self.layout == "Horiz" then
		self.widget:getMainWindow():setSize(CEGUI.USize.new(CEGUI.UDim.new(0.0, slotSize), CEGUI.UDim.new(0.0, self.iconSize)))
		--Place it at the bottom of the screen
		self.widget:getMainWindow():setYPosition(CEGUI.UDim.new(1.0, -self.iconSize))
		self.widget:getMainWindow():setXPosition(CEGUI.UDim.new(0.5, -(slotSize / 2.0)))
	else
		self.widget:getMainWindow():setSize(CEGUI.USize.new(CEGUI.UDim.new(0.0, self.iconSize), CEGUI.UDim.new(0.0, slotSize + 20)))
		--Drag bar needs to be resized to the top for vertical action bars.
		self.dragBar:setSize(CEGUI.USize.new(CEGUI.UDim.new(1.0, 0.0), CEGUI.UDim.new(0.0, 12.0)))
		--Need to shift the icon container down so that our drag bar doesn't overlap.
		self.iconContainer:setPosition(CEGUI.UVector2.new(CEGUI.UDim.new(0.0, 0.0), CEGUI.UDim.new(0.0, 12.0)))
	end
	self.widget:show()

	self.worldDragDrop = Ember.OgreView.Gui.ActionBarIconDragDropTarget.new(root)

	--Dragging an icon from the action bar to the world, and releasing it, should cause that icon to be destroyed.
	self.worldDragDrop_Dropped = function(actionBarIcon)
		if actionBarIcon and actionBarIcon:getSlot() then
			self:removeExistingIcon(actionBarIcon:getSlot())
		end
	end
	self.worldDragDrop_Dropped_connector = self.worldDragDrop.EventActionBarIconDropped:connect(self.worldDragDrop_Dropped)

	--Create our slots in the ActionBar.
	for _ = 1, self.maxSlots do
		self:addSlot()
	end
end

--Input caught by ActionBarInput is handled here.
--@param key The hotkey pressed.
function ActionBar:gotInput(key)
	local slotNum = self.hotkeys[key].slotNum
	local actionBarIcon = self.slots[slotNum].slot:getActionBarIcon()
	for _, v in pairs(self.icons) do
		if actionBarIcon == v.actionBarIcon and v.defaultAction then
			v.defaultAction:executeAction()
		end
	end
end

--Create a new action bar.
--@param rotation The rotation of the action bar, either horizontal or vertical.
function ActionBar.new(rotation, defActionList, erisAvatar)
	local actionbar = { iconSize = 50,
						maxSlots = 5,
						iconcounter = 0,
						slotcounter = 0,
						inputHelper = nil,
						iconContainer = nil,
						icons = {},
						connectors = {},
						widget = nil,
						layout = rotation, --Vertical or horizontal action bar
						defaultActionList = defActionList,
						hotkeys = {},
						name = "",
						entityCandidates = { size = 0 },
						timer = nil,
						slots = {} };
	serverInfo = Eris.ServerInfo.new()
	erisAvatar:getConnection():getServerInfo(serverInfo)
	actionbar.avatarId = Ember.OgreView.Gui.ActionBarIconManager.AvatarIdType.new(serverInfo, erisAvatar:getId())

	setmetatable(actionbar, { __index = ActionBar })
	return actionbar
end

--The default hotkey mapping of an actionbar. Only the first action bar created should respond to these.
function ActionBar:defaultKeyMapping()
	self:keyMapping("1", 1)
	self:keyMapping("2", 2)
	self:keyMapping("3", 3)
	self:keyMapping("4", 4)
	self:keyMapping("5", 5)
end

--Given a key and the slot number in the action bar, we create a hotkey mapping.
--@param key The key which we wish to use as a hotkey.
--@param slotNum The slot number executed by the key press.
function ActionBar:keyMapping(key, slotNum)
	local input = {}
	input.key = Ember.OgreView.Gui.ActionBarInput.new(key)
	input.slotNum = slotNum
	self.hotkeys[key] = input

	local slotWrapper = self.slots[slotNum]
	if slotWrapper then
		local numWindow = self.widget:createWindow("EmberLook/StaticText")
		numWindow:setProperty("FrameEnabled", "false");
		numWindow:setProperty("BackgroundEnabled", "false");
		numWindow:setProperty("VertFormatting", "TopAligned");
		numWindow:setText(key)
		numWindow:setPosition(CEGUI.UVector2.new(CEGUI.UDim.new(0, 1), CEGUI.UDim.new(0, 1)))
		numWindow:setHeight(CEGUI.UDim.new(0, 20))
		numWindow:setMousePassThroughEnabled(true)
		slotWrapper.slot:getWindow():addChild(numWindow)
	end

	connect(self.connectors, input.key.EventGotHotkeyInput, self.gotInput, self)
end

--Initialization of the action bar, we build the widget here.
function ActionBar:init(widgetName)
	self.actionBarIconManager = guiManager:getActionBarIconManager()

	self:buildCEGUIWidget(widgetName)
	self:loadSavedAttributes()

	connect(self.connectors, emberOgre:getWorld():getAvatar().EventRemovedEntityFromInventory, function(entity)
		local actionBarIconWrapper = self.icons[entity:getId()]
		if actionBarIconWrapper then
			actionBarIconWrapper.actionBarIcon:setSlot(nil)
			self.actionBarIconManager:destroyIcon(actionBarIconWrapper.actionBarIcon)
			self.icons[entity:getId()] = nil
		end
	end)

end

function ActionBar:shutdown()

	--Save action bar properties
	self:saveAttr()

	--Delete all of the action bar slots.
	for _, v in pairs(self.slots) do
		self.actionBarIconManager:destroySlot(v.slot)
	end

	--Delete all of the action bar icons.
	for _, v in pairs(self.icons) do
		self.actionBarIconManager:destroyIcon(v.actionBarIcon)
	end

	disconnectAll(self.connectors)
	guiManager:destroyWidget(self.widget)
	collectgarbage()

end
