-- ************************************ GestureEvent ************************************ --
GestureEvent = deriveLuaClass("GestureEvent", "Event")

-- ###### Configuration variables ######
-- GestureEvent.sequenceStart = nil		-- animation when event starts
-- GestureEvent.sequenceThanks = nil		-- animation when you find 1 item
-- GestureEvent.sequenceDone = nil		-- animation when you find all items
-- GestureEvent.sequenceFail = nil		-- sequence for when the task time ends and you don't have all of the items
GestureEvent.gestures = {}
GestureEvent.doGestureObject = ""
GestureEvent.afterGesturesObject = ""
GestureEvent.eventBarImage = ""
GestureEvent.gestureLocation = {x=500, y=500}
GestureEvent.startScale = 0.3
GestureEvent.timer = 0
GestureEvent.suspended = true
GestureEvent.completed = false
GestureEvent.completedGestures = 0
GestureEvent.scoreOneDone = 10
GestureEvent.scoreAllDone = 100
GestureEvent.noHearts = false
GestureEvent.playObjectsDefaultAnimations = true
GestureEvent.doWonAnimation = true

GestureEvent.gestureDescs =
{
	[1] = {
		class="GestureSprite",
		image="EVENT_GESTURES_CIRCLE1:events",
		path={
			{x=15, y=1},
			{x=16, y=21},
			{x=15, y=43},
			{x=16, y=64},
			{x=21, y=77},
			{x=30, y=89},
			{x=46, y=100},
			{x=64, y=103},
			{x=81, y=100},
			{x=98, y=91},
			{x=108, y=78},
			{x=114, y=68},
			{x=115, y=62},
			{x=114, y=40},
			{x=115, y=21},
			{x=114, y=13},
			{x=114, y=1},
		},
		gestureType = "line",
	},

	[2] = {
		class="GestureSprite",
		image="EVENT_GESTURES_CIRCLE2:events",
		path={
			{x=114, y=3},
			{x=114, y=18},
			{x=114, y=40},
			{x=113, y=60},
			{x=106, y=77},
			{x=91, y=94},
			{x=74, y=102},
			{x=62, y=104},
			{x=42, y=99},
			{x=26, y=86},
			{x=17, y=71},
			{x=14, y=62},
			{x=15, y=40},
			{x=15, y=28},
			{x=15, y=12},
			{x=15, y=2},
		},
		gestureType = "line",
	},

	[3] = {
		class="GestureSprite",
		image="EVENT_GESTURES_CIRCLE3:events",
		path={
			{x=16, y=108},
			{x=16, y=88},
			{x=16, y=65},
			{x=16, y=43},
			{x=24, y=25},
			{x=40, y=12},
			{x=60, y=4},
			{x=81, y=8},
			{x=97, y=18},
			{x=109, y=32},
			{x=114, y=45},
			{x=114, y=67},
			{x=114, y=85},
			{x=115, y=97},
			{x=115, y=107},
		},
		gestureType = "line",
	},

	[4] = {
		class="GestureSprite",
		image="EVENT_GESTURES_CIRCLE4:events",
		path={
			{x=114, y=108},
			{x=114, y=87},
			{x=114, y=58},
			{x=114, y=44},
			{x=104, y=26},
			{x=91, y=12},
			{x=65, y=5},
			{x=38, y=11},
			{x=22, y=27},
			{x=15, y=46},
			{x=14, y=74},
			{x=15, y=92},
			{x=15, y=105},
		},
		gestureType = "line",
	},

	[5] = {
		class="GestureSprite",
		image="EVENT_GESTURES_CIRCLE5:events",
		path={
			{x=21, y=79},
			{x=30, y=90},
			{x=44, y=98},
			{x=66, y=104},
			{x=84, y=99},
			{x=103, y=87},
			{x=113, y=66},
			{x=115, y=47},
			{x=104, y=23},
			{x=85, y=8},
			{x=62, y=3},
			{x=44, y=9},
			{x=25, y=24},
			{x=18, y=36},
			{x=16, y=47},
			{x=16, y=51},
			{x=15, y=64},
		},
		gestureType = "line",
	},

	[6] = {
		class="GestureSprite",
		image="EVENT_GESTURES_CIRCLE6:events",
		path={
			{x=109, y=77},
			{x=88, y=98},
			{x=61, y=104},
			{x=31, y=91},
			{x=17, y=70},
			{x=14, y=52},
			{x=21, y=30},
			{x=38, y=13},
			{x=63, y=4},
			{x=83, y=9},
			{x=99, y=19},
			{x=110, y=35},
			{x=114, y=48},
			{x=113, y=64},
		},
		gestureType = "line",
	},

	[7] = {
		class="GestureSprite",
		image="EVENT_GESTURES_LINE1:events",
		path={
			{x=2, y=14},
			{x=33, y=14},
			{x=67, y=14},
			{x=93, y=14},
			{x=110, y=14},
		},
		gestureType = "line",
	},

	[8] = {
		class="GestureSprite",
		image="EVENT_GESTURES_LINE2:events",
		path={
			{x=110, y=13},
			{x=89, y=14},
			{x=63, y=13},
			{x=39, y=13},
			{x=19, y=13},
			{x=4, y=13},
		},
		gestureType = "line",
	},

	[9] = {
		class="GestureSprite",
		image="EVENT_GESTURES_LINE3:events",
		path={
			{x=15, y=109},
			{x=15, y=84},
			{x=14, y=64},
			{x=15, y=45},
			{x=15, y=30},
			{x=14, y=12},
			{x=16, y=-1},
		},
		gestureType = "line",
	},

	[10] = {
		class="GestureSprite",
		image="EVENT_GESTURES_LINE4:events",
		path={
			{x=14, y=2},
			{x=14, y=23},
			{x=14, y=44},
			{x=14, y=71},
			{x=14, y=93},
			{x=14, y=106},
		},
		gestureType = "line",
	},

	[11] = {
		class="GestureSprite",
		image="EVENT_GESTURES_LINE5:events",
		path={
			{x=80, y=80},
			{x=65, y=61},
			{x=48, y=45},
			{x=29, y=27},
			{x=16, y=12},
			{x=3, y=0},
		},
		gestureType = "line",
	},

	[12] = {
		class="GestureSprite",
		image="EVENT_GESTURES_LINE6:events",
		path={
			{x=5, y=78},
			{x=23, y=59},
			{x=43, y=40},
			{x=59, y=23},
			{x=70, y=9},
			{x=82, y=1},
		},
		gestureType = "line",
	},

	[13] = {
		class="GestureSprite",
		image="EVENT_GESTURES_LINE7:events",
		path={
			{x=5, y=1},
			{x=14, y=10},
			{x=21, y=16},
			{x=28, y=24},
			{x=46, y=42},
			{x=60, y=57},
			{x=72, y=68},
			{x=82, y=77},
		},
		gestureType = "line",
	},

	[14] = {
		class="GestureSprite",
		image="EVENT_GESTURES_LINE8:events",
		path={
			{x=80, y=1},
			{x=60, y=21},
			{x=43, y=38},
			{x=29, y=51},
			{x=18, y=62},
			{x=12, y=68},
			{x=2, y=77},
		},
		gestureType = "line",
	},

	-- Clicking
	[15] = {
		class="GestureSprite",
		image="EVENT_GESTURES_OUTLINE1:events",
		endScale=1.0,
		offset = {x=58, y=55},
		path = {
		},
		gestureType = "click",
	},

	-- Hold down (push)
	[16] = {
		class="GestureSprite",
		image="EVENT_GESTURES_OUTLINE1:events",
		endScale=1.0,
		offset = {x=58, y=55},
		path = {
		},
		gestureType = "down",
	},

	[17] = {
		class="GestureSprite",
		image="EVENT_GESTURES_OUTLINE4:events",
		endScale=1.1,
		offset = {x=58, y=55},
		path = {
			{x=3, y=52},
			{x=110, y=53},
		},
		gestureType = "moveHor",
	},

	[18] = {
		class="GestureSprite",
		image="EVENT_GESTURES_OUTLINE5:events",
		endScale=1.1,
		offset = {x=58, y=55},
		path = {
			{x=57, y=2},
			{x=57, y=108},
		},
		gestureType = "moveVer",
	},

	[19] = {
		class="GestureSprite",
		image="EVENT_GESTURES_OUTLINE3:events",
		endScale=1.1,
		offset = {x=58, y=55},
		path = {
			{x=19, y=15},
			{x=94, y=92},
		},
		gestureType = "moveBackslash",
	},

	[20] = {
		class="GestureSprite",
		image="EVENT_GESTURES_OUTLINE2:events",
		endScale=1.1,
		offset = {x=58, y=55},
		path = {
			{x=19, y=90},
			{x=96, y=16},
		},
		gestureType = "moveSlash",
	},
}

function GestureEvent:init(desc)
	GestureEvent.superInit(self, desc)
	self.completed = false
	self.started = false
	self.wonAnimationDone = false
	self.moving = false
	self.mouseX = 0
	self.mouseY = 0
	self.mousePrevX = 0
	self.mousePrevY = 0
	self.gestureObjects = {}
	self.currentGesture = 1
	self.eventBarImage = desc.eventBarImage
	self.doGestureObject = desc.doGestureObject
	self.afterGesturesObject = desc.afterGesturesObject

	self.fillSprite = level:newChild({class="Sprite", image="EVENT_GESTURES_OUTLINE_FILL:events", anchor="center"})
	self.fillSprite.visible = false
	for _, v in pairs(desc.gestures) do
		local obj = level:newChild(self.gestureDescs[v.desc])
		obj.location = v.gestureLocation or desc.gestureLocation
		obj.animation = v.animation
		obj.objectId = v.objectId
		obj.drainSpeed = v.drainSpeed
		obj.growSpeed = v.growSpeed
		obj.trigger = v.trigger
		obj.invertProgress = v.invert
		obj.visible = false
		obj.timeBeforeStart = v.timeBeforeStart or 0
		table.push_back(self.gestureObjects, obj)

		if (obj.gestureType == "click") then
			obj.label = obj:newChild({class="Label", x=obj.width/2, y=25, scale=1.1, roundPos=false, font="EVENT_GESTURES_ACTION:events", anchor="center", textId="TEXT_GESTURE_CLICK" })
		elseif (obj.gestureType == "down") then
			obj.label = obj:newChild({class="Label", x=obj.width/2, y=25, scale=1.1, roundPos=false, font="EVENT_GESTURES_ACTION:events", anchor="center", textId="TEXT_GESTURE_DOWN" })
		end
	end

	self.dot = level:newChild({class="Sprite", image="EVENT_GESTURES_DOT:events", anchor="center"})
	self.dot.label = self.dot:newChild({class="Label", y=-2, font="EVENT_GESTURES_NUMBERS:events", color0=0xffffff, anchor="center"})
	self.dot.visible = false
	self:resetDot()
end

function GestureEvent:getCurrentGesture()
	return self.gestureObjects[self.currentGesture]
end

function GestureEvent:onInitialized()
	eventBar.startValue = 0
	eventBar.targetValue = table.icount(self.gestureObjects)
	eventBar.progress = 0
	eventBar.description = self.eventBarText or "TEXT_EVENT_GESTURE"
	eventBar.icon = self.eventBarImage
end

function GestureEvent:onShiftFinish()
    GestureEvent.super:onShiftFinish()
	self.completed = true
	if (self:getCurrentGesture()) then
		self:getCurrentGesture().visible = false
		self.dot.visible = false
		self.fillSprite.visible = false
	end	
end

function GestureEvent:onShiftStart()
	self.afterGesturesObject = level:getObject(self.afterGesturesObject)
	if (isTable(self.afterGesturesObject)) then
		-- The table is present, set to occupied while patient is there
		if (self.afterGesturesObject.setMaxOccupants) then
			self.afterGesturesObject:setMaxOccupants(0)
		end
		self.afterGesturesObject:setVisible(false)
	end
end

function GestureEvent:onStart()
	assert(not self.started)
	if (not self.started) then
		self.doGestureObject = level:getObject(self.doGestureObject)
		assert(isTable(self.doGestureObject))
		if (self.doGestureObject.clickable) then
			print("Warning: Gesture Object was clickable: \""..self.doGestureObject.name.."\"")
			self.doGestureObject.clickable = false
		end
		self.doGestureObject.event = self
		if (self.visibleOnStart) then
			self.doGestureObject.visible = true
		end
		self:initGesture()
		self.doGestureObject.clickable = true
		self.fillSprite.visible = false
		self.started = true
		eventBar:activate()
	end
end

function GestureEvent:eventFinished()
	if (self.doWonAnimation and isTable(taskSystem:getAnimationDesc("eventWon"))) then
		return taskSystem:createAnimation("eventWon")
	end
end

function GestureEvent:eventWon()
	self.fillSprite.visible = false
	eventBar:deactivate()
	self.completed = true
	self.won = true
	
	if (level.eventThanks) then
		level:eventThanks()
	end
	
	if (isTable(self.afterGesturesObject)) then
		--table present, set to occupied while patient is there
		if (self.afterGesturesObject.setMaxOccupants) then
			self.afterGesturesObject:setMaxOccupants(1)
		end
		self.afterGesturesObject.visible = true
		self.doGestureObject.visible = false
	end
	self.doGestureObject.clickable = false
end

function GestureEvent:initGesture()
	local gesture = self:getCurrentGesture()

	gesture.object = nil
	self.fillSprite.scale = self.startScale - 0.1
	if (gesture.objectId) then
		gesture.object = level:getSpriteExt(gesture.objectId)
		if (not gesture.object) then
			print("Failure: Gesture Object missing")
		elseif (self.moving) then
			gesture.object.playIdleAnimations = false
		end
	end
	self.fillSprite.visible = gesture.gestureType ~= "line"
	if (not self.moving) then
		self.dot.frame = 0
	end
end

function GestureEvent:resetDot()
	self.mouseX = 0
	self.mouseY = 0
	self.mousePrevX = 0
	self.mousePrevY = 0

	local gesture = self:getCurrentGesture()

	self.dot.image = "EVENT_GESTURES_DOT:events"

	if (gesture) then
		gesture.progress = 0
		self.fillSprite.scale = self.startScale
		if (gesture.gestureType == "line") then
			self.dot.x = gesture.path[1].x
			self.dot.y = gesture.path[1].y
			self.dot.pathIdx = 1
		else
			if (gesture.gestureType == "click") then
				self.dot.image = "EVENT_GESTURES_DOT_CLICK:events"
			end
			self.dot.x = gesture.offset.x
			self.dot.y = gesture.offset.y
		end
		gesture:addChild(self.dot)
		self.fillSprite.x = self.dot.screenX
		self.fillSprite.y = self.dot.screenY
		self.dot.label.text = table.icount(self.gestureObjects) - self.currentGesture + 1
	end
end

function GestureEvent:onMouseDown(x, y)
	if (self.dot.visible and not self.completed and not self.suspended) then
		self.mouseX = x
		self.mouseY = y
		self.mousePrevX = x
		self.mousePrevY = y
		self.moving = self.dot:isInside(x, y)
		if (self.moving) then
			self.dot.frame = 2
			if (self:getCurrentGesture().object) then
				self:getCurrentGesture().object.playIdleAnimations = false
				if (self:getCurrentGesture().object and self:getCurrentGesture().progress == 0) then
					self:getCurrentGesture().object:playAnimation({animation=self:getCurrentGesture().animation, cycles=0})
				end

				if (self:getCurrentGesture().gestureType == "click") then
					if (self.dot:isInside(x, y)) then
						self.fillSprite:setScale(self.fillSprite.scaleX + 0.15)
						if (self.fillSprite.scaleX >= self:getCurrentGesture().endScale) then
							self:nextGesture(true)
							return true
						end
					else
						self:resetDot()
					end
				end
			end
		end
		return self.moving
	end
	return false
end

function GestureEvent:onMouseUp(x, y)
	if (not self.completed) then
		self.mouseX = x
		self.mouseY = y
		self.mousePrevX = x
		self.mousePrevY = y
		local handled = self.moving
		self.moving = false
		self.dot.frame = 0
		local gestureType = self:getCurrentGesture().gestureType
		if (gestureType ~= "click" and gestureType ~= "down") then
			self:resetDot()
		end
		return handled
	end
	return false
end

function GestureEvent:createOverhead()
	if (self.suspended == false) then
		self.doGestureObject.overheadPosition = self.overheadPosition or {x=self.doGestureObject.width/2,y=0}
		self.doGestureObject:createOverhead("icon_gesture:icons")
	end
end

function GestureEvent:activateGesture()
	self.suspended = false
	if (self.doGestureObject.isPresent)then
		self:showGesture(true)
	else
		self:createOverhead()
	end
end

function GestureEvent:onMouseMoved(x, y, lButtonDown)
	if (self.dot.visible and not self.completed and not self.suspended) then
		self.mousePrevX = self.mouseX
		self.mousePrevY = self.mouseY
		self.mouseX = x
		self.mouseY = y

		if (self.started and self.moving and self:getCurrentGesture().gestureType == "line") then
			local pos, pIdx, fraction, distance = self:getCurrentGesture():getPathPos(x, y, self.dot.pathIdx)
			self.dot.x = pos.x
			self.dot.y = pos.y
			self.dot.pathIdx = pIdx
			self:getCurrentGesture().progress = fraction
			if (distance > 70) then
				self.moving = false
				self.dot.frame = 0
				self:resetDot()
				self.dot:animate(
					{color="red", duration=200},
					{color="white", duration=200})
			elseif (self.dot.pathIdx == table.icount(self:getCurrentGesture().path)-1) then
				self.completedGestures = self.completedGestures + 1
				self:nextGesture(true)
			end

			return true
		end
	end
	return false
end

function GestureEvent:onTick(time)
	if (not self.started) then
		return
	end
	
	if (self.won and not self.wonAnimationDone and (not self.checkWinConditions or self:checkWinConditions())) then
		self:eventFinished()
		self.wonAnimationDone = true
	end

	if (self.timer >= 0) then
		self.timer = self.timer - time;
		if (self.timer <= 0) then
			self:activateGesture()
		end
		return
	end

	if (not self.doGestureObject.working) then
		return
	end

	if (not self.completed) then
		local doReturn = false
		local gesture = self:getCurrentGesture()
		if (gesture) then
			if (self.moving and gesture.gestureType ~= "line") then
				local x = self.mouseX - self.mousePrevX
				local y = self.mouseY - self.mousePrevY
				if (gesture.gestureType == "down") then
					if (self.dot:isInside(self.mouseX, self.mouseY)) then
						self.fillSprite.scale = self.fillSprite.scaleX + ((time / gesture.growSpeed) * 0.01)
						doReturn = true
					else
						self.moving = false
						self.dot.frame = 0
					end
				else
					if (gesture.gestureType == "moveHor") then
						if (x ~= 0) then
							angle = y / x
							local diff = (math.abs(angle) * 2) + 1
							self.fillSprite.scale = self.fillSprite.scaleX + ((time / gesture.growSpeed) * (0.01 / diff))
							doReturn = true
						end
					elseif (gesture.gestureType == "moveVer") then
						if (y ~= 0) then
							angle = x / y
							local diff = (math.abs(angle) * 2) + 1
							self.fillSprite.scale = self.fillSprite.scaleX + ((time / gesture.growSpeed) * (0.01 / diff))
							doReturn = true
						end
					elseif (gesture.gestureType == "moveBackslash") then
						if (y ~= 0) then
							angle = x / y
							local diff = (math.abs(1-angle) * 2) + 1
							self.fillSprite.scale = self.fillSprite.scaleX + ((time / gesture.growSpeed) * (0.01 / diff))
							doReturn = true
						end
					elseif (gesture.gestureType == "moveSlash") then
						if (y ~= 0) then
							angle = x / y
							local diff = (math.abs(1+angle) * 2) + 1
							self.fillSprite.scale = self.fillSprite.scaleX + ((time / gesture.growSpeed) * (0.01 / diff))
							doReturn = true
						end
					end

					if (doReturn) then
						local np = {x=self.dot.x + x, y=self.dot.y + y}
						local p = math.findNearestPoint(np, gesture.path[1], gesture.path[2])
						self.dot.x = p.x
						self.dot.y = p.y
					end
				end

				if (self.fillSprite.scaleX >= gesture.endScale) then
					self:nextGesture(true)
					return
				end
			end

			if (not doReturn) then
				if (gesture and gesture.gestureType ~= "line") then
					if (self.fillSprite.scaleX > self.startScale) then
						self.fillSprite.scale = math.max(self.startScale, self.fillSprite.scaleX - ((time / gesture.drainSpeed) * 0.01))
					end
				end
			end

			local prevProgress = gesture.progress
			if (gesture.gestureType ~= "line") then
				gesture.progress = (self.fillSprite.scaleX-self.startScale) / (gesture.endScale-self.startScale)
				if (gesture.progress < 0.01) then
					gesture.progress = 0
				elseif (gesture.progress > 0.95) then
					gesture.progress = 1
				end

				if (self.playObjectsDefaultAnimations and
					gesture.gestureType == "down" and
					prevProgress > 0 and
					gesture.progress == 0 and
					gesture.object and
					not level.inStoryMode) then
						gesture.object:playDefaultAnimation()
				end
			end

			if (gesture.object and gesture.progress >= prevProgress) then
				local progress = gesture.progress
				if (gesture.invertProgress) then
					progress = 1.0 - progress
				end

				if (gesture.gestureType ~= "line" and gesture.gestureType ~= "down") then
					gesture.object.frame = 0
					if (math.floor(progress * 20) % 2 == 0) then
						gesture.object.frame = (gesture.object.frameCount-1)
					end
				else
					gesture.object.frame = (gesture.object.frameCount-0.01) * progress
				end
			end
		end
		self.mousePrevX = self.mouseX
		self.mousePrevY = self.mouseY
	end
end

-- Set the fillSprite
function GestureEvent:nextGesture(done)

	if (self.completed) then
		return
	end
	
	if (done and not self.noHearts) then
		level:heartsToBar(
			self.scoreOneDone,
			self:getCurrentGesture().location.x + self:getCurrentGesture().width,
			self:getCurrentGesture().location.y, false)
	end

	eventBar:incValue(1)
	self:getCurrentGesture().visible = false

	if (self:getCurrentGesture() and self:getCurrentGesture().trigger) then
		level:fireTrigger(self:getCurrentGesture().trigger)
	end

	if (self.currentGesture < table.icount(self.gestureObjects)) then
		local p1 = {x=self.dot.screenX, y=self.dot.screenY}
		self.currentGesture = self.currentGesture + 1
		self.timer = self:getCurrentGesture().timeBeforeStart or 0
		self:resetDot()
		local p2 = {x=self.dot.screenX, y=self.dot.screenY}
		self.moving = self.moving and math.calcDistance(p1, p2) <= 35 and self:getCurrentGesture().gestureType == "line"
		self:initGesture()

		-- Suspend gesture
		self:showGesture(false)
		self.suspended = true

		if (level.eventThanks) then
			level:eventThanks()
		end
		if (isTable(taskSystem:getAnimationDesc("eventThanks"))) then
		    taskSystem:createAnimation("eventThanks")
		end
	else
		if (not self.noHearts) then
			level:heartsToBar(
				self.scoreAllDone,
				self:getCurrentGesture().location.x + self:getCurrentGesture().width,
				self:getCurrentGesture().location.y, false)
		end

		self.fillSprite.visible = false
		eventBar:deactivate()
		self:eventWon()
		self.completed = true
	end
end

function GestureEvent:showGesture(visible)
	if (not self.completed) then
		self:getCurrentGesture().visible = visible
		self.dot.visible = visible
		self.fillSprite.visible = visible

		if (visible) then
			self:initGesture()
			self:resetDot()

			if (self.moving and self:getCurrentGesture().object) then
				self:getCurrentGesture().object:playAnimation({animation=self:getCurrentGesture().animation, cycles=0})
			end

			hintManager:handle({
				id = "first_gesture",
				sprite = self:getCurrentGesture(),
				x = self.doGestureObject.x + self.doGestureObject.width/2,
				y = self.doGestureObject.y,
				tip = self.hintTip
			})
		end
	end
end

--------------------------------------------------------------------
GestureSprite = deriveLuaClass("GestureSprite", "Sprite")

function GestureSprite:init(desc)
	GestureSprite.superInit(desc)
	self.path = desc.path
	self.gestureType = desc.gestureType
	self.offset = desc.offset
	self.progress = 0

	self.curPathIdx = 1
	self.pathLength = 0
	for i=1, table.icount(self.path)-2 do
		self.pathLength = self.pathLength + math.calcDistance(self.path[i], self.path[i+1])
	end

	if (desc.gestureType ~= "line") then
		self.endScale = desc.endScale
	end
end

function GestureSprite:getPathPos(screenX, screenY, pathIdx)
	local pos = {x=screenX - self.screenX, y=screenY - self.screenY}
	local minDist = 1000000
	local newPos = {}
	local newPathIdx = 0
	local fraction = 1

	for i=pathIdx, pathIdx+1 do
		local pos, dist = math.findNearestPoint(pos, self.path[i], self.path[i+1])
		if (dist < minDist) then
			minDist = dist
			newPos = pos
			newPathIdx = i
		end
	end

	local length = 0
	if (newPathIdx > 1) then
		for i=1, newPathIdx-1 do
			length = length + math.calcDistance(self.path[i], self.path[i+1])
		end
	end

	length =length + math.calcDistance(self.path[newPathIdx], newPos)
	assert(self.pathLength > 0)
	return newPos, newPathIdx, math.min(1, length / self.pathLength), minDist
end

-- ************************************ Gesture Object ************************************ --
DoGestureObject = deriveLuaClass("DoGestureObject", "Object")
DoGestureObject.event = nil
DoGestureObject.isPresent = false
DoGestureObject.working = false

function DoGestureObject:init(desc)
	DoGestureObject.superInit(desc)
end

function DoGestureObject:onClick()
	if (self.event.suspended)then
		return
	else
		if (self.event and not self.event.completed and not self.selected) then
			self.isPresent = true
			self.selected = true
			self.clickable = false
			hero:addTask({class="SetTask", positionLock=self})
			hero:addTask({class="WorkTask", target=self})
		end
	end
end

function DoGestureObject:onWorkStart(task)
	DoGestureObject.super.onWorkStart(self, task)
	hero.playIdleAnimations = false
	self.selected = false
	task.duration = -1
	self.event:showGesture(true)
	self:removeOverhead()
	self.working = true
	return true
end

function DoGestureObject:onWorkUpdate(task, time)
	DoGestureObject.default_onWorkUpdate(self, task, time)
	if (taskSystem:hasTaskFor(hero) or self.event.completed or self.event.timer > 1) then
		self.event:showGesture(false)
		hero.playIdleAnimations = true
		self.isPresent = false
		self.clickable = not self.event.completed
		if (not self.event.completed) then
			self.event:createOverhead()
		end
		self.working = false
		return true
	end
	return false
end

-- p = point
-- a, b = line from a to b
function math.findNearestPoint(p, a, b)
	local dot_pa = (p.x - a.x) * (b.x - a.x) + (p.y - a.y) * (b.y - a.y)
	if (dot_pa < 0) then
		-- There is no point between a and b that is the nearest so a is the nearest
		-- Return the amount it is beyond point a (negative)
		return a, math.calcDistance(a, p)
	end

	local dot_pb = (p.x - b.x) * (a.x - b.x) + (p.y - b.y) * (a.y - b.y)
	if (dot_pb < 0) then
		-- Return the amount it is beyond point b (positive)
		return b, math.calcDistance(b, p)
	end

	local resultPoint = {x=0, y=0}
	-- There is a point on the line between a and b that is the nearest
	resultPoint.x = a.x + ((b.x - a.x) * dot_pa) / (dot_pa + dot_pb)
	resultPoint.y = a.y + ((b.y - a.y) * dot_pa) / (dot_pa + dot_pb)

	-- Return 0: meaning the point is on the line a => b
	return resultPoint, math.calcDistance(resultPoint, p)
end

function math.calcDistance(p1, p2)
	local deltax = p2.x - p1.x
	local deltay = p2.y - p1.y
	return math.sqrt(deltax * deltax + deltay * deltay)
end