Player = Entity:new()
Player.type = "Player"

function Player:new(x,y)
	local o = Entity:new(x,y)

	-- physics
	o.zero_speed = 0.01 -- gameworld pixels

	o.move_x = 0 -- gameworld pixels
	o.move_speed = 1.3 -- gameworld pixels

	o.nodrift_frames = 0 -- frames

	o.air_friction = 0.01 -- gameworld pixels
	o.ground_friction = 0.3 -- gameworld pixels
	o.wall_friction = 0.3 -- gameworld pixels

	o.jump_impulse = 3.5 -- gameworld pixels

	o.coyote_value = 5 -- frames
	o.coyote_amount = 5 -- int


	-- dash values
	o.dash_timer = 0 -- seconds
	o.dash_time = 0.15 -- seconds
	o.dash_distance = 40 -- gameworld pixels
	o.dash_speed = o.dash_distance / (o.dash_time*60) -- pixels
	o.dash_count = 1 -- int
	o.dash_amount = 10 -- int
	o.dash_cooldown_time = 0.1 -- seconds
	o.dash_cooldown_timer = 0 -- seconds

	-- hook values
	o.hook_swing_speed = math.rad(0.05)
	o.hook_anchor = {
		x = nil,
		y = nil
	}

	-- walljump values
	o.walljump_nodrift_amount = 12
	o.walljump_impulse = { x = 2.5, y = 3.5 }

	-- light values
	o.light_radius = 40 -- screen pixels

	-- status
	o.can_jump = true
	o.can_fall = true
	o.can_friction = true
	o.can_hook = true
	o.can_walljump = true

	o.is_dashing = false
	o.is_hooked = false
	o.is_sliding = false
	o.is_jumping = false
	o.is_on_ground = false
	o.is_on_ladder = false

	o.mask_type = animation.moth_mask
	o.wall_hit = 0

	o.respawn_anchor = {
		x = o.pos.x,
		y = o.pos.y
	}

	-- sprite
	o.target_offset = {x = 0, y = 0}
	o.body = Animation:new(animation.nancy.idle)
	o.mask = Animation:new(animation.moth_mask.idle)

	o:centerOffset(o.body)
	o:createBox(o.body,0,3,-1,-3)

	-- lights
	o.light = Light:new(o.pos.x,o.pos.y,o.light_radius)

	o:id()

	setmetatable(o, self)
	self.__index = self
	return o
end

function Player:doLogic()

	-- reset coyote_value
	if self.is_on_ground then
		self.coyote_value = self.coyote_amount
	elseif self.coyote_value > 0 then
		self.coyote_value = self.coyote_value - 1
	end

	-- not dashing, normal movment
	if self.dash_timer <= 0 then
		-- horizontal movement
		if not self.is_hooked then
			if self.nodrift_frames > 0 then
				self.move_x = 0
			elseif Keybind:checkDown(Keybind.move.left) then
				self.move_x = -1
				self.vel.x = math.min(self.vel.x, -self.move_speed)
			elseif Keybind:checkDown(Keybind.move.right) then
				self.move_x = 1
				self.vel.x = math.max(self.vel.x, self.move_speed)
			end
		end

		-- jump if on ground (coyotevalue) or if 0
		if self.can_jump and Keybind:checkPressed(Keybind.move.jump) then
			if self.can_walljump and self.wall_hit ~= 0 then
				self.is_sliding = false
				self.vel.y = -self.walljump_impulse.y
				self.vel.x = -self.walljump_impulse.x * self.wall_hit
				self.move_x = 0
				self.sprite_flip.x = -self.sprite_flip.x
				self.nodrift_frames = self.walljump_nodrift_amount
			elseif self.coyote_value > 0 then
				self.vel.y = -self.jump_impulse
				self.coyote_value = 0
			end
		end
	end

	-- dash timer
	self.dash_cooldown_timer = math.max(0,self.dash_cooldown_timer - current_dt)

	-- try to dash
	if Keybind:checkDown(Keybind.move.dash) then
		if self.dash_cooldown_timer == 0
		and not self.is_dashing
		and self.dash_count > 0 then
			self:unhook()

			-- state player
			self.is_dashing = true
			self.dash_count = self.dash_count - 1

			-- get dash direction
			local vertical = 0
			if Keybind:checkDown(Keybind.move.down) then vertical = vertical + 1 end
			if Keybind:checkDown(Keybind.move.up) then vertical = vertical - 1 end
			local horizontal = 0
			if Keybind:checkDown(Keybind.move.right) then horizontal = horizontal + 1 end
			if Keybind:checkDown(Keybind.move.left) then horizontal = horizontal - 1 end

			-- if no direction, then dash forward
			if horizontal == 0 and vertical == 0 then
				horizontal = self.sprite_flip.x
			end

			-- set dash values
			self.dashDirection = getAngleFromVector(horizontal, vertical)
			self.dash_timer = math.floor(self.dash_time * game.framerate)
		end
	else
		-- not dashing!
		self.is_dashing = false
	end

	if self.can_hook and Keybind:checkPressed(Keybind.move.hook) then
		if self.is_hooked then
			self:unhook()
		else
			local anchor = self:checkNearest("HookAnchor",self.hook_distance)
			if anchor then
				self.is_hooked = true
				self.hook_distance = anchor.hook_distance
				self.hook_anchor = {
					x = anchor.pos.x,
					y = anchor.pos.y
				}
			end
		end
	end
end

function Player:doPhysics()
	if self.dash_timer <= 0 then
		if self.is_on_ground then
			self.vel.x = self.vel.x * (1-self.ground_friction)
		else
			self.vel.x = self.vel.x * (1-self.air_friction)
		end

		self.is_sliding = false
		if self.wall_hit == 0 then
			self.vel.y = self.vel.y * (1-self.air_friction)
		elseif self.nodrift_frames ~= self.walljump_nodrift_amount then
			self.is_sliding = true
			self.vel.y = self.vel.y * (1-self.wall_friction)
		end

		if math.abs(self.vel.x) < self.zero_speed then self.vel.x = 0 end
	end

	-- reset state
	self.can_fall = true
	self.is_on_ground = false

	-- adjust timers
	self.dash_timer = self.dash_timer - 1
	self.nodrift_frames = self.nodrift_frames - 1

	-- DASH STATE
	if self.dash_timer > 0 then
		self.can_fall = false
		-- dash particle
		local particle_data = {
			animation = self.body,
			sprite_tint = hex2rgb("#fed100"),
			sprite_alpha = 0.5,
			sprite_flip = {
				x = self.sprite_flip.x,
				y = self.sprite_flip.y
			}
		}
		Particle:new(self.pos.x,self.pos.y,particle_data)
		self.dash_cooldown_timer = self.dash_cooldown_time
		-- dash movement
		self.vel.x = self.dash_speed * math.cos(self.dashDirection)
		self.vel.y = self.dash_speed * math.sin(self.dashDirection)
	end

	-- hook state
	if self.is_hooked then
		self.move_x = 0
		local hook = vector(self.pos.x, self.pos.y, self.hook_anchor.x, self.hook_anchor.y)
		local dist =  math.min(getVectorValue(hook), self.hook_distance)

			local hook_angle = getAngleFromVector(hook[1],hook[2])-math.rad(180)

			if Keybind:checkDown(Keybind.move.right) then
				hook_angle = hook_angle - self.hook_swing_speed
			end

			if Keybind:checkDown(Keybind.move.left) then
				hook_angle = hook_angle + self.hook_swing_speed

			end

			local particle_data = {
				animation = self.body,
				sprite_tint = hex2rgb("#fed100"),
				sprite_alpha = 0.5,
				sprite_flip = {
					x = self.sprite_flip.x,
					y = self.sprite_flip.y
				}
			}
			Particle:new(self.pos.x,self.pos.y,particle_data)

			local pos_x = self.hook_anchor.x + dist * math.cos(hook_angle)
			local pos_y = self.hook_anchor.y + dist * math.sin(hook_angle)
			self.vel.x = self.vel.x + pos_x - self.pos.x
			self.vel.y = self.vel.y + pos_y - self.pos.y
			self.pos.x = pos_x
			self.pos.y = pos_y
	end


	if self.can_fall then
		-- not in dash
		self.dash_timer = 0
		self.vel.y = self.vel.y + gravity
	end

	-- horizontal collision
	if not self:isCollidingAt(self.pos.x + self.vel.x, self.pos.y, LoadedObjects.Collisions) then
		self.pos.x = self.pos.x + self.vel.x
		self.wall_hit = 0
	else
		self.wall_hit = math.sign(self.vel.x)
		self.vel.x = 0
	end

	-- vertical collision
	if not self:isCollidingAt(self.pos.x, self.pos.y + self.vel.y, LoadedObjects.Collisions) then
		self.pos.y = self.pos.y + self.vel.y
	else
		if self.vel.y > 0 then
			self.is_on_ground = true
			self.dash_count = self.dash_amount
		end
		self.vel.y = 0
	end

	-- if u collision w hazard, respawn
	if self:isCollidingAt(self.pos.x, self.pos.y, LoadedObjects.Hazards) then
		self:respawn()
	end

	self:adjustLight(self.target_offset.x,self.target_offset.y)
end

function Player:respawn()
	self.pos.x = self.respawn_anchor.x
	self.pos.y = self.respawn_anchor.y
end

function Player:handleAnimation()
	-- flip sprite to look in the direction is moving
	if self.is_hooked then
		if self.vel.x ~= 0 then
			self.sprite_flip.x = math.sign(self.vel.x)
		end
	elseif self.move_x ~= 0 then
		self.sprite_flip.x = math.sign(self.move_x)
	end

	-- animation priority
	if self.vel.y > 1.25 or self.is_sliding then
		self.body = self.body:change(animation.nancy.fall)
		self.mask = self.mask:change(self.mask_type.fall)
	elseif self.vel.y < 0 then
		self.body = self.body:change(animation.nancy.jump)
		self.mask = self.mask:change(self.mask_type.jump)
	elseif self.vel.x + self.move_x ~= 0 then
		self.body = self.body:change(animation.nancy.run)
		self.mask = self.mask:change(self.mask_type.run)
	else
		self.body = self.body:change(animation.nancy.idle)
		self.mask = self.mask:change(self.mask_type.idle)
	end

	-- special case: idle animation gets slower by time
	if self.body.anim_path == animation.nancy.idle.path then
		if self.body.anim_speed < 0.5 then
			self.body.anim_speed = self.body.anim_speed + 0.001
		end
	end

	if self.is_hooked then
		love.graphics.line(
			-Camera.pos.x + self.pos.x,
			-Camera.pos.y + self.pos.y,
			-Camera.pos.x + self.hook_anchor.x,
			-Camera.pos.y + self.hook_anchor.y
		)
	end

	self.body:animate()
	self:draw(self.body)

	if self.dash_count > 0 then
		self:draw(self.mask)
	end
	self.move_x = 0
end

function Player:unhook()
	self.is_hooked = false
	self.hook_anchor = nil
end

function Player:debug()
	Entity.debug(self)
	love.graphics.print("wall_hit: "..self.wall_hit)
end
