require 'scripts/gui/gui_element.lua'

--------------------------------------------------------------------------------
-- GUISlider Object script
--! States
--! * idle
--!
--! Attributes
--! * listener : script component that listens to the element events
--! * clickable : area for click detection. If not present, it is automatically
--!               created.
--! * autoAdjustClickableArea : flag that determines automatic adjusment of the 
--!                             clickable area according to the currrent visual.
--! * idleVisual: visual for idle state
--! * thumbElement: script component of the thumb node
--! * direction: either "horizontal" or "vertical"
--! * currentValue: initial value
--! * dragThreshold: minimum move value in pixels to start thumb manipulation
--! * increment: value offset when direction keys are pressed
--!
--! Events
--! * onSliderDragStart(slider, current_value)
--! * onSliderReleased(slider, current_value)
--! * onSliderValueChange(slider, newValue)
--!
--------------------------------------------------------------------------------

GUISlider = class(GUIElement)

--------------------------------------------------------------------------------
-- start
--! @brief Callback when the object is added to the world
--------------------------------------------------------------------------------
function GUISlider:start()
	
	GUI:debugPrint("[" .. tostring(self) .. "]\tGUISlider:start()", "runtime")
	
	if self.thumbElement == nil then
		GUI:error(" GUISlider does not have a reference to a thumb element")
		return
	else
		self.thumbElement = ScriptComponent_getScriptTable(self.thumbElement)
		if self.thumbElement.idleVisual ~= nil then
			self.thumbElementWidth, self.thumbElementHeight = VisualComponent_getSize(self.thumbElement.idleVisual)
		end
		
		self:enforceListenerToSelf(self.thumbElement, "GUISlider", "Thumb")
	end
	
	if self.direction == nil then
		self.direction = "horizontal"
	end
	
	if self.dragThreshold == nil then
		self.dragThreshold = 4
	end
	
	if self.increment == nil then
		self.increment = 0.02
	end

	if self.states == nil then
		self.states = GUIElement.element_states
	end
	
	self:setupClickableArea()
	
	GUIElement.start(self)
	
	if self.currentValue == nil then
		self.currentValue = 0
	end
	
	self:setValue(self.currentValue)
	
	self.isDragEngaged = false
	self.isDragging = false

end

--------------------------------------------------------------------------------
-- sendCommand
--! @brief Function to handle a gui command sent by the GUI system to the element
--! @param command command to handle
--------------------------------------------------------------------------------
function GUISlider:sendCommand(command)
	
	GUI:debugPrint("[" .. tostring(self) .. "]\tGUISlider:sendCommand() " .. tostring(command.id), "commands")
	
	if command.id == GUI_COMMAND_FOCUS or command.id == GUI_COMMAND_UNFOCUS then
		return self.thumbElement:sendCommand(command)
	elseif command.id == GUI_COMMAND_CLICK then
		
		if command.posX and command.posY then
			local localPosX, localPosY = WorldNode_worldToLocalPosition(self.worldNode, command.posX, command.posY)
			self:moveThumb(localPosX, localPosY)
			
			if self.listener ~= nil and self.listener.onSliderValueChange ~= nil then
				self.listener:onSliderValueChange(self, self.currentValue)
			end
		end
		return self.thumbElement:sendCommand(command)
		
	elseif command.id == GUI_COMMAND_DRAG then
		local localPosX, localPosY = WorldNode_worldToLocalPosition(self.worldNode, command.posX, command.posY)
		
		if not self.isDragging then
			if not self.isDragEngaged then
				local localPrevPosX, localPrevPosY = WorldNode_worldToLocalPosition(self.worldNode, command.previousPosX, command.previousPosY)

				self.isDragEngaged = true
				local isHorizontalDrag = math.abs(localPosX - localPrevPosX) > math.abs(localPosY - localPrevPosY)
				
				if self.direction == "horizontal" and not isHorizontalDrag or
				   self.direction == "vertical" and isHorizontalDrag then
				   	return false
				end
				
				self.isDragging = true
				
				if self.listener ~= nil and self.listener.onSliderDragStart ~= nil then
					self.listener:onSliderDragStart(self, self.currentValue)
				end
				
			end	
		else
			self:moveThumb(localPosX, localPosY)
			
			if self.listener ~= nil and self.listener.onSliderValueChange ~= nil then
				self.listener:onSliderValueChange(self, self.currentValue)
			end
			return true
		end
	elseif command.id == GUI_COMMAND_RELEASE then
		GUI:debugPrint("[" .. tostring(self) .. "]\tGUISlider release ", "actions")
		if self.listener ~= nil and self.listener.onSliderReleased ~= nil then
			self.listener:onSliderReleased(self, self.currentValue)
		end
		self.isDragEngaged = false
		self.isDragging = false
		return self.thumbElement:sendCommand(command)
	elseif command.id == GUI_COMMAND_LEFT and self.direction == "horizontal" and self.currentValue > 0.0 then
		GUI:debugPrint("[" .. tostring(self) .. "]\tGUISlider Key left " .. tostring(self), "actions")
		self:setValue(self.currentValue - self.increment)
		return true
	elseif command.id == GUI_COMMAND_RIGHT and self.direction == "horizontal" and self.currentValue < 1.0 then
		GUI:debugPrint("[" .. tostring(self) .. "]\tGUISlider Key right " .. tostring(self), "actions")
		self:setValue(self.currentValue + self.increment)
		return true
	elseif command.id == GUI_COMMAND_UP and self.direction == "vertical" and self.currentValue > 0.0 then
		GUI:debugPrint("[" .. tostring(self) .. "]\tGUISlider Key up " .. tostring(self), "actions")
		self:setValue(self.currentValue - self.increment)
		return true
	elseif command.id == GUI_COMMAND_DOWN and self.direction == "vertical" and self.currentValue < 1.0 then
		GUI:debugPrint("[" .. tostring(self) .. "]\tGUISlider Key down " .. tostring(self), "actions")
		self:setValue(self.currentValue + self.increment)
		return true
	-- elseif command.id == GUI_COMMAND_RELEASE then
		
	else
		return GUIElement.sendCommand(self, command)
	end
	
end

--------------------------------------------------------------------------------
-- onButtonPress
--! @brief Callback when a button is pressed
--! @param button Button that is pressed
--------------------------------------------------------------------------------
function GUISlider:onButtonPress(button, posX, posY)
	
	if not posX or not posY then
		return
	end
	
	if button == self.thumbElement then
		GUI:debugPrint("[" .. tostring(self) .. "]\tGUISlider -> thumb pressed @ " .. posX .. "," .. posY, "callbacks")
		local thumbPosX, thumbPosY = WorldNode_getLocalPosition(self.thumbElement.worldNode)
		sliderPosX = thumbPosX + posX
		sliderPosY = thumbPosY + posY
		
		if not self.thumbPressedStarted then
			self.thumbPressedStarted = true
			
			--Component_disable(self.thumbElement.clickable)
			
			self.thumbPressedStartPosX = sliderPosX
			self.thumbPressedStartPosY = sliderPosY
		elseif not self.thumbDragStarted then
			if self.direction == "horizontal" and math.abs(self.thumbPressedStartPosX - sliderPosX) > self.dragThreshold then
				self.thumbDragStarted = true
			elseif self.direction == "vertical" and math.abs(self.thumbPressedStartPosY, sliderPosY) > self.dragThreshold then
				self.thumbDragStarted = true
			end
		else
			self:moveThumb(sliderPosX, sliderPosY)
			
			if self.listener ~= nil and self.listener.onSliderValueChange ~= nil then
				self.listener:onSliderValueChange(self, self.currentValue)
			end

			-- self:sendCommand({id = GUI_COMMAND_PRESS, posX = sliderPosX, posY = sliderPosY })
		end
	end
	
end

--------------------------------------------------------------------------------
-- onButtonRelease
--! @brief Callback when a button has been released
--! @param button Button that is released
--------------------------------------------------------------------------------
function GUISlider:onButtonRelease(button)
	if button == self.thumbElement then
		GUI:debugPrint("[" .. tostring(self) .. "]\tGUISlider -> Thumb released", "callbacks")
		--Component_enable(self.thumbElement.clickable)
		
		self.thumbPressedStarted = false
		self.thumbDragStarted = false
	end
end

--------------------------------------------------------------------------------
-- setValue
--! @brief Changes the slider value position
--! @param newValue value between 0 an 1
--------------------------------------------------------------------------------
function GUISlider:setValue(newValue)
	
	self.currentValue = Math.clamp(newValue, 0, 1)
	
	local width, height = ClickableComponent_getBoxShapeSize(self.clickable)
	
	if self.direction == "horizontal" then
		local posX = width * self.currentValue - width / 2
		WorldNode_setLocalPosition(self.thumbElement.worldNode, posX, 0)
	elseif self.direction == "vertical" then
		local posY = height * self.currentValue - height / 2
		WorldNode_setLocalPosition(self.thumbElement.worldNode, 0, posY)
	end
	
end

--------------------------------------------------------------------------------
-- moveThumb
--! @brief Moves the thumb object within the slider area
--! @param posX slider local position X
--! @param posY slider local position Y
--------------------------------------------------------------------------------
function GUISlider:moveThumb(posX, posY)
	local width, height = ClickableComponent_getBoxShapeSize(self.clickable)
	if self.direction == "horizontal" then
		posX = Math.clamp(posX, -width / 2, width / 2)
		WorldNode_setLocalPosition(self.thumbElement.worldNode, posX, 0)
		self.currentValue = (posX + width / 2) / width
	elseif self.direction == "vertical" then
		posY = Math.clamp(posY, -height / 2, height / 2)
		WorldNode_setLocalPosition(self.thumbElement.worldNode, 0, posY)
		self.currentValue = (posY + height / 2) / height
	end
end

