-----------------------------------
-- The different colors of the tank
-----------------------------------
TankColor =
{
   YELLOW = 0,
   RED = 1,
   BLUE = 2,
   GREEN = 3,
}


---------------------------------------
-- The different directions of the tank
---------------------------------------
TankDirection =
{
   LEFT = 0,
   RIGHT = 1,
   UP = 2,
   DOWN = 3,
}


---------------------
-- Creates a new tank
---------------------
function newTank(name, tileX, tileY, dir, color)
   local o = {}
   
   
   --------------------------
   -- Constructor of the tank
   --------------------------
   function o:construct(name, tileX, tileY, dir, color)
      self.name = name
      self.x, self.y = convertTileToPixels(tileX, tileY)
      self.dir = dir
      self.moveDirX = 0
      self.moveDirY = 0
      self.wheelState = 0
      
      self.collectedBonuses = {}
      self.speed = boombastic.tankSpeed
      self.bombPower = 1
      self.numBombs = 1
      self.numRockets = 0
      self.alive = true
      self.victories = 0
      
      self.initTileX = tileX
      self.initTileY = tileY
      self.initDir = dir
      
      -- Load the sprites of the tank
      self.sprites = {}
      for i = 0, 7 do
         self.sprites[i] = newSprite("images/sprites.png",
                                     i * boombastic.tileSize, 152 + color * boombastic.tileSize,
                                     boombastic.tileSize, boombastic.tileSize)
         self.sprites[i]:setCenter(0.5, 0.5)
      end
      
      return self
   end
   
   
   -------------------
   -- Relives the tank
   -------------------
   function o:relive()
      self.x, self.y = convertTileToPixels(self.initTileX, self.initTileY)
      self.dir = self.initDir
      self.moveDirX = 0
      self.moveDirY = 0
      self.wheelState = 0
      self.collectedBonuses = {}
      self.speed = boombastic.tankSpeed
      self.bombPower = 1
      self.numBombs = 1
      self.numRockets = 0
      self.alive = true
   end
   
   
   -----------------------------------------
   -- Checks whether the tank is still alive
   -----------------------------------------
   function o:isAlive()
      return self.alive
   end
   
   
   --------------------------------------------------------------
   -- Makes the tank win (increments the number of its victories)
   --------------------------------------------------------------
   function o:win()
      self.victories = self.victories + 1
   end
   
   
   -----------------
   -- Kills the tank
   -----------------
   function o:kill()
      self.alive = false
      
      -- Distribute randomly the collected bonuses on the map
      for bonusType, numBonuses in pairs(self.collectedBonuses) do
         for i = 1, numBonuses do
            boombastic.bonuses:addBonusAtRandomPosition(bonusType)
         end
      end
      
      -- Add an explosion of fire
      boombastic.particleSystems:addFireExplosion(self.x, self.y)
   end
   
   
   ----------------------------------------
   -- Moves the tank in the given direction
   ----------------------------------------
   function o:move(dir)
      local dirs = { { -1, 0 }, { 1, 0 }, { 0, -1 }, { 0, 1 } }
      self.moveDirX = dirs[dir + 1][1]
      self.moveDirY = dirs[dir + 1][2]
      self.dir = dir
   end
   
   
   -----------------------------------------------------
   -- Makes the tank drop a bomb at its current position
   -----------------------------------------------------
   function o:dropBomb()
      if (self.alive and self.numBombs > 0) then
         local tileX, tileY = convertPixelsToTile(self.x, self.y)
         if (boombastic.bombs:addBomb(tileX, tileY, self.bombPower, self)) then
            self.numBombs = self.numBombs - 1
         end
      end
   end
   
   
   ---------------------------------------------------------
   -- Makes the tank launch a rocket at its current position
   ---------------------------------------------------------
   function o:launchRocket()
      if (self.alive and self.numRockets > 0) then
         local tileX, tileY = convertPixelsToTile(self.x, self.y)
         boombastic.rockets:addRocket(tileX, tileY, self.dir, self.bombPower, self)
         self.numRockets = self.numRockets - 1
      end
   end
   
   
   --------------------------------------------------------------------
   -- Handles collisions of the tank against the rocks, bombs, walls...
   --------------------------------------------------------------------
   function o:collide(prevX, prevY)
      local prevTileX, prevTileY = convertPixelsToTile(prevX, prevY)
      local prevCenterX, prevCenterY = convertTileToPixels(prevTileX, prevTileY)
      
      local moveX = self.x - prevX
      local moveY = self.y - prevY
      
      -- Collision with the left tile?
      if (moveX < 0) then
         local edgeX = self.x - 0.5 * boombastic.tileSize
         local edgeTileX, edgeTileY = convertPixelsToTile(edgeX, self.y)
         if (edgeTileX ~= prevTileX and tileIsBlocking(edgeTileX, edgeTileY)) then
            self.x = math.min(prevX, prevCenterX)
         end
      
      -- Collision with the right tile?
      elseif (moveX > 0) then
         local edgeX = self.x + 0.5 * boombastic.tileSize
         local edgeTileX, edgeTileY = convertPixelsToTile(edgeX, self.y)
         if (edgeTileX ~= prevTileX and tileIsBlocking(edgeTileX, edgeTileY)) then
            self.x = math.max(prevX, prevCenterX)
         end
      end
      
      -- Collision with the top tile?
      if (moveY < 0) then
         local edgeY = self.y - 0.5 * boombastic.tileSize
         local edgeTileX, edgeTileY = convertPixelsToTile(self.x, edgeY)
         if (edgeTileY ~= prevTileY and tileIsBlocking(edgeTileX, edgeTileY)) then
            self.y = math.min(prevY, prevCenterY)
         end
      
      -- Collision with the bottom tile?
      elseif (moveY > 0) then
         local edgeY = self.y + 0.5 * boombastic.tileSize
         local edgeTileX, edgeTileY = convertPixelsToTile(self.x, edgeY)
         if (edgeTileY ~= prevTileY and tileIsBlocking(edgeTileX, edgeTileY)) then
            self.y = math.max(prevY, prevCenterY)
         end
      end
   end
   
   
   ------------------------------------------------------
   -- Updates the tank (move it in the correct direction)
   ------------------------------------------------------
   function o:update(dt)
      if (not self.alive) then
         return
      end
   
      local moveAmount = self.speed * dt
      local oldX = self.x
      local oldY = self.y
      
      
      -- Move the tank horizontally
      if (self.moveDirX ~= 0) then
         local prevX = self.x
         local prevY = self.y
         local orientation = self.moveDirX
         
         while (moveAmount > 0) do
            local pathPos = boombastic.map:getNearestHorizontalPath(self.x, self.y)
            local pathDistance = math.abs(self.y - pathPos)
            
            -- If we are already on a a path, we just move in the right direction
            if (pathDistance == 0) then
               self.x = self.x + orientation * moveAmount
            
            -- Otherwise, we move the tank to the nearest horizontal path
            elseif (pathDistance < boombastic.moveThreshold) then
               if (self.y > pathPos) then
                  self.y = math.max(pathPos, self.y - moveAmount)
               else
                  self.y = math.min(pathPos, self.y + moveAmount)
               end
            end
            
            self:collide(prevX, prevY)
            
            local distance = math.abs(self.x - prevX) + math.abs(self.y - prevY)
            if (math.abs(distance) > 0.01) then
               moveAmount = moveAmount - distance
            else
               break
            end
         end
      
      
      -- Move the tank vertically
      elseif (self.moveDirY ~= 0) then
         local prevX = self.x
         local prevY = self.y
         local orientation = self.moveDirY
         
         while (moveAmount > 0) do
            local pathPos = boombastic.map:getNearestVerticalPath(self.x, self.y)
            local pathDistance = math.abs(self.x - pathPos)
            
            -- If we are already on a a path, we just move in the right direction
            if (pathDistance == 0) then
               self.y = self.y + orientation * moveAmount
            
            -- Otherwise, we move the tank to the nearest vertical path
            elseif (pathDistance < boombastic.moveThreshold) then
               if (self.x > pathPos) then
                  self.x = math.max(pathPos, self.x - moveAmount)
               else
                  self.x = math.min(pathPos, self.x + moveAmount)
               end
            end
            
            self:collide(prevX, prevY)
            
            local distance = math.abs(self.x - prevX) + math.abs(self.y - prevY)
            if (math.abs(distance) > 0.01) then
               moveAmount = moveAmount - distance
            else
               break
            end
         end
      end
      
      
      -- Update the appearance of the tank if it has moved
      if (self.x ~= oldX or self.y ~= oldY) then
         if (math.abs(self.x - oldX) > math.abs(self.y - oldY)) then
            self.dir = (self.x < oldX) and TankDirection.LEFT or TankDirection.RIGHT
         else
            self.dir = (self.y < oldY) and TankDirection.UP or TankDirection.DOWN
         end
         self.wheelState = (self.wheelState + 1) % 2
      end
      
      
      -- Pick-up a bonus
      local tileX, tileY = convertPixelsToTile(self.x, self.y)
      boombastic.bonuses:applyBonus(tileX, tileY, self)
      
      
      self.moveDirX = 0
      self.moveDirY = 0
   end
   
   
   -----------------
   -- Draws the tank
   -----------------
   function o:draw()
      if (self:isAlive()) then
         local sprite = self.sprites[2 * self.dir + self.wheelState]
         sprite:draw(boombastic.origX + self.x, boombastic.origY + self.y)
      end
   end
   
   
   return o:construct(name, tileX, tileY, dir, color)
end
