Player = {}
class("Player").extends(NobleSprite)

function Player:init(x, y)
	Player.super.init(self, "assets/sprites/player", true)

	-- Animation properties
	self.animation:addState("run", 8, 14)
	self.animation.run.frameDuration = 2
	self.animation:addState("up", 1, 7)
	self.animation.up.frameDuration = 6
	self.animation:addState("down", 1, 7)
	self.animation.down.frameDuration = 6
	self.animation:addState("boom", 15, 21)
	self.animation.boom.frameDuration = 6
	self.animation.boom.loop = false

	self.animation:setState("down")

	self:add()

	-- Collision properties
	self:setSize(64, 64)
	self:moveTo(x, y)
	self:setZIndex(ZIndex.player)
	self:setGroups(CollideGroups.player)
	self:setCollidesWithGroups(
		{
			CollideGroups.enemy,
			CollideGroups.wall
		})
	self:setCollideRect(3, 19, 60, 33)
	self:setTag(1)

	-- Physics properties
	self.fallSpeed = 0.05
	self.xVelocity = -0.5
	self.yVelocity = self.fallSpeed
	self.maxXSpeed = 2
	self.maxYSpeed = 5
	self.acceleration = 0.05
	self.friction = 0.95
	self.yInertia = 0
	self.maxFallSpeed = 0.7
	self.frictionAccel = 0.3

	-- Player State
	self.touchingGround = false
	self.touchingCeiling = false
	self.touchingWall = false
	Player.dead = false
	Player.fullBat = 10000
	Player.bat = Player.fullBat
	Player.dischargeRate = 1
	Player.moveRight = false
	Player.moveLeft = false -- TODO: refactor to use a single variable
	self.lastDirection = nil
	self.targetDone = false


	self.debug = Noble.Settings.get("debug")
end

function Player:handleInput()
	if Player.bat <= 0 or Player.dead then
		self.yInertia = self.yInertia * self.friction
		return
	end

	local crankChange, acceleratedChange = playdate.getCrankChange()

	-- X velocity
	if not playdate.buttonIsPressed(playdate.kButtonLeft) and not playdate.buttonIsPressed(playdate.kButtonRight) then
		self.xVelocity = self.xVelocity * self.friction
		Player.moveRight = false
		Player.moveLeft = false
		self.animation:setState("down")
	end

	local isChangingDirection = false
	if playdate.buttonIsPressed(playdate.kButtonLeft) then
		if self.lastDirection == "right" then
			isChangingDirection = true
		end
		self:changeToRunState("left")
		self.lastDirection = "left"
		Player.moveLeft = true
		Player.moveRight = false
		self.xVelocity = self.xVelocity - self.acceleration
	elseif playdate.buttonIsPressed(playdate.kButtonRight) then
		if self.lastDirection == "left" then
			isChangingDirection = true
		end
		self:changeToRunState("right")
		Player.moveRight = true
		Player.moveLeft = false
		self.lastDirection = "right"
		self.xVelocity = self.xVelocity + self.acceleration
	end

	if isChangingDirection then
		self.xVelocity = self.xVelocity * self.frictionAccel
	end

	-- Y velocity
	if crankChange ~= 0 then
		if Player.moveRight == false and Player.moveLeft == false then
			if crankChange < 0 then          -- TODO: animation depending on inertia
				if self.animation.current == "down" then -- TODO: ABSOLUTE BULLSHIT
					self.animation:setState("up")
				end
			else
				if self.animation.current == "up" then
					self.animation:setState("down")
				end
			end
		end
		self.yInertia = self.yInertia - (acceleratedChange * 0.02)
	else
		self.yInertia = self.yInertia * self.friction
	end
end

function Player:changeToRunState(direction)
	if direction == "left" then
		self.animation.direction = Noble.Animation.DIRECTION_LEFT
		self.animation:setState("run")
	elseif direction == "right" then
		self.animation.direction = Noble.Animation.DIRECTION_RIGHT
		self.animation:setState("run")
	end
end

function Player:boom(collisionObject)
	Player.dead = true
	self.animation:setState("boom")

	local particleB = ParticlePoly(self.x, self.y)
	particleB:setThickness(2)
	particleB:setAngular(-15, 15)
	particleB:setSize(1, 2)
	particleB:setSpeed(1, 3)
	particleB:setMode(Particles.modes.STAY)
	particleB:setBounds(0, 0, 400, 240)
	particleB:setColour(Graphics.kColorXOR)
	particleB:add(20)
	if collisionObject then
		collisionObject:remove()
	end
end

function Player:handleMovementAndCollisions()
	if Player.dead then
		return
	end

	local xVel = self.xVelocity
	local yVel = self.yVelocity + self.yInertia + self.fallSpeed

	if (self.x < 20 and xVel < 0) or (self.x > 380 and xVel > 0) then -- Screen bounds
		xVel = 0
	end

	if self.y < -20 and yVel < 0 and Player.bat > 0 then
		yVel = 0
	end

	local _, _, collisions, length = self:checkCollisions(self.x + xVel, self.y + yVel)

	self.touchingGround = false
	self.touchingCeiling = false
	self.touchingWall = false

	for i = 1, length do
		local collision = collisions[i]
		local collisionType = collision.type
		local collisionObject = collision.other
		local collisionTag = collisionObject:getTag()
		if collisionType == Graphics.sprite.kCollisionTypeSlide then
			if collision.normal.y == -1 then
				self.touchingGround = true
			elseif collision.normal.y == 1 then
				self.touchingCeiling = true
			end

			if collision.normal.x ~= 0 then
				self.touchingWall = true
			end
		end

		if collisionTag == 3 then -- Ground
			self:boom()
			return
		elseif collisionTag == 154 then -- Baleba
			self:boom(collisionObject)
			return
		elseif collisionTag == 2 then -- Tank
			self:boom()
			BigBoom()

			self.targetDone = true
			local particleC = ParticlePoly(collisionObject.x, collisionObject.y)
			particleC:setThickness(5)
			particleC:setAngular(-15, 15)
			particleC:setSize(1, 10)
			particleC:setSpeed(1, 3)
			particleC:setMode(Particles.modes.STAY)
			particleC:setBounds(0, 0, 400, 240)
			particleC:setColour(Graphics.kColorXOR)
			particleC:add(50)
			collisionObject:fadeout()
			return
		end
	end

	self:moveTo(self.x + xVel, self.y + yVel)
end

function Player:handleDischarge(state)
	if Player.dead then
		return
	end
	if Player.bat <= 0 then
		Player.bat = 0
		self.fallSpeed = 2
		self.animation:setState("down")
		return
	end

	if Player.bat < 5000 then
		self.fallSpeed = self.maxFallSpeed
	end

	if Player.bat < 3000 then
		self.frictionAccel = 0.4
	end

	if state == "run" then
		Player.dischargeRate = 5
	elseif state == "up" then
		Player.dischargeRate = 10
	else
		Player.dischargeRate = 1
	end

	if self.y < 5 then
		Player.dischargeRate = 20
	end

	Player.bat = Player.bat - Player.dischargeRate
end

function Player:update()
	self:handleMovementAndCollisions()
	if Player.dead then
		return
	end

	self:handleDischarge(self.animation.currentName)
	self:handleInput()
end

function Player:isMovingRight()
	return Player.moveRight
end

function Player:isDead()
	return Player.dead
end

function Player:getBat()
	return Player.bat
end
