-----------------------------
-- Levak ©2014 --------------
-- http://levak.free.fr/ ----
-- levak92@gmail.com --------
-----------------------------

Grid = class()
function Grid:init(x, y, s)
   self.x, self.y, self.s = x, y, s
   self.grid = {}
   self.dead = {}

   local i
   for i = 1, s do
      self.grid[i] = {}
   end

   self.null = Tile(1, 1, 0)

   self:addRandomTile()
   self:addRandomTile()
end

function Grid:getEmptySlots()
   local t = {}
   local grid = self.grid

   local i, j
   for i = 1, self.s do
      for j = 1, self.s do
         if grid[i][j] == nil then
            t[#t+1] = {x=i, y=j}
         end
      end
   end

   return t
end

function Grid:canMove()
   local i, j
   local s = self.s

   for i = 1, s do
      for j = 1, s do
         local tile = self.grid[i][j]
         local value = tile and tile.data.value or 0
         if value == 0 then
            return true
         elseif i > 1
         and self.grid[i-1][j]
         and self.grid[i-1][j].data.value == value then
            return true
         elseif j > 1
         and self.grid[i][j-1]
         and self.grid[i][j-1].data.value == value then
            return true
         elseif i < s
         and self.grid[i+1][j]
         and self.grid[i+1][j].data.value == value then
            return true
         elseif j < s
         and self.grid[i][j+1]
         and self.grid[i][j+1].data.value == value then
            return true
         end
      end
   end
   return false
end

function Grid:addRandomTile(delay)
   local t = self:getEmptySlots()

   if #t > 0 then
      local coord = t[math.random(#t)]
      local value = math.random(10) == 1 and 4 or 2
      local tile = Tile(coord.x, coord.y, value)
      self.grid[coord.x][coord.y] = tile
      tile.scale = 0
      tile:Delay(delay or 0):Animate({scale=1}, TILE_ANIM)
      return tile
   end
end

function Grid:moveTile(tile, x, y)
   self.grid[x][y] = tile
   self.grid[tile.data.x][tile.data.y] = nil
   tile.data.x, tile.data.y = x, y
end

function Grid:killTile(tile)
   self.dead[#self.dead + 1] = tile
   self.grid[tile.data.x][tile.data.y] = nil
end

function Grid:buryTile(tile)
   local i
   for i = 1, #self.dead do
      if self.dead[i] == tile then
         table.remove(self.dead, i)
         break
      end
   end
end

function Grid:paint(gc)
   local x, y = self.x * ww, self.y * wh
   local m = 4/320 * ww

   gc:setColorRGB(187, 173, 160)
   fillRoundedRect(gc, x, y,
                   self.s*TILE_SIZE + m,
                   self.s*TILE_SIZE + m, m)

   x, y = x + m/2, y + m/2

   local i, j
   local grid = self.grid

   for i = 1, self.s do
      for j = 1, self.s do
         self.null.x, self.null.y = (i-1)*TILE_SIZE/ww, (j-1)*TILE_SIZE/ww
         self.null:paint(gc, x, y)
      end
   end

   for i = 1, self.s do
      for j = 1, self.s do
         if grid[i][j] then
            grid[i][j]:paint(gc, x, y)
         end
      end
   end

   for i = 1, #self.dead do
      self.dead[i]:paint(gc, x, y)
   end
end

local dir2vect = {
   left = {x=-1, y=0},
   right= {x=1,  y=0},
   down = {x=0,  y=1},
   up   = {x=0,  y=-1}
}

function Grid:getFarPos(tile, dir)
   local s = self.s
   local vect = dir2vect[dir]
   local x, y = tile.data.x + vect.x, tile.data.y + vect.y

   while x > 0 and x <= s and y > 0 and y <= s do
      if self.grid[x][y] then
         break
      end

      x = x + vect.x
      y = y + vect.y
   end

   return x - vect.x, y - vect.y
end

function Grid:doesMerge(tile, dir)
   local s = self.s
   local vect = dir2vect[dir]
   local x, y = tile.data.x - vect.x, tile.data.y - vect.y

   while x > 0 and x <= s and y > 0 and y <= s do
      local t = self.grid[x][y]
      if t then
         return t.data.value == tile.data.value and t or nil
      end

      x = x - vect.x
      y = y - vect.y
   end

   return nil
end

local dir2iter = {
   right= {i=-1, s=false},
   left = {i=1,  s=false},
   up   = {i=1,  s=true},
   down = {i=-1, s=true}
}

function Grid:getTiles(dir)
   local s = self.s
   local iter = dir2iter[dir]
   local i, j
   if iter.s then
      i, j = 1, iter.i > 0 and 1 or s
   else
      i, j = iter.i > 0 and 1 or s, 1
   end
   local grid = self.grid

   if iter.s then
      return function()
         local ret = nil
         while not ret do
            if i < 1 or i > s then
               j, i = j + iter.i, 1
               if j < 1 or j > s then
                  break
               end
            end
            ret = grid[i][j]
            i = i + 1
         end
         return ret
      end
   else
      return function()
         local ret = nil
         while not ret do
            if j < 1 or j > s then
               i, j = i + iter.i, 1
               if i < 1 or i > s then
                  break
               end
            end
            ret = grid[i][j]
            j = j + 1
         end
         return ret
      end
   end
end