-- Copyright (C) 2019-2024  Igara Studio S.A.
--
-- This file is released under the terms of the MIT license.
-- Read LICENSE.txt for more information.

-- This version of Aseprite doesn't support tilemaps
if TilesetMode == nil then return end

dofile('./test_utils.lua')

local rgba = app.pixelColor.rgba

-- Check constants
assert(TilemapMode.PIXELS == 0)
assert(TilemapMode.TILES == 1)
assert(TilesetMode.MANUAL == 0)
assert(TilesetMode.AUTO == 1)
assert(TilesetMode.STACK == 2)

----------------------------------------------------------------------
-- Tests drawing in the tilemap
----------------------------------------------------------------------

do
  local spr = Sprite(32, 32, ColorMode.INDEXED)
  spr.gridBounds = Rectangle{ 0, 0, 4, 4 }
  assert(spr.layers[1].isImage)
  assert(not spr.layers[1].isTilemap)

  ----------------------------------------------------------------------
  -- Create a tilemap
  ----------------------------------------------------------------------

  app.command.NewLayer{ tilemap=true }
  assert(#spr.layers == 2)
  local tilemapLay = spr.layers[2]
  assert(tilemapLay.isImage)
  assert(tilemapLay.isTilemap)
  assert(#tilemapLay.cels == 0)

  ----------------------------------------------------------------------
  -- Draw the first pixel on the tilemap so a new tile is created
  ----------------------------------------------------------------------

  app.useTool{
    tool='pencil',
    color=1,
    layer=tilemapLay,
    tilesetMode=TilesetMode.STACK,
    points={ Point(1, 2), Point(2, 2) }}
  assert(#tilemapLay.cels == 1)
  assert(tilemapLay:cel(1).image.colorMode == ColorMode.TILEMAP)
  local tilemapCel = tilemapLay:cel(1)
  assert(tilemapCel.bounds == Rectangle(0, 0, 4, 4))

  assert(#spr.tilesets == 1) -- one tileset
  assert(spr.tilesets[1] == tilemapLay.tileset)

  local tileset = tilemapLay.tileset
  assert(#tileset == 2) -- empty tile + our tile
  assert(tileset:getTile(0):isEmpty())
  assert(not tileset:getTile(1):isEmpty())
  expect_img(tileset:getTile(1), { 0,0,0,0,
                                   0,0,0,0,
                                   0,1,1,0,
                                   0,0,0,0 })

  tilemapCel.position = Point(2, 2)
  assert(tilemapCel.bounds == Rectangle(2, 2, 4, 4))
  assert(#tileset == 2)

  assert(tilemapCel.image.width == 1)
  assert(tilemapCel.image.height == 1)
  assert(tilemapCel.image:getPixel(0, 0) == 1)

  ----------------------------------------------------------------------
  -- Draw a second pixel with locked mode (new tiles are not generated)
  ----------------------------------------------------------------------

  app.useTool{
    tool='pencil',
    color=1,
    cel=tilemapCel,
    tilesetMode=TilesetMode.MANUAL,
    points={ Point(0, 0) }}

  assert(tilemapCel.bounds == Rectangle(2, 2, 4, 4))
  assert(#tileset == 2)

  ----------------------------------------------------------------------
  -- Draw pixels generating new tiles
  ----------------------------------------------------------------------

  app.useTool{
    tool='pencil',
    color=1,
    cel=tilemapCel,
    tilesetMode=TilesetMode.STACK,
    points={ Point(0, 0) }}

  assert(tilemapCel.bounds == Rectangle(-2, -2, 8, 8))
  assert(#tileset == 3)
  assert(tileset:getTile(0):isEmpty())
  assert(not tileset:getTile(1):isEmpty())
  assert(not tileset:getTile(2):isEmpty())
  expect_img(tilemapCel.image, { 2, 0,
                                 0, 1 })

  app.useTool{
    tool='pencil',
    color=1,
    cel=tilemapCel,
    tilesetMode=TilesetMode.STACK,
    points={ Point(6, 6) }}

  assert(tilemapCel.bounds == Rectangle(-2, -2, 12, 12))
  assert(#tileset == 4)
  assert(not tileset:getTile(3):isEmpty())
  expect_img(tilemapCel.image, { 2, 0, 0,
                                 0, 1, 0,
                                 0, 0, 3 })

  ----------------------------------------------------------------------
  -- Draw in manual mode to modify existent tiles
  ----------------------------------------------------------------------

  assert(tilemapCel.bounds == Rectangle(-2, -2, 12, 12))
  assert(#tileset == 4)
  expect_img(tileset:getTile(1), { 0,0,0,0,
                                   0,0,0,0,
                                   0,1,1,0,
                                   0,0,0,0 })
  expect_img(tileset:getTile(2), { 0,0,0,0,
                                   0,0,0,0,
                                   0,0,1,0,
                                   0,0,0,0 })
  expect_img(tileset:getTile(3), { 1,0,0,0,
                                   0,0,0,0,
                                   0,0,0,0,
                                   0,0,0,0 })
  expect_img(tilemapCel.image, { 2, 0, 0,
                                 0, 1, 0,
                                 0, 0, 3 })

  app.useTool{
    tool='rectangle',
    color=2,
    cel=tilemapCel,
    tilesetMode=TilesetMode.MANUAL,
    points={ Point(0, 0), Point(9, 9) }}

  assert(tilemapCel.bounds == Rectangle(-2, -2, 12, 12))
  assert(#tileset == 4)
  expect_img(tileset:getTile(1), { 0,0,0,0,
                                   0,0,0,0,
                                   0,1,1,0,
                                   0,0,0,0 })
  expect_img(tileset:getTile(2), { 0,0,0,0,
                                   0,0,0,0,
                                   0,0,2,2,
                                   0,0,2,0 })
  expect_img(tileset:getTile(3), { 1,0,0,2,
                                   0,0,0,2,
                                   0,0,0,2,
                                   2,2,2,2 })
  expect_img(tilemapCel.image, { 2, 0, 0,
                                 0, 1, 0,
                                 0, 0, 3 })

  tilemapCel.position = Point(1, 1)
  app.useTool{
    tool='line',
    color=3,
    cel=tilemapCel,
    tilesetMode=TilesetMode.MANUAL,
    points={ Point(1, 1), Point(12, 12) }}

  assert(tilemapCel.bounds == Rectangle(1, 1, 12, 12))
  assert(#tileset == 4)
  expect_img(tileset:getTile(1), { 3,0,0,0,
                                   0,3,0,0,
                                   0,1,3,0,
                                   0,0,0,3 })
  expect_img(tileset:getTile(2), { 3,0,0,0,
                                   0,3,0,0,
                                   0,0,3,2,
                                   0,0,2,3 })
  expect_img(tileset:getTile(3), { 3,0,0,2,
                                   0,3,0,2,
                                   0,0,3,2,
                                   2,2,2,3 })
  expect_img(tilemapCel.image, { 2, 0, 0,
                                 0, 1, 0,
                                 0, 0, 3 })

end

----------------------------------------------------------------------
-- Tests of get/set app.fgTile and app.bgTile
----------------------------------------------------------------------

do
  local spr = Sprite(32, 32, ColorMode.INDEXED)
  spr.gridBounds = Rectangle{ 0, 0, 2, 2 }
  assert(spr.layers[1].isImage)
  assert(not spr.layers[1].isTilemap)

  ----------------------------------------------------------------------
  -- Create a tilemap
  ----------------------------------------------------------------------

  app.command.NewLayer{ tilemap=true }
  assert(#spr.layers == 2)
  app.layer = spr.layers[2]
  assert(app.layer.isImage)
  assert(app.layer.isTilemap)
  assert(#app.layer.cels == 0)

  ----------------------------------------------------------------------
  -- Draw something on the tilemap so new tiles are created
  ----------------------------------------------------------------------

  app.useTool{
    tool='pencil',
    color=1,
    tilesetMode=TilesetMode.STACK,
    points={ Point(2, 0), Point(4, 0), Point(5,1) }
  }
  assert(#app.layer.cels == 1)
  assert(app.layer:cel(1).image.colorMode == ColorMode.TILEMAP)
  local tilemapCel = app.layer:cel(1)
  assert(tilemapCel.bounds == Rectangle(2, 0, 4, 2))
  assert(#spr.layers[2].tileset == 3)

  app.fgTile = 1
  app.useTool{
    tool='pencil',
    tilemapMode=TilemapMode.TILES,
    points={ Point(0, 0), Point(5, 0)}
  }

  app.fgTile = 2
  app.useTool{
    tool='pencil',
    tilemapMode=TilemapMode.TILES,
    points={ Point(0, 2), Point(5, 2)}
  }

  local cel = spr.layers[2].cels[1]
  expect_img(cel.image, { 1,1,1,
                          2,2,2 })

  app.fgTile = 0
  app.useTool{
    tool='pencil',
    tilemapMode=TilemapMode.TILES,
    points={ Point(0, 0), Point(4, 0),
             Point(4, 2), Point(0, 2)}
  }

  local cel = spr.layers[2].cels[1]
  assert(cel == nil)
  local tileImg1 = spr.layers[2].tileset:tile(1).image
  expect_img(tileImg1, { 1,1,
                         0,0 })
  local tileImg2 = spr.layers[2].tileset:tile(2).image
  expect_img(tileImg2, { 1,0,
                         0,1 })
end

----------------------------------------------------------------------
-- Tests drawing in the tilemap with tiles
----------------------------------------------------------------------

do
  local spr = Sprite(32, 32)
  spr.gridBounds = Rectangle(0, 0, 8, 8)
  app.command.NewLayer{ tilemap=true }

  local tm = app.activeLayer
  local ts = tm.tileset
  assert(ts ~= nil)
  expect_eq(0, ts.grid.origin.x)
  expect_eq(0, ts.grid.origin.y)
  expect_eq(8, ts.grid.tileSize.width)
  expect_eq(8, ts.grid.tileSize.height)

  app.useTool{
    tool='pencil',
    color=Color{ r=0, g=0, b=0 },
    tilemapMode=TilemapMode.PIXELS,
    tilesetMode=TilesetMode.STACK,
    points={ Point(0, 0), Point(31, 31) }}

  local cel = tm.cels[1]
  expect_eq(2, #ts)
  expect_img(cel.image, { 1,0,0,0,
                          0,1,0,0,
                          0,0,1,0,
                          0,0,0,1 })

  app.useTool{
    tool='pencil',
    color=Color{ index=1 }, -- We can use a Color{ index } to paint tiles too
    tilemapMode=TilemapMode.TILES,
    tilesetMode=TilesetMode.STACK,
    points={ Point(0, 16) }} -- y=16 is the first pixel of 3rd row of tiles
  cel = tm.cels[1]
  expect_img(cel.image, { 1,0,0,0,
                          0,1,0,0,
                          1,0,1,0,
                          0,0,0,1 })

  app.useTool{
    tool='pencil',
    color=Color{ tile=1 }, -- Color{ tile } is the new constructor to paint tiles + flags
    tilemapMode=TilemapMode.TILES,
    tilesetMode=TilesetMode.STACK,
    points={ Point(0, 0), Point(16, 0) }} -- x=16 is the first pixel of 3rd column of tiles
  cel = tm.cels[1]
  expect_img(cel.image, { 1,1,1,0,
                          0,1,0,0,
                          1,0,1,0,
                          0,0,0,1 })

  -- Move layer origin to 10, 8
  cel.position = { 10, 8 }
  expect_eq(Point{ 10, 8 }, cel.position)
  app.useTool{
    tool='pencil',
    color=Color{ tile=0 },
    tilemapMode=TilemapMode.TILES,
    tilesetMode=TilesetMode.STACK,
    points={ { 10, 8 }, { 18, 16 } }} -- {10,8} is the first existent tile in the tilemap
                                      -- these are tiles 2,1 and 3,2
  cel = tm.cels[1]
  expect_img(cel.image, { 0,1,1,0,
                          0,0,0,0,
                          1,0,1,0,
                          0,0,0,1 })

  app.useTool{
    tool='pencil',
    color=Color{ tile=1 },
    tilemapMode=TilemapMode.TILES,
    tilesetMode=TilesetMode.STACK,
    points={ Point(1, 7), Point(2, 8) }} -- Tile 0,0 and 1,1

  cel = tm.cels[1]
  expect_img(cel.image, { 1,0, 0,0,0,0,
                          0,1, 0,1,1,0,
                          0,0, 0,0,0,0,
                          0,0, 1,0,1,0,
                          0,0, 0,0,0,1 })

end

----------------------------------------------------------------------
-- Tests drawing when the grid origin is in a negative position
----------------------------------------------------------------------

do
  local spr = Sprite(32, 32)
  app.command.NewLayer{ tilemap=true }
  local tilemapLay = spr.layers[2]

  app.useTool{
    tool='pencil',
    color=Color{ r=0, g=255, b=0 },
    layer=tilemapLay,
    tilesetMode=TilesetMode.STACK,
    points={ Point(0, 0) }}
  local tilemapCel = tilemapLay:cel(1)
  assert(tilemapCel.bounds == Rectangle(0, 0, 16, 16))

  local tileset = tilemapLay.tileset
  assert(#tileset == 2) -- empty tile + our tile

  tilemapCel.position = Point(-1, -1)
  assert(tilemapCel.bounds == Rectangle(-1, -1, 16, 16))

  app.useTool{
    tool='pencil',
    color=Color{ r=255, g=0, b=0 },
    cel=tilemapCel,
    tilesetMode=TilesetMode.STACK,
    points={ Point(0, 0) }}

  assert(tilemapCel.bounds == Rectangle(-1, -1, 16, 16))
  assert(#tileset == 3)

  local img = tileset:getTile(2)

  assert(img:getPixel(0, 0) == rgba(0, 255, 0, 255))
  assert(img:getPixel(1, 0) == rgba(0, 0, 0, 0))
  assert(img:getPixel(2, 0) == rgba(0, 0, 0, 0))

  assert(img:getPixel(0, 1) == rgba(0, 0, 0, 0))
  assert(img:getPixel(1, 1) == rgba(255, 0, 0, 255))
  assert(img:getPixel(2, 1) == rgba(0, 0, 0, 0))

  assert(img:getPixel(0, 2) == rgba(0, 0, 0, 0))
  assert(img:getPixel(1, 2) == rgba(0, 0, 0, 0))
  assert(img:getPixel(2, 2) == rgba(0, 0, 0, 0))
end

----------------------------------------------------------------------
-- Tests that extra tiles are not created
----------------------------------------------------------------------

do
  local spr = Sprite(32, 32)
  app.command.NewLayer{ tilemap=true }
  local tilemapLay = spr.layers[2]

  app.useTool{
    tool='pencil',
    color=Color{ r=0, g=255, b=0 },
    layer=tilemapLay,
    tilesetMode=TilesetMode.STACK,
    points={ Point(0, 0) }}
  local tilemapCel = tilemapLay:cel(1)
  tilemapCel.position = Point(-1, -1)

  app.useTool{
    tool='pencil',
    color=Color{ r=255, g=0, b=0 },
    cel=tilemapCel,
    tilesetMode=TilesetMode.STACK,
    points={ Point(30, 30) }}

  assert(tilemapCel.bounds == Rectangle(-1, -1, 32, 32))
end

----------------------------------------------------------------------
-- Tests moving & copying tiles
----------------------------------------------------------------------

do
  local spr = Sprite(32, 32)
  app.command.NewLayer{ tilemap=true }
  local tilemapLay = spr.layers[2]

  app.useTool{
    tool='ellipse',
    color=Color{ r=0, g=0, b=0 },
    layer=tilemapLay,
    tilesetMode=TilesetMode.STACK,
    points={ Point(0, 0), Point(31, 31) }}

  local ts = tilemapLay.tileset
  assert(#ts == 5)

  local imgs = { ts:getTile(1),
                 ts:getTile(2),
                 ts:getTile(3),
                 ts:getTile(4) }

  -- No op = move tile 1 before the tile 1, result=0,1,2,3,4
  app.range.tiles = { 1 }
  app.command.MoveTiles{ before=1 }
  expect_eq(imgs[1], ts:getTile(1))
  expect_eq(imgs[2], ts:getTile(2))
  expect_eq(imgs[3], ts:getTile(3))
  expect_eq(imgs[4], ts:getTile(4))
  expect_eq(1, #app.range.tiles)
  expect_eq(1, app.range.tiles[1])

  -- Move tile 2 before tile 1, result=0,2,1,3,4
  app.range.tiles = { 2 }
  app.command.MoveTiles{ before=1 }
  expect_eq(imgs[2], ts:getTile(1))
  expect_eq(imgs[1], ts:getTile(2))
  expect_eq(imgs[3], ts:getTile(3))
  expect_eq(imgs[4], ts:getTile(4))
  expect_eq(1, #app.range.tiles)
  expect_eq(1, app.range.tiles[1])
  app.undo()

  -- Move tiles 1 and 2 before 4, result=0,3,1,2,4
  app.range.tiles = { 1, 2 }
  app.command.MoveTiles({ before=4 })
  expect_eq(imgs[3], ts:getTile(1))
  expect_eq(imgs[1], ts:getTile(2))
  expect_eq(imgs[2], ts:getTile(3))
  expect_eq(imgs[4], ts:getTile(4))
  expect_eq(2, #app.range.tiles)
  expect_eq(2, app.range.tiles[1])
  expect_eq(3, app.range.tiles[2])
  app.undo()

  -- Move tiles 1 and 3 before 3, result=0,2,1,3,4
  app.range.tiles = { 1, 3 }
  app.command.MoveTiles({ before=3 })
  expect_eq(imgs[2], ts:getTile(1))
  expect_eq(imgs[1], ts:getTile(2))
  expect_eq(imgs[3], ts:getTile(3))
  expect_eq(imgs[4], ts:getTile(4))
  expect_eq(2, #app.range.tiles)
  expect_eq(2, app.range.tiles[1])
  expect_eq(3, app.range.tiles[2])
  app.undo()

  -- Copy tiles 1 before 1, result=0,1,1,2,3,4
  app.range.tiles = { 1 }
  app.command.CopyTiles({ before=1 })
  expect_eq(1, #app.range.tiles)
  expect_eq(1, app.range.tiles[1])
  expect_eq(6, #ts)
  assert(ts:getTile(0):isEmpty())
  assert(ts:getTile(1):isEqual(imgs[1]))
  assert(ts:getTile(2):isEqual(imgs[1]))
  assert(ts:getTile(3):isEqual(imgs[2]))
  assert(ts:getTile(4):isEqual(imgs[3]))
  assert(ts:getTile(5):isEqual(imgs[4]))
  app.undo()

  -- Copy tiles 1 before 4, result=0,1,2,3,1,4
  app.range.tiles = { 1 }
  app.command.CopyTiles({ before=4 })
  expect_eq(1, #app.range.tiles)
  expect_eq(4, app.range.tiles[1])
  expect_eq(6, #ts)
  assert(ts:getTile(0):isEmpty())
  assert(ts:getTile(1):isEqual(imgs[1]))
  assert(ts:getTile(2):isEqual(imgs[2]))
  assert(ts:getTile(3):isEqual(imgs[3]))
  assert(ts:getTile(4):isEqual(imgs[1]))
  assert(ts:getTile(5):isEqual(imgs[4]))
  app.undo()

  -- Copy tiles 1, 2, and 4, before 5, result=0,1,2,3,4,1,2,4
  app.range.tiles = { 1, 2, 4 }
  app.command.CopyTiles({ before=5 })
  assert(ts:getTile(0):isEmpty())
  assert(ts:getTile(1):isEqual(imgs[1]))
  assert(ts:getTile(2):isEqual(imgs[2]))
  assert(ts:getTile(3):isEqual(imgs[3]))
  assert(ts:getTile(4):isEqual(imgs[4]))
  assert(ts:getTile(5):isEqual(imgs[1]))
  assert(ts:getTile(6):isEqual(imgs[2]))
  assert(ts:getTile(7):isEqual(imgs[4]))
  app.undo()

  -- Copy tiles 2, and 4, before 1, result=0,2,4,1,2,3,4
  app.range.tiles = { 2, 4 }
  app.command.CopyTiles({ before=1 })
  assert(ts:getTile(0):isEmpty())
  assert(ts:getTile(1):isEqual(imgs[2]))
  assert(ts:getTile(2):isEqual(imgs[4]))
  assert(ts:getTile(3):isEqual(imgs[1]))
  assert(ts:getTile(4):isEqual(imgs[2]))
  assert(ts:getTile(5):isEqual(imgs[3]))
  assert(ts:getTile(6):isEqual(imgs[4]))
  app.undo()

  -- Copy tiles 1, and 4, before 5, result=0,1,2,3,4,1,4
  app.range.tiles = { 1, 4 }
  app.command.CopyTiles({ before=5 })
  assert(ts:getTile(0):isEmpty())
  assert(ts:getTile(1):isEqual(imgs[1]))
  assert(ts:getTile(2):isEqual(imgs[2]))
  assert(ts:getTile(3):isEqual(imgs[3]))
  assert(ts:getTile(4):isEqual(imgs[4]))
  assert(ts:getTile(5):isEqual(imgs[1]))
  assert(ts:getTile(6):isEqual(imgs[4]))
  app.undo()

  -- Copy tiles 1, and 4, before 6, result=0,1,2,3,4,-,1,4
  app.range.tiles = { 1, 4 }
  app.command.CopyTiles({ before=6 })
  assert(ts:getTile(0):isEmpty())
  assert(ts:getTile(1):isEqual(imgs[1]))
  assert(ts:getTile(2):isEqual(imgs[2]))
  assert(ts:getTile(3):isEqual(imgs[3]))
  assert(ts:getTile(4):isEqual(imgs[4]))
  assert(ts:getTile(5):isEmpty())
  assert(ts:getTile(6):isEqual(imgs[1]))
  assert(ts:getTile(7):isEqual(imgs[4]))
  app.undo()

  -- Copy tiles 3, and 4, before 8, result=0,1,2,3,4,-,-,-,3,4
  app.range.tiles = { 3, 4 }
  app.command.CopyTiles({ before=8 })
  assert(ts:getTile(0):isEmpty())
  assert(ts:getTile(1):isEqual(imgs[1]))
  assert(ts:getTile(2):isEqual(imgs[2]))
  assert(ts:getTile(3):isEqual(imgs[3]))
  assert(ts:getTile(4):isEqual(imgs[4]))
  assert(ts:getTile(5):isPlain())
  assert(ts:getTile(6):isPlain())
  assert(ts:getTile(7):isPlain())
  assert(ts:getTile(8):isEqual(imgs[3]))
  assert(ts:getTile(9):isEqual(imgs[4]))
  app.undo()

end

----------------------------------------------------------------------
-- Tests bug with alpha=0 and different RGB values
----------------------------------------------------------------------

do
  local spr = Sprite(32, 32)
  spr.gridBounds = Rectangle(0, 0, 2, 2)
  app.command.NewLayer{ tilemap=true }

  local tm = app.activeLayer
  local ts = tm.tileset
  expect_eq(1, #ts)

  app.useTool{
    tool='pencil',
    color=Color{ r=0, g=0, b=0, a=255 },
    tilemapMode=TilemapMode.PIXELS,
    tilesetMode=TilesetMode.STACK,
    points={ Point(0, 0), Point(3, 0) }}

  expect_eq(2, #ts)

  app.useTool{
    tool='pencil',
    color=Color{ r=255, g=0, b=0, a=0 },
    tilemapMode=TilemapMode.PIXELS,
    tilesetMode=TilesetMode.STACK,
    points={ Point(0, 0), Point(1, 0) }}

  -- If #ts is == 3, it means that the last useTool() with a r=255 a=0
  -- created a new tile, that shouldn't be the case (because a=0
  -- should ignore RGB values to compare tiles)
  expect_eq(2, #ts)

end

----------------------------------------------------------------------
-- Tests tiles dissapearing in AUTO mode
----------------------------------------------------------------------

do
  local spr = Sprite(32, 16, ColorMode.INDEXED)
  spr.gridBounds = Rectangle(0, 0, 2, 2)
  app.command.ConvertLayer{ to="tilemap" }
  app.useTool{ points={ Point(0, 0), Point(31, 31) }, color=1, tool="filled_ellipse", tilesetMode=TilesetMode.AUTO }
  app.useTool{ points={ Point(4, 4), Point(27, 27) }, color=2, tool="filled_ellipse", tilesetMode=TilesetMode.AUTO }
  app.useTool{ points={ Point(0, 0), Point(32, 16) }, color=3, tool="line", tilesetMode=TilesetMode.AUTO }
  app.useTool{ points={ Point(0, 0), Point(31, 8),
                        Point(0, 8), Point(31, 16) },
               brush=4, color=3, tool="pencil", tilesetMode=TilesetMode.AUTO }

  local i = spr.cels[1].image
  assert(i:getPixel(4, 6) ~= 0)
  assert(i:getPixel(8, 7) ~= 0)
end

-----------------------------------------------------------------------
-- Test CanvasSize with tilemaps when we trim out content
-----------------------------------------------------------------------

do
  local sprite = Sprite(32, 32)
  sprite.gridBounds = Rectangle(0, 0, 16, 16)

  -- Create a 2x2 tilemap with 4 tiles drawing an ellipse
  app.command.NewLayer{ tilemap=true }
  app.useTool{
    tool='filled_ellipse',
    color=Color{ r=255, g=255, b=0 },
    tilesetMode=TilesetMode.AUTO,
    points={ Point(0, 0), Point(31, 31) }
  }
  assert(#sprite.layers == 2)
  -- remove the unused layer image
  sprite:deleteLayer("Layer 1")
  assert(#sprite.layers == 1)

  local celMap = app.activeLayer.cels[1]
  assert(celMap.bounds == Rectangle(0, 0, 32, 32))

  -- resize the canvas to 16x16 starting point(4,9)
  -- covering a large part of the tile in 0,0
  app.command.CanvasSize{
    bounds=Rectangle(4,9,16,16),
    trimOutside=true
  }

  assert(#app.activeLayer.cels == 1)

  expect_eq(celMap.bounds, Rectangle(-4, -9, 32, 32))
  expect_eq(sprite.bounds.width, 16)
  expect_eq(sprite.bounds.height, 16)

  -- grid is 16x16, so expected image width/height is 2
  expect_eq(celMap.image.width, 2)
  expect_eq(celMap.image.height, 2)
  expect_eq(celMap.image:getPixel(0,0), 1)
  expect_eq(celMap.image:getPixel(1,0), 2)
  expect_eq(celMap.image:getPixel(0,1), 3)
  expect_eq(celMap.image:getPixel(1,1), 4)

  -- undo the last canvas resize
  app.command.Undo()

  -- reposition the cel in the sprite
  celMap.position = Point(0,16)
  expect_eq(celMap.bounds, Rectangle(0, 16, 32, 32))

  -- deleting the whole tilemap cel when it's outside the bounds
  app.command.CanvasSize {
    bounds=Rectangle(0,0,8,8),
    trimOutside=true
  }

  expect_eq(#app.activeLayer.cels, 0)
  expect_eq(sprite.bounds.width, 8)
  expect_eq(sprite.bounds.height, 8)

  -- undo canvas resize
  app.command.Undo()

  -- undo set position
  app.command.Undo()
  expect_eq(celMap.bounds, Rectangle(0, 0, 32, 32))

  app.command.CanvasSize{
    bounds=Rectangle(16,16,16,16),
    trimOutside=true
  }

  assert(celMap ~= nil)
  expect_eq(celMap.bounds.x, 0)
  expect_eq(celMap.bounds.y, 0)
  expect_eq(celMap.image.width, 1)
  expect_eq(celMap.image.height, 1)
  expect_eq(celMap.position, Point(0,0))
  expect_eq(celMap.image:getPixel(0,0), 4)
end

-----------------------------------------------------------------------
-- Test CanvasSize when the bounds of an image is shrunken
-----------------------------------------------------------------------

do
  local sprite = Sprite(32, 32)
  sprite.gridBounds = Rectangle(0, 0, 8, 8)

  app.command.NewLayer{ tilemap=true }
  local tilemapLayer = sprite.layers[2]

  -- tile 1
  app.useTool {
    tool='pencil', color=Color{r=255, b=0, g=255},
    layer=tilemapLayer,
    points={ Point(7,8), Point(0, 15) },
    tilesetMode=TilesetMode.AUTO
  }

  local celMap = tilemapLayer.cels[1]

  -- tile 2
  app.useTool {
    tool='pencil', color=Color{r=0, b=255, g=255},
    cel=celMap,
    points={ Point(24,16), Point(31, 23) },
    tilesetMode=TilesetMode.AUTO
  }

  -- tile 3
  app.useTool {
    tool='pencil', color=Color{r=0, b=0, g=255},
    cel=celMap,
    points={ Point(30,24), Point(24, 31) },
    tilesetMode=TilesetMode.AUTO
  }

  -- tile 4
  app.useTool {
    tool='pencil', color=Color{r=0, b=255, g=0},
    cel=celMap,
    points={ Point(17,24), Point(23, 31) },
    tilesetMode=TilesetMode.AUTO
  }

  -- crop this sprite a little below pixel(0,0)
  app.command.CanvasSize {
    bounds=Rectangle(8,16,20,16),
    trimOutside=true, ui=false
  }

  expect_eq(sprite.bounds.width, 20)
  expect_eq(sprite.bounds.height, 16)

  -- ideally, we expect a 3x2 image, however, some parts of top and left
  -- of the image are empty/notile, so it's optimized by shrunking it
  -- we got paddings instead.

  expect_eq(celMap.position, Point(8, 0))
  expect_eq(celMap.image.width, 2)
  expect_eq(celMap.image.height, 2)
  expect_eq(celMap.image:getPixel(0, 0), 0)
  expect_eq(celMap.image:getPixel(1, 0), 2)
  expect_eq(celMap.image:getPixel(1, 1), 3)
  expect_eq(celMap.image:getPixel(0, 1), 4)
end

----------------------------------------------------------------------
-- Tests canvas resizing in the tilemap with tiles 1x1
----------------------------------------------------------------------

do
  local sprite1 = Sprite(4, 4)
  -- Create a tilemap layer which grid size is 1x1
  sprite1.gridBounds = Rectangle(1, 1, 1, 1)
  app.command.NewLayer{ tilemap=true }
  -- Create a tilemap of 2x2 tiles:
  --   ______
  --  |  ∏∏  |
  --  |  ∏∏  |
  --  |      |
  --  |______|
  app.useTool{
    tool='pencil',
    color=Color{ r=255, g=0, b=0 },
    tilesetMode=TilesetMode.AUTO,
    points={ Point(1, 0) }
  }
  app.useTool{
    tool='pencil',
    color=Color{ r=0, g=255, b=0 },
    tilesetMode=TilesetMode.AUTO,
    points={ Point(2, 0) }
  }
  app.useTool{
    tool='pencil',
    color=Color{ r=0, g=0, b=255 },
    tilesetMode=TilesetMode.AUTO,
    points={ Point(1, 1) }
  }
  app.useTool{
    tool='pencil',
    color=Color{ r=255, g=255, b=255 },
    tilesetMode=TilesetMode.AUTO,
    points={ Point(2, 1) }
  }

  app.command.CanvasSize{
    ui=false,
    bounds=Rectangle(0,0,2,2),
    trimOutside=true
  }

  local cel = app.activeLayer.cels[1]
  expect_eq(cel.image.width, 1)
  expect_eq(cel.image.height, 2)
  expect_eq(cel.bounds, Rectangle(1, 0, 1, 2))

  app.command.CanvasSize{
    ui=false,
    bounds=Rectangle(0,0,2,1),
    trimOutside=true
  }

  cel = app.activeLayer.cels[1]
  expect_eq(cel.image.width, 1)
  expect_eq(cel.image.height, 1)
  expect_eq(cel.bounds, Rectangle(1, 0, 1, 1))
end

----------------------------------------------------------------------
-- Tests canvas resizing in the tilemap with tiles 2x2, origin 2,1
----------------------------------------------------------------------

do
  local sprite2 = Sprite(8, 8)
  -- Create a tilemap layer which grid size is 2x2
  sprite2.gridBounds = Rectangle(0, 0, 2, 2)
  app.command.NewLayer{ tilemap=true }
  -- Create a tilemap of 4x4 tiles:
  --
  --        ,--------- x = 2
  --       |
  --   ____v______
  --  |           |
  --  |    ∏∏XX   |<-- y = 1
  --  |    ∏∏XX   |
  --  |    OO∏∏   |
  --  |    OO∏∏   |
  --  |           |
  --  |           |
  --  |___________|

  -- Making a pixel to make a Cel
  app.useTool{
    tool='pencil',
    color=Color{ r=255, g=0, b=0 },
    tilesetMode=TilesetMode.AUTO,
    points={ Point(0, 0) }
  }

  -- Moving the Cel to de desired position
  app.activeLayer.cels[1].position = Point(2, 1)

  -- Filling with squares of 2x2
  app.useTool{
    tool='pencil',
    color=Color{ r=255, g=0, b=0 },
    tilesetMode=TilesetMode.AUTO,
    points={ Point(3, 1), Point(3, 2), Point(2, 2) }
  }

  app.useTool{
    tool='pencil',
    color=Color{ r=0, g=255, b=0 },
    tilesetMode=TilesetMode.AUTO,
    points={ Point(4, 1), Point(5, 1), Point(5, 2), Point(4, 2) }
  }

  app.useTool{
    tool='pencil',
    color=Color{ r=0, g=0, b=255 },
    tilesetMode=TilesetMode.AUTO,
    points={ Point(2, 3), Point(3, 3), Point(3, 4), Point(2, 4) }
  }

  app.useTool{
    tool='pencil',
    color=Color{ r=255, g=255, b=255 },
    tilesetMode=TilesetMode.AUTO,
    points={ Point(4, 3), Point(5, 3), Point(5, 4), Point(4, 4) }
  }

  -- ======================================================================
  -- Cutting the canvas in the midle of the tilemap:
  --
  --       ,--------- x = -1
  --      |
  --   ___v _______
  --  |    |  |    |
  --      ∏|∏X|X    <-- y = -1
  --  |----|--|----|
  --      ∏|∏X|X
  --      O|O∏|∏
  --  |----|--|----|
  --      O|O∏|∏
  --       |  |
  --       |  |
  --  |____|__|____|
  app.command.CanvasSize{
    ui=false,
    bounds=Rectangle(3,2,2,2),
    trimOutside=true
  }

  expect_eq(app.activeLayer.cels[1].position, Point(-1,-1))
  expect_eq(app.activeLayer.cels[1].image.width, 2) -- width in tilemap terms
  expect_eq(app.activeLayer.cels[1].image.height, 2) -- height in tilemap terms
  -- ======================================================================

  app.command.Undo()

  -- ======================================================================
  -- Cutting the canvas from the bottom
  --        ,--------- x = 2
  --       |
  --   ____v______
  --  |           |
  --  |    ∏∏XX   |<-- y = 1
  --  |    ∏∏XX   |
  app.command.CanvasSize{
    ui=false,
    bounds=Rectangle(0,0,8,3),
    trimOutside=true
  }

  expect_eq(app.activeLayer.cels[1].position, Point(2,1))
  expect_eq(app.activeLayer.cels[1].image.width, 2) -- width in tilemap terms
  expect_eq(app.activeLayer.cels[1].image.height, 1) -- height in tilemap terms
  -- ======================================================================
  -- Cutting the canvas from the right
  --        ,--------- x = 2
  --       |
  --   ____v_
  --  |      |
  --  |    ∏∏|<-- y = 1
  --  |    ∏∏|
  app.command.CanvasSize{
    ui=false,
    bounds=Rectangle(0,0,4,3),
    trimOutside=true
  }

  expect_eq(app.activeLayer.cels[1].position, Point(2,1))
  expect_eq(app.activeLayer.cels[1].image.width, 1) -- width in tilemap terms
  expect_eq(app.activeLayer.cels[1].image.height, 1) -- height in tilemap terms
  -- ======================================================================

  app.command.Undo()
  app.command.Undo()

  -- ======================================================================
  -- Cutting the canvas from the left, partial tile:
  --
  --       ,--------- x = -1
  --      |
  --      v ______
  --  |    |      |
  --  |   ∏|∏XX   |<-- y = 1
  --  |   ∏|∏XX   |
  --  |   O|O∏∏   |
  --  |   O|O∏∏   |
  --  |    |      |
  --  |    |      |
  --  |    |______|
  app.command.CanvasSize{
    ui=false,
    bounds=Rectangle(3,0,5,8),
    trimOutside=true
  }

  expect_eq(app.activeLayer.cels[1].position, Point(-1, 1))
  expect_eq(app.activeLayer.cels[1].image.width, 2) -- width in tilemap terms
  expect_eq(app.activeLayer.cels[1].image.height, 2) -- height in tilemap terms
  -- ======================================================================

  app.command.Undo()

  -- ======================================================================
  -- Cutting the canvas from the top, partial tile:
  --
  --        ,--------- x = 2
  --       |
  --   ____|______
  --       v
  --   ____∏∏XX___ <-- y = -1
  --  |    ∏∏XX   |
  --  |    OO∏∏   |
  --  |    OO∏∏   |
  --  |           |
  --  |           |
  --  |___________|
  app.command.CanvasSize{
    ui=false,
    bounds=Rectangle(0,2,8,6),
    trimOutside=true
  }

  expect_eq(app.activeLayer.cels[1].position, Point(2, -1))
  expect_eq(app.activeLayer.cels[1].image.width, 2) -- width in tilemap terms
  expect_eq(app.activeLayer.cels[1].image.height, 2) -- height in tilemap terms

  -- ======================================================================

  app.command.Undo()

  -- ======================================================================
  -- Cutting the canvas from the left, cutting first tile column:
  --
  --         ,--------- x = 0
  --         |
  --         v____
  --  |     |     |
  --  |     |XX   |<-- y = 1
  --  |     |XX   |
  --  |     |∏∏   |
  --  |     |∏∏   |
  --  |     |     |
  --  |     |     |
  --  |     |_____|
  app.command.CanvasSize{
    ui=false,
    bounds=Rectangle(4,0,4,8),
    trimOutside=true
  }

  expect_eq(app.activeLayer.cels[1].position, Point(0, 1))
  expect_eq(app.activeLayer.cels[1].image.width, 1) -- width in tilemap terms
  expect_eq(app.activeLayer.cels[1].image.height, 2) -- height in tilemap terms
  -- ======================================================================

  app.command.Undo()

  -- ======================================================================
  -- Cutting the canvas from the top, cutting the top tile row:
  --
  --        ,--------- x = 2
  --       |
  --   ____|______
  --       |
  --       |
  --   ____v______
  --  |    OO∏∏   | <-- y = 0
  --  |    OO∏∏   |
  --  |           |
  --  |           |
  --  |___________|
  app.command.CanvasSize{
    ui=false,
    bounds=Rectangle(0,3,8,5),
    trimOutside=true
  }

  expect_eq(app.activeLayer.cels[1].position, Point(2, 0))
  expect_eq(app.activeLayer.cels[1].image.width, 2) -- width in tilemap terms
  expect_eq(app.activeLayer.cels[1].image.height, 1) -- height in tilemap terms
end

----------------------------------------------------------------------
-- Tests canvas resizing in the tilemap with tiles 2x2 with shrink action
----------------------------------------------------------------------

do
  local sprite3 = Sprite(8, 8)
  -- Create a tilemap layer which grid size is 2x2
  sprite3.gridBounds = Rectangle(0, 0, 2, 2)
  app.command.NewLayer{ tilemap=true }
  -- Create a tilemap of 4x4 tiles:
  --
  --    ,--------- x = 0
  --   |
  --   v__________
  --  |           |
  --  |∏∏         |<-- y = 1
  --  |∏∏         |
  --  |OO       XX|
  --  |OO       XX|
  --  |         ∏∏|
  --  |         ∏∏|
  --  |___________|

  -- Making a pixel to make a Cel
  app.useTool{
    tool='pencil',
    color=Color{ r=255, g=0, b=0 },
    tilesetMode=TilesetMode.AUTO,
    points={ Point(0, 0) }
  }

  -- Moving the Cel to de desired position
  app.activeLayer.cels[1].position = Point(0, 1)

  -- Filling with squares of 2x2
  app.useTool{
    tool='pencil',
    color=Color{ r=255, g=0, b=0 },
    tilesetMode=TilesetMode.AUTO,
    points={ Point(1, 1), Point(1, 2), Point(0, 2) }
  }

  app.useTool{
    tool='pencil',
    color=Color{ r=0, g=255, b=0 },
    tilesetMode=TilesetMode.AUTO,
    points={ Point(0, 3), Point(1, 3), Point(1, 4), Point(0, 4) }
  }

  app.useTool{
    tool='pencil',
    color=Color{ r=0, g=0, b=255 },
    tilesetMode=TilesetMode.AUTO,
    points={ Point(6, 3), Point(7, 3), Point(6, 4), Point(7, 4) }
  }

  app.useTool{
    tool='pencil',
    color=Color{ r=255, g=255, b=255 },
    tilesetMode=TilesetMode.AUTO,
    points={ Point(6, 5), Point(7, 5), Point(6, 6), Point(7, 6) }
  }

  -- ======================================================================
  -- Cutting the canvas from the left, cutting the most left tile column:
  --
  --             ,--------- x = 4
  --            |
  --      ______v_
  --  |  |        |
  --  |  |        |
  --  |  |        |
  --  |  |      XX| <-- y = 3
  --  |  |      XX|
  --  |  |      ∏∏|
  --  |  |      ∏∏|
  --  |  |________|
  app.command.CanvasSize{
    ui=false,
    bounds=Rectangle(2,0,6,8),
    trimOutside=true
  }

  expect_eq(app.activeLayer.cels[1].position, Point(4, 3))
  expect_eq(app.activeLayer.cels[1].image.width, 1) -- width in tilemap terms
  expect_eq(app.activeLayer.cels[1].image.height, 2) -- height in tilemap terms
end

----------------------------------------------------------------------
-- Tests removal of unused tilesets when deleting tilemaps
----------------------------------------------------------------------

do
  local spr = Sprite(32, 32, ColorMode.INDEXED)
  assert(spr.layers[1].isImage)
  assert(not spr.layers[1].isTilemap)

  -- Create some tilemaps
  app.command.NewLayer{ tilemap=true }
  assert(#spr.layers == 2)
  local tilemapLay1 = spr.layers[2]
  assert(tilemapLay1.isImage)
  assert(tilemapLay1.isTilemap)
  assert(#spr.tilesets == 1)
  assert(spr.tilesets[1] == tilemapLay1.tileset)

  app.command.NewLayer{ tilemap=true }
  assert(#spr.layers == 3)
  local tilemapLay2 = spr.layers[3]
  assert(tilemapLay2.isImage)
  assert(tilemapLay2.isTilemap)
  assert(#spr.tilesets == 2)
  assert(spr.tilesets[2] == tilemapLay2.tileset)

  app.command.NewLayer{ tilemap=true }
  assert(#spr.layers == 4)
  local tilemapLay3 = spr.layers[4]
  assert(tilemapLay3.isImage)
  assert(tilemapLay3.isTilemap)
  assert(#spr.tilesets == 3)
  assert(spr.tilesets[3] == tilemapLay3.tileset)

  -- Remove tilemap 2 and check that a tilemap was removed and
  -- tilesets of remaining tilemaps are correct.
  app.range.layers = { tilemapLay2 }
  app.command.RemoveLayer()
  assert(#spr.layers == 3)
  assert(#spr.tilesets == 2)
  assert(spr.tilesets[1] == tilemapLay1.tileset)
  assert(spr.tilesets[2] == tilemapLay3.tileset)

  -- Undo tilemap removal and check that it goes back to
  -- previous state.
  app.undo()

  assert(#spr.layers == 4)
  assert(#spr.tilesets == 3)
  assert(spr.tilesets[1] == tilemapLay1.tileset)
  assert(spr.tilesets[2] == tilemapLay2.tileset)
  assert(spr.tilesets[3] == tilemapLay3.tileset)

  -- Try removing 2 tilemaps now
  app.range.layers = { tilemapLay1, tilemapLay2 }
  app.command.RemoveLayer()
  assert(#spr.layers == 2)
  assert(#spr.tilesets == 1)
  assert(spr.tilesets[1] == tilemapLay3.tileset)

  app.undo()

  assert(#spr.layers == 4)
  assert(#spr.tilesets == 3)
  assert(spr.tilesets[1] == tilemapLay1.tileset)
  assert(spr.tilesets[2] == tilemapLay2.tileset)
  assert(spr.tilesets[3] == tilemapLay3.tileset)

  -- Assign same tileset to tilemap 1 and tilemap 3.
  local oldTilemapLay3Tileset = tilemapLay3.tileset
  tilemapLay3.tileset = tilemapLay1.tileset
  -- We have to manually delete tilemap 3 tileset because
  -- assigning a different tileset doesn't check for/remove
  -- unused tilesets (TODO: should we add this?)
  spr:deleteTileset(oldTilemapLay3Tileset)

  assert(#spr.tilesets == 2)
  assert(spr.tilesets[1] == tilemapLay1.tileset)
  assert(spr.tilesets[2] == tilemapLay2.tileset)
  assert(spr.tilesets[1] == tilemapLay3.tileset)

  -- Remove tilemap 1 and check that no tileset was removed.
  app.range.layers = { tilemapLay1 }
  app.command.RemoveLayer()
  assert(#spr.layers == 3)
  assert(#spr.tilesets == 2)
  assert(spr.tilesets[2] == tilemapLay2.tileset)
  assert(spr.tilesets[1] == tilemapLay3.tileset)

  -- Remove tilemap 3 and check that the tileset was removed now.
  app.range.layers = { tilemapLay3 }
  app.command.RemoveLayer()
  assert(#spr.layers == 2)
  assert(#spr.tilesets == 1)
  assert(spr.tilesets[1] == tilemapLay2.tileset)

  -- Undo all
  app.undo()
  app.undo()
  app.undo()
  -- Manually re-assign its tileset to tilemap 3.
  tilemapLay3.tileset = spr.tilesets[3]

  assert(#spr.layers == 4)
  assert(#spr.tilesets == 3)
  assert(spr.tilesets[1] == tilemapLay1.tileset)
  assert(spr.tilesets[2] == tilemapLay2.tileset)
  assert(spr.tilesets[3] == tilemapLay3.tileset)
end

-----------------------------------------------------------------------
-- Test tilemaps with flags (flips)
-----------------------------------------------------------------------

do
  local spr = Sprite(8, 8, ColorMode.INDEXED)

  -- Create a new tilemap (delete the default regular layer)
  app.command.NewLayer{ tilemap=true, gridBounds=Rectangle{ 0, 0, 2, 2 } }
  local mapLay = app.layer
  spr:deleteLayer(spr.layers[1])
  assert(#mapLay.cels == 0)

  -- Set a palette just for testing/preview the result
  spr:setPalette(Palette{ fromResource="Teletext" })

  -- Create one tile with these pixels (indexes):
  -- 12
  -- 34
  local tile = spr:newTile(mapLay.tileset, 1)
  array_to_pixels({ 1, 2,
                    3, 4 }, tile.image)

  -- Fill the tilemap with this only tile
  app.useTool{ tool='paint_bucket', color=1, layer=mapLay,
               tilemapMode=TilemapMode.TILES,
               points={ Point(0, 0) }}

  assert(#mapLay.cels == 1)
  local map = mapLay.cels[1].image
  assert(map.width == 4)
  assert(map.height == 4)

  -- Constants
  local x = app.pixelColor.TILE_XFLIP
  local y = app.pixelColor.TILE_YFLIP
  local d = app.pixelColor.TILE_DFLIP

  -- Normal (tile without flags)
  -- 12
  -- 34
  map:drawPixel(0, 0, 1)

  -- Flip X
  -- 21
  -- 43
  map:drawPixel(1, 0, 1|x)

  -- Flip Y
  -- 34
  -- 12
  map:drawPixel(0, 1, 1|y)

  -- Flip X+Y
  -- 43
  -- 21
  map:drawPixel(1, 1, 1|x|y)

  -- Flip D
  -- 31
  -- 42
  map:drawPixel(2, 2, 1|d)

  -- Flip X+D
  -- 42
  -- 31
  map:drawPixel(3, 2, 1|x|d)

  -- Flip Y+D
  -- 13
  -- 24
  map:drawPixel(2, 3, 1|y|d)

  -- Flip X+Y+D
  -- 24
  -- 13
  map:drawPixel(3, 3, 1|x|y|d)

  -- Clear areas
  app.useTool{ tool='filled_rectangle', color=0, layer=mapLay,
               tilemapMode=TilemapMode.TILES,
               points={ Point(0, 4), Point(3, 7) }}
  app.useTool{ tool='filled_rectangle', color=0, layer=mapLay,
               tilemapMode=TilemapMode.TILES,
               points={ Point(4, 0), Point(7, 3) }}

  -- Save, load, and test if the tilemap were saved correctly with the
  -- given flags
  spr:saveAs('_test_tile_flags.aseprite')
  spr:close()

  local spr2 = app.open('_test_tile_flags.aseprite')
  local map2 = spr2.layers[1].cels[1].image
  expect_img(map2, { 1,   (1|x),   0,     0,
                     1|y, (1|x|y), 0,     0,
                     0,   0,     1|d,   (1|x|d),
                     0,   0,     1|y|d, (1|x|y|d) })
end
