local Movement = {
	STILL_LEFT = "STILL_LEFT",
	STILL_RIGHT = "STILL_RIGHT",
	MOVING_LEFT = "MOVING_LEFT",
	MOVING_RIGHT  = "MOVING_RIGHT",
	CLIMBING_UP = "CLIMBING_UP",
	CLIMBING_DOWN  = "CLIMBING_DOWN",
	FALLING = "FALLING",
	JUMPING_LEFT = "JUMPING_LEFT",
	JUMPING_RIGHT = "JUMPING_RIGHT",
	JUMPING_STRAIGHT_UP = "JUMPING_STRAIGHT_UP",
	CLIMBING_STILL = "CLIMBING_STILL",
}

local Actions = {
	LEFT_ARROW_PRESS = "LEFT_ARROW_PRESS",
	LEFT_ARROW_RELEASE = "LEFT_ARROW_RELEASE",
	RIGHT_ARROW_PRESS = "RIGHT_ARROW_PRESS",
	RIGHT_ARROW_RELEASE = "RIGHT_ARROW_RELEASE",
	UP_ARROW_PRESS = "UP_ARROW_PRESS",
	UP_ARROW_RELEASE = "UP_ARROW_RELEASE",
	DOWN_ARROW_PRESS = "DOWN_ARROW_PRESS",
	DOWN_ARROW_RELEASE = "DOWN_ARROW_RELEASE",
	SPACE_BAR_PRESS = "SPACE_BAR_PRESS",
	SPACE_BAR_RELEASE = "SPACE_BAR_RELEASE",
}

-- Transition table: [current_state][input] = next_state
local transitions = {
	[Movement.STILL_RIGHT] = {
		[Actions.RIGHT_ARROW_PRESS] = Movement.MOVING_RIGHT,
		[Actions.RIGHT_ARROW_RELEASE] = Movement.STILL_RIGHT,
		[Actions.LEFT_ARROW_PRESS] = Movement.MOVING_LEFT,
		[Actions.LEFT_ARROW_RELEASE] = Movement.STILL_LEFT,
		[Actions.UP_ARROW_PRESS] = Movement.CLIMBING_UP,
		[Actions.UP_ARROW_RELEASE] = Movement.CLIMBING_STILL,
		[Actions.DOWN_ARROW_PRESS] = Movement.CLIMBING_DOWN,
		[Actions.DOWN_ARROW_RELEASE] = Movement.CLIMBING_STILL,
	},
	[Movement.STILL_LEFT] = {
		[Actions.RIGHT_ARROW_PRESS] = Movement.MOVING_RIGHT,
		[Actions.RIGHT_ARROW_RELEASE] = Movement.STILL_RIGHT,
		[Actions.LEFT_ARROW_PRESS] = Movement.MOVING_LEFT,
		[Actions.LEFT_ARROW_RELEASE] = Movement.STILL_LEFT,
		[Actions.UP_ARROW_PRESS] = Movement.CLIMBING_UP,
		[Actions.UP_ARROW_RELEASE] = Movement.CLIMBING_STILL,
		[Actions.DOWN_ARROW_PRESS] = Movement.CLIMBING_DOWN,
		[Actions.DOWN_ARROW_RELEASE] = Movement.CLIMBING_STILL,
	},
	[Movement.MOVING_LEFT] = {
		[Actions.RIGHT_ARROW_PRESS] = Movement.MOVING_RIGHT,
		[Actions.LEFT_ARROW_PRESS] = Movement.MOVING_LEFT,
		[Actions.LEFT_ARROW_RELEASE] = Movement.STILL_LEFT,
		[Actions.UP_ARROW_PRESS] = Movement.CLIMBING_UP,
		[Actions.DOWN_ARROW_PRESS] = Movement.CLIMBING_DOWN,
	},
	[Movement.MOVING_RIGHT] = {
		[Actions.RIGHT_ARROW_PRESS] = Movement.MOVING_RIGHT,
		[Actions.RIGHT_ARROW_RELEASE] = Movement.STILL_RIGHT,
		[Actions.LEFT_ARROW_PRESS] = Movement.MOVING_LEFT,
		[Actions.UP_ARROW_PRESS] = Movement.CLIMBING_UP,
		[Actions.DOWN_ARROW_PRESS] = Movement.CLIMBING_DOWN,
	},
	[Movement.CLIMBING_STILL] = {
		[Actions.RIGHT_ARROW_PRESS] = Movement.MOVING_RIGHT,
		[Actions.RIGHT_ARROW_RELEASE] = Movement.STILL_RIGHT,
		[Actions.LEFT_ARROW_PRESS] = Movement.MOVING_LEFT,
		[Actions.LEFT_ARROW_RELEASE] = Movement.STILL_LEFT,
		[Actions.UP_ARROW_PRESS] = Movement.CLIMBING_UP,
		[Actions.UP_ARROW_RELEASE] = Movement.CLIMBING_STILL,
		[Actions.DOWN_ARROW_PRESS] = Movement.CLIMBING_DOWN,
		[Actions.DOWN_ARROW_RELEASE] = Movement.CLIMBING_STILL,
	},
	[Movement.CLIMBING_UP] = {
		[Actions.RIGHT_ARROW_PRESS] = Movement.MOVING_RIGHT,
		[Actions.LEFT_ARROW_PRESS] = Movement.MOVING_LEFT,
		[Actions.LEFT_ARROW_PRESS] = Movement.MOVING_LEFT,
		[Actions.LEFT_ARROW_RELEASE] = Movement.STILL_LEFT,
		[Actions.UP_ARROW_PRESS] = Movement.CLIMBING_UP,
		[Actions.UP_ARROW_RELEASE] = Movement.CLIMBING_STILL,
		[Actions.DOWN_ARROW_PRESS] = Movement.CLIMBING_DOWN,
		[Actions.DOWN_ARROW_RELEASE] = Movement.CLIMBING_STILL,
	},
	[Movement.CLIMBING_DOWN] = {
		[Actions.RIGHT_ARROW_PRESS] = Movement.MOVING_RIGHT,
		[Actions.LEFT_ARROW_PRESS] = Movement.MOVING_LEFT,
		[Actions.UP_ARROW_PRESS] = Movement.CLIMBING_UP,
		[Actions.UP_ARROW_RELEASE] = Movement.CLIMBING_STILL,
		[Actions.DOWN_ARROW_PRESS] = Movement.CLIMBING_DOWN,
		[Actions.DOWN_ARROW_RELEASE] = Movement.CLIMBING_STILL,
	},
}

function init(self)
	msg.post(".", "acquire_input_focus")
	self.direction = vmath.vector3(0,0,0)
	self.speed = 250
	self.gravity = -5000 -- gravity force
	self.jump_velocity = 800-- how strong the jump is 
	self.velocity = vmath.vector3(0, 0, 0)
	self.grounded = false
	self.jumped = false
	self.near_up_ladder = false
	self.correction = vmath.vector3()
	self.climbing = false
	self.is_moving = 0
	
	self.left_pressed = false
	self.right_pressed = false
	self.up_pressed = false
	self.down_pressed = false
	self.jump_pressed = false

	self.have_amulet = false
	self.amulet_timer = 0
	self.regenerating = false
	self.regen_timer = 0
	self.blink_on_timer = 0
	self.blink_off_timer = 0

	self.paused = false
		
	self.current_movement = Movement.STILL_RIGHT
	
	go.set_position(vmath.vector3(100,100,0.2))
end

function final(self)
	-- Add finalization code here
	-- Learn more: https://defold.com/manuals/script/
	-- Remove this function if not needed
end

local function handleInput(self, input)
	local Transitions = transitions[self.current_movement]
	local next_movement = Transitions and Transitions[input]
	if next_movement then
		--print("Transitioning from", self.current_movement, "to", next_movement, "on input", input)
		self.current_movement = next_movement
		if next_movement == Movement.MOVING_LEFT then
			sprite.play_flipbook("#sprite", hash("run_left"))
			msg.post("holding_amulet", "left")
			self.direction.x = -1
			self.velocity.y = 0
		end
		if next_movement == Movement.MOVING_RIGHT then
			sprite.play_flipbook("#sprite", hash("run_right"))
			msg.post("holding_amulet", "right")
			self.direction.x = 1
			self.velocity.y = 0
		end
		if next_movement == Movement.STILL_RIGHT then
			if not self.climbing then
				sprite.play_flipbook("#sprite", hash("still_right"))
			else
				next_movement = Movement.CLIMBING_STILL
				sprite.play_flipbook("#sprite", hash("still_climbing"))
			end
			self.direction.x = 0
		end
		if next_movement == Movement.STILL_LEFT then
			if not self.climbing then
				sprite.play_flipbook("#sprite", hash("still_left"))
			else
				next_movement = Movement.CLIMBING_STILL
				sprite.play_flipbook("#sprite", hash("still_climbing"))
			end
			self.direction.x = 0
		end
		if next_movement == Movement.CLIMBING_STILL then
			sprite.play_flipbook("#sprite", hash("still_climbing"))
			self.velocity.y = 0
			self.climbing = true
			if self.right_pressed then
				handleInput(self, Actions.RIGHT_ARROW_PRESS)
				--print("Actions.LEFT_RIGHT_PRESS from climbing_still")
			elseif self.left_pressed then
				handleInput(self, Actions.LEFT_ARROW_PRESS)
				--print("Actions.LEFT_ARROW_PRESS from climbing_still")
			end
		end
		if next_movement == Movement.CLIMBING_UP then
			sprite.play_flipbook("#sprite", hash("climbing"))
			self.direction.x = 0
			self.velocity.y = 150
			self.climbing = true
		end
		if next_movement == Movement.CLIMBING_DOWN then
			sprite.play_flipbook("#sprite", hash("climbing"))
			self.direction.x = 0
			self.velocity.y = -150
			self.climbing = true
		end
	else
		--print("No transition defined for", input, "in state", self.current_movement)
	end
end

function update(self, dt)

	if not self.paused then
	
		if self.amulet_timer > 0 then
			self.amulet_timer = self.amulet_timer - dt
	--		print ("You have " .. self.amulet_timer .. " seconds left of amulet")
			if self.amulet_timer < 0 then
				self.amulet_timer = 0
				self.have_amulet = false
				msg.post("holding_amulet", "disable_sprite")
			end
		end

		if self.regen_timer > 0 then
			self.regen_timer = self.regen_timer - dt
	--		print ("You have " .. self.regen_timer .. " seconds left of regeneration")
			if self.regen_timer < 0 then
				self.regen_timer = 0
				self.regenerating = false
				msg.post("#sprite", "enable")
			end
			if self.regenerating then
				if self.blink_on_timer > 0 then
					self.blink_on_timer = self.blink_on_timer - dt
					msg.post("#sprite", "enable")
				elseif self.blink_off_timer > 0 then
					self.blink_off_timer = self.blink_off_timer - dt
					msg.post("#sprite", "disable")
				else
					self.blink_on_timer = 0.25
					self.blink_off_timer = 0.25
				end
			end
		end
			
		self.correction = vmath.vector3()
		
		self.velocity.x = self.direction.x * self.speed
		local pos = go.get_position()

		pos = pos + self.velocity * dt
		if pos.x < 24 then
			pos.x = 24
		elseif pos.x > 1030 then
			pos.x = 1030
		end
		if pos.y < 0 then
			pos.y = 100
		end

		local centre = vmath.vector3(pos.x, pos.y - 10, 0)
		local platform = pos +  vmath.vector3(0, -36, 0)
		local ladder_up_left = pos + vmath.vector3(-24, -32, 0)
		local ladder_up_right = pos + vmath.vector3(24, -32, 0)
		local ladder_down_left = pos + vmath.vector3(-24, -42, 0)
		local ladder_down_right = pos + vmath.vector3(24, -42, 0)
			
		--msg.post("@render:", "draw_line", { start_point = centre, end_point = platform, color = vmath.vector4(1,0,0,1) })
		--msg.post("@render:", "draw_line", { start_point = ladder_up_left, end_point = ladder_up_right, color = vmath.vector4(1,0,0,1) })
		--msg.post("@render:", "draw_line", { start_point = ladder_up_right, end_point = ladder_up_left, color = vmath.vector4(1,0,0,1) })
		--msg.post("@render:", "draw_line", { start_point = ladder_down_left, end_point = ladder_down_right, color = vmath.vector4(1,0,0,1) })
		--msg.post("@render:", "draw_line", { start_point = ladder_down_right, end_point = ladder_down_left, color = vmath.vector4(1,0,0,1) })
		
		local platform_result = physics.raycast(centre, platform, { hash("platform") })
		if platform_result then
			local point = platform_result.position - pos
	--		msg.post("@render:", "draw_debug_text", { text = "Platform", position = vmath.vector3(20, 400, 0), color = vmath.vector4(1, 0, 0, 1) } )
	--		msg.post("@render:", "draw_debug_text", { text = "X : " .. tostring(point.x), position = vmath.vector3(20, 380, 0), color = vmath.vector4(1, 0, 0, 1) } )
	--		msg.post("@render:", "draw_debug_text", { text = "Y : " .. tostring(point.y), position = vmath.vector3(20, 360, 0), color = vmath.vector4(1, 0, 0, 1) } )
			if not self.climbing then
				self.jumped = false
				self.speed = 250
				self.grounded = true
				self.velocity.y = 0
				if point.y > -36 then
					pos.y = pos.y + (point.y + 36)
				end
				--if self.climbing then
				--	self.climbing = false
				--	sprite.play_flipbook("#sprite", hash("still_left"))			
				--end
			end
		else
			self.grounded = false
		end
		
		local ladder_up_result1 = physics.raycast(ladder_up_right, ladder_up_left, { hash("ladder") })
		local ladder_up_result2 = physics.raycast(ladder_up_left, ladder_up_right, { hash("ladder") })
		if ladder_up_result1 then
			self.near_up_ladder = true
		elseif ladder_up_result2 then
			self.near_up_ladder = true
		else
			self.near_up_ladder = false
		end		
				
		local ladder_down_result1 = physics.raycast(ladder_down_right, ladder_down_left, { hash("ladder") })
		local ladder_down_result2 = physics.raycast(ladder_down_left, ladder_down_right, { hash("ladder") })
		if ladder_down_result1 then
			self.near_down_ladder = true
		elseif ladder_down_result2 then
			self.near_down_ladder = true
		else
			self.near_down_ladder = false
		end		

		if not (self.near_up_ladder or self.near_down_ladder)then
			if self.climbing then
				self.climbing = false
				if self.left_pressed then
					handleInput(self, Actions.LEFT_ARROW_PRESS)
				end
				if self.right_pressed then
					handleInput(self, Actions.RIGHT_ARROW_PRESS)
				end
			end
		end

--		local debug_vars = {
--			x = pos.x, 
--			y = pos.y, 
--			x_vel = self.velocity.x, 
--			y_vel = self.velocity.y, 
--			grounded = self.grounded,
--			near_up_ladder = self.near_up_ladder,
--			near_down_ladder = self.near_down_ladder,
--			jumped = self.jumped,
--			climbing = self.climbing,
--			left_pressed = self.left_pressed,
--			right_pressed = self.right_pressed,
--			up_pressed = self.up_pressed,
--			down_pressed = self.down_pressed,
--		}
--		msg.post("#debug", "set_vars", debug_vars)

		if not self.grounded and not ((self.near_up_ladder or self.near_down_ladder) and self.climbing) then
			self.velocity.y = self.velocity.y + self.gravity * dt
		end

		go.set_position(pos)
	end
end

function fixed_update(self, dt)

end

function on_message(self, message_id, message, sender)
	if message_id == hash("contact_point_response") then
		if message.group == hash("curse") then
			if not self.regenerating then
				--print("collision with curse")
				msg.post(message.other_id, "dying")
			end
		end
		if message.group == hash("amulet") then
			self.have_amulet = true
			self.amulet_timer = 5
			msg.post("holding_amulet", "enable_sprite")
			--print("collision with amulet")
		end
		if message.group == hash("mummy") then
			msg.post("main:/controller#main", "LevelCompleted")
			--print("collision with mummy")
		end
	end
	if message_id == hash("curse_dying_reply") then
		if message.dying == false then
			msg.post("/holding_amulet#holdingamulet", "disable_sprite")
			msg.post("main:/controller#main", "LifeLost")

			--else						
				self.current_movement = Movement.MOVING_RIGHT
				handleInput(self, Actions.RIGHT_ARROW_RELEASE)
				go.set_position(vmath.vector3(100,100,0.2))
				self.regenerating = true
				self.regen_timer = 3
			--end
		end
	end
	if message_id == hash("pause") then
		self.paused = true
	end
	if message_id == hash("unpause") then
		self.paused = false
	end
end

function on_input(self, action_id, action)
	if not self.paused then
		if action_id == hash("left") then
			if action.pressed then
				self.left_pressed = true
				handleInput(self, Actions.LEFT_ARROW_PRESS)
			elseif action.released then
				self.left_pressed = false
				handleInput(self, Actions.LEFT_ARROW_RELEASE)
			end
		end
		if action_id == hash("right") then
			if action.pressed then
				self.right_pressed = true
				handleInput(self, Actions.RIGHT_ARROW_PRESS)
			elseif action.released then
				self.right_pressed = false
				handleInput(self, Actions.RIGHT_ARROW_RELEASE)
			end
		end
		if action_id == hash("jump") and action.pressed and not self.jumped and not self.climbing then
			self.velocity.y = self.jump_velocity
			self.speed = 500
			self.jumped = true
		end
		if action_id == hash("climb_up") then
			if action.pressed then
				self.up_pressed = true
				handleInput(self, Actions.UP_ARROW_PRESS)
			elseif action.released then
				self.up_pressed = false
				handleInput(self, Actions.UP_ARROW_RELEASE)
			end
		end
		if action_id == hash("climb_down") then
			if action.pressed then
				self.down_pressed = true
				handleInput(self, Actions.DOWN_ARROW_PRESS)
			elseif action.released then
				self.down_pressed = false
				handleInput(self, Actions.DOWN_ARROW_RELEASE)
			end
		end
	end
end

function on_reload(self)
-- Add reload-handling code here
-- Learn more: https://defold.com/manuals/hot-reload/
-- Remove this function if not needed
end