local consts = require 'constants'

local Tile = {}
Tile.__index = Tile

local spritesheet

local tileActions = {}

function tileActions:showMessage(state)
   state.message = self.signText or 'default text'
end

function tileActions:loadArea(state)
   if self.target then
      state:loadMap(self.target)
      state.char.x = self.targetX * consts.tileSize
      state.char.y = self.targetY * consts.tileSize
   else
      state.message = 'No target assigned to this door!'
   end
end

function Tile:new(name)
   if not spritesheet then
      spritesheet = love.graphics.newImage('tiles/spritesheet.png')
   end

   local f = io.open('tiles/' .. name .. '.lua')
   local t
   if f then
      t = load('return' .. f:read('*all'))()
      f:close()
      if t.animation then
         t.totalFrames = #t.animation
         t.quads = {}
         for i = 1, t.totalFrames do
            t.quads[i] = love.graphics.newQuad(
               t.animation[i][1] * consts.tileSize,
               t.animation[i][2] * consts.tileSize,
               consts.tileSize,
               consts.tileSize,
               spritesheet:getDimensions())
         end
         t.draw = tileAnimation
         t.drawPx = tileAnimationPx
      else
         t.quad = love.graphics.newQuad(
            t.spriteX * consts.tileSize,
            t.spriteY * consts.tileSize,
            consts.tileSize,
            consts.tileSize,
            spritesheet:getDimensions())
      end
   else
      t = {
         name   = name,
         sprite = love.graphics.newImage('tiles/' .. name .. '.png'),
         pass   = name == 'grass',
         draw   = function(self, x, y)
            love.graphics.draw(self.sprite, x * consts.tileSize, y * consts.tileSize)
         end
      }
   end
   return setmetatable(t, Tile)
end

function Tile:action(self, state)
end

function Tile:enterTile(self, state)
end

function Tile.mkCopy(self)
   local t = {}
   for k, v in pairs(self) do
      t[k] = v
   end
   return setmetatable(t, Tile)
end

function tileAnimation(self, x, y, t)
   local tM = math.floor(t / 40) % self.totalFrames

   return self.quads[tM + 1], x * consts.tileSize, y * consts.tileSize
end

function tileAnimationPx(self, x, y, t)
   local tM = math.floor(t / 8) % self.totalFrames

   return self.quads[tM + 1], x, y
end

function Tile:draw(x, y)
   return self.quad, x * consts.tileSize, y * consts.tileSize
end

function Tile:drawPx(x, y)
   return self.quad, x, y
end

local tileCache = {}

local function getTile(name)
   tileCache[name] = tileCache[name] or Tile:new(name)
   return tileCache[name]
end

local function newSprite(data, n)
   if not spritesheet then
      spritesheet = love.graphics.newImage('tiles/spritesheet.png')
   end

   local tileset = data.tilesets[1]

   local x = (n - 1) % 32
   local y = math.floor((n - x - 1) / 32)
   local t = {
      name = 'unknown',
   }
   for prop, val in pairs(tileset.tiles[n].properties) do
      t[prop] = val
   end

   if t.action then
      t.action = tileActions[t.action]
   end
   if t.enterTile then
      t.enterTile = tileActions[t.enterTile]
   end

   t.quad = love.graphics.newQuad(
      x * consts.tileSize,
      y * consts.tileSize,
      consts.tileSize,
      consts.tileSize,
      spritesheet:getDimensions())

   return setmetatable(t, Tile)
end

local function getSprite(data, n)
   tileCache[n] = tileCache[n] or newSprite(data, n)
   return tileCache[n]
end

local function copyTile(name)
   return Tile:new(name)
end

local function allTiles()
   return tileCache
end

local nilTile = {
   pass = false,
   name = 'nil',
   draw = function(self, x, y) end,
   drawPx = function(self, x, y) end,
   action = function() end,
}

return {
   getTile = getTile,
   getSprite = getSprite,
   copyTile = copyTile,
   allTiles = allTiles,
   nilTile = nilTile,
}
