local consts = require 'constants'
local tile = require 'tile'

local Board = {}
Board.__index = Board

function Board:new()
   local t = {
      base = {},
      entity = {},
   }

   for y = 0, consts.boardHeight - 1 do
      for x = 0, consts.boardWidth - 1 do
         t.base[y * consts.boardWidth + x] = tile.getTile('grass')
         t.entity[y * consts.boardWidth + x] = {}
      end
   end

   return setmetatable(t, Board)
end

function loadBoard(filename)
   local f = io.open(filename)
   if f then
      data = load('return ' .. f:read('*all'))()
      local t = {
         base = {},
         entity = {},
      }

      for y = 0, consts.boardHeight - 1 do
         for x = 0, consts.boardWidth - 1 do
            local n = y * consts.boardWidth + x
            for k, v in pairs(data[n+1]) do
               if k == 1 then
                  t.base[n] = tile.getTile(v)
               elseif k == 2 then
                  t.entity[n] = tile.copyTile(v)
               else
                  t.entity[n][k] = v
               end
            end
         end

      end

      return setmetatable(t, Board)
   end
end

function loadBoardFromTiled(filename)
   local data = loadfile(filename)()

   local t = {
      base = {},
      entity = {},
   }

   for x = 0, consts.boardWidth - 1 do
      for y = 0, consts.boardHeight - 1 do
         local n = y * consts.boardWidth + x

         local baseT = data.layers[1].data[n + 1]
         if baseT ~= 0 then
            t.base[n] = tile.getSprite(data, baseT)
         end

         local entityT = data.layers[2].data[n + 1]
         if entityT ~= 0 then
            t.entity[n] = t.entity[n] or {}
            table.insert(t.entity[n], tile.getSprite(data, entityT))
         end

         local decorT = data.layers[3].data[n + 1]
         if decorT ~= 0 then
            t.entity[n] = t.entity[n] or {}
            table.insert(t.entity[n], tile.getSprite(data, decorT))
         end

      end
   end

   for i, meta in pairs(data.layers[4].objects) do
      local gx = math.floor(meta.x / consts.tileSize)
      local gy = math.floor(meta.y / consts.tileSize)
      local n = gy * consts.boardWidth + gx

      if t.entity[n] then
         local c = t.entity[n][1]:mkCopy()
         for prop, val in pairs(meta.properties) do
            c[prop] = val
         end
         t.entity[n][1] = c
      end
   end

   t.south = data.properties.south
   t.north = data.properties.north
   t.east = data.properties.east
   t.west = data.properties.west
   return setmetatable(t, Board)
end

function Board:enterSquare(x, y, state)
   if 0 <= x and x < consts.boardWidth then
      local e = self.entity[y * consts.boardWidth + x]
      if e and e[1] then e[1]:enterTile(state) end
   end
end

function Board:offside(x, y)
   if x < 0 then
      return 'west'
   elseif x >= consts.boardWidth then
      return 'east'
   elseif y < 0 then
      return 'north'
   elseif y >= consts.boardHeight then
      return 'south'
   end
end

function Board:lookup(x, y)
   if 0 <= x and x < consts.boardWidth then
      return self.base[y * consts.boardWidth + x]
   end
end

function Board:set(x, y, r)
   self.base[y * consts.boardWidth + x] = r
end

function Board:lookupEntity(x, y)
   if 0 <= x and x < consts.boardWidth then
      return self.entity[y * consts.boardWidth + x]
   end
end

function Board:setEntity(x, y, r)
   self.entity[y * consts.boardWidth + x] = r
end

function Board:passable(x, y)
   local t, e = self:lookup(x, y), self:lookupEntity(x, y)
   if e and e[1] then
      return t and t.pass and e[1].pass
   else
      return t and t.pass
   end
end

return {
   Board = Board,
   loadBoard = loadBoard,
   loadBoardFromTiled = loadBoardFromTiled,
}
