class TileDrawingHelper
  attr_accessor :tileset
  attr_accessor :autotiles

  AUTOTILE_PATTERNS = [
    [[27, 28, 33, 34], [5, 28, 33, 34], [27,  6, 33, 34], [5,  6, 33, 34],
     [27, 28, 33, 12], [5, 28, 33, 12], [27,  6, 33, 12], [5,  6, 33, 12]],
    [[27, 28, 11, 34], [5, 28, 11, 34], [27,  6, 11, 34], [5,  6, 11, 34],
     [27, 28, 11, 12], [5, 28, 11, 12], [27,  6, 11, 12], [5,  6, 11, 12]],
    [[25, 26, 31, 32], [25,  6, 31, 32], [25, 26, 31, 12], [25,  6, 31, 12],
     [15, 16, 21, 22], [15, 16, 21, 12], [15, 16, 11, 22], [15, 16, 11, 12]],
    [[29, 30, 35, 36], [29, 30, 11, 36], [5, 30, 35, 36], [5, 30, 11, 36],
     [39, 40, 45, 46], [5, 40, 45, 46], [39,  6, 45, 46], [5,  6, 45, 46]],
    [[25, 30, 31, 36], [15, 16, 45, 46], [13, 14, 19, 20], [13, 14, 19, 12],
     [17, 18, 23, 24], [17, 18, 11, 24], [41, 42, 47, 48], [5, 42, 47, 48]],
    [[37, 38, 43, 44], [37,  6, 43, 44], [13, 18, 19, 24], [13, 14, 43, 44],
     [37, 42, 43, 48], [17, 18, 47, 48], [13, 18, 43, 48], [1,  2,  7,  8]]
  ]

  # converts neighbors returned from tableNeighbors to tile indexes
  NEIGHBORS_TO_AUTOTILE_INDEX = [
    46, 44, 46, 44, 43, 41, 43, 40, 46, 44, 46, 44, 43, 41, 43, 40,
    42, 32, 42, 32, 35, 19, 35, 18, 42, 32, 42, 32, 34, 17, 34, 16,
    46, 44, 46, 44, 43, 41, 43, 40, 46, 44, 46, 44, 43, 41, 43, 40,
    42, 32, 42, 32, 35, 19, 35, 18, 42, 32, 42, 32, 34, 17, 34, 16,
    45, 39, 45, 39, 33, 31, 33, 29, 45, 39, 45, 39, 33, 31, 33, 29,
    37, 27, 37, 27, 23, 15, 23, 13, 37, 27, 37, 27, 22, 11, 22,  9,
    45, 39, 45, 39, 33, 31, 33, 29, 45, 39, 45, 39, 33, 31, 33, 29,
    36, 26, 36, 26, 21,  7, 21,  5, 36, 26, 36, 26, 20,  3, 20,  1,
    46, 44, 46, 44, 43, 41, 43, 40, 46, 44, 46, 44, 43, 41, 43, 40,
    42, 32, 42, 32, 35, 19, 35, 18, 42, 32, 42, 32, 34, 17, 34, 16,
    46, 44, 46, 44, 43, 41, 43, 40, 46, 44, 46, 44, 43, 41, 43, 40,
    42, 32, 42, 32, 35, 19, 35, 18, 42, 32, 42, 32, 34, 17, 34, 16,
    45, 38, 45, 38, 33, 30, 33, 28, 45, 38, 45, 38, 33, 30, 33, 28,
    37, 25, 37, 25, 23, 14, 23, 12, 37, 25, 37, 25, 22, 10, 22,  8,
    45, 38, 45, 38, 33, 30, 33, 28, 45, 38, 45, 38, 33, 30, 33, 28,
    36, 24, 36, 24, 21,  6, 21,  4, 36, 24, 36, 24, 20,  2, 20,  0
  ]

  def self.tableNeighbors(data, x, y, layer = nil)
    return 0 if x < 0 || x >= data.xsize
    return 0 if y < 0 || y >= data.ysize
    if layer.nil?
      t = data[x, y]
    else
      t = data[x, y, layer]
    end
    xp1 = [x + 1, data.xsize - 1].min
    yp1 = [y + 1, data.ysize - 1].min
    xm1 = [x - 1, 0].max
    ym1 = [y - 1, 0].max
    i = 0
    if layer.nil?
      i |= 0x01 if data[  x, ym1] == t   # N
      i |= 0x02 if data[xp1, ym1] == t   # NE
      i |= 0x04 if data[xp1,   y] == t   # E
      i |= 0x08 if data[xp1, yp1] == t   # SE
      i |= 0x10 if data[  x, yp1] == t   # S
      i |= 0x20 if data[xm1, yp1] == t   # SW
      i |= 0x40 if data[xm1,   y] == t   # W
      i |= 0x80 if data[xm1, ym1] == t   # NW
    else
      i |= 0x01 if data[  x, ym1, layer] == t   # N
      i |= 0x02 if data[xp1, ym1, layer] == t   # NE
      i |= 0x04 if data[xp1,   y, layer] == t   # E
      i |= 0x08 if data[xp1, yp1, layer] == t   # SE
      i |= 0x10 if data[  x, yp1, layer] == t   # S
      i |= 0x20 if data[xm1, yp1, layer] == t   # SW
      i |= 0x40 if data[xm1,   y, layer] == t   # W
      i |= 0x80 if data[xm1, ym1, layer] == t   # NW
    end
    return i
  end

  def self.fromTileset(tileset)
    bmtileset = pbGetTileset(tileset.tileset_name)
    bmautotiles = []
    7.times do |i|
      bmautotiles.push(pbGetAutotile(tileset.autotile_names[i]))
    end
    return self.new(bmtileset, bmautotiles)
  end

  def initialize(tileset, autotiles)
    if tileset.mega?
      @tileset = TilemapRenderer::TilesetWrapper.wrapTileset(tileset)
      tileset.dispose
      @shouldWrap = true
    else
      @tileset = tileset
      @shouldWrap = false
    end
    @autotiles = autotiles
  end

  def dispose
    @tileset&.dispose
    @tileset = nil
    @autotiles.each_with_index do |autotile, i|
      autotile.dispose
      @autotiles[i] = nil
    end
  end

  def bltSmallAutotile(bitmap, x, y, cxTile, cyTile, id, frame)
    return if id >= 384 || frame < 0 || !@autotiles
    autotile = @autotiles[(id / 48) - 1]
    return if !autotile || autotile.disposed?
    cxTile = [cxTile / 2, 1].max
    cyTile = [cyTile / 2, 1].max
    if autotile.height == 32
      anim = frame * 32
      src_rect = Rect.new(anim, 0, 32, 32)
      bitmap.stretch_blt(Rect.new(x, y, cxTile * 2, cyTile * 2), autotile, src_rect)
    else
      anim = frame * 96
      id %= 48
      tiles = AUTOTILE_PATTERNS[id >> 3][id & 7]
      src = Rect.new(0, 0, 0, 0)
      4.times do |i|
        tile_position = tiles[i] - 1
        src.set(((tile_position % 6) * 16) + anim, (tile_position / 6) * 16, 16, 16)
        bitmap.stretch_blt(Rect.new((i % 2 * cxTile) + x, (i / 2 * cyTile) + y, cxTile, cyTile),
                           autotile, src)
      end
    end
  end

  def bltSmallRegularTile(bitmap, x, y, cxTile, cyTile, id)
    return if id < 384 || !@tileset || @tileset.disposed?
    rect = Rect.new(((id - 384) % 8) * 32, ((id - 384) / 8) * 32, 32, 32)
    rect = TilemapRenderer::TilesetWrapper.getWrappedRect(rect) if @shouldWrap
    bitmap.stretch_blt(Rect.new(x, y, cxTile, cyTile), @tileset, rect)
  end

  def bltSmallTile(bitmap, x, y, cxTile, cyTile, id, frame = 0)
    if id >= 384
      bltSmallRegularTile(bitmap, x, y, cxTile, cyTile, id)
    elsif id > 0
      bltSmallAutotile(bitmap, x, y, cxTile, cyTile, id, frame)
    end
  end

  def bltAutotile(bitmap, x, y, id, frame)
    bltSmallAutotile(bitmap, x, y, 32, 32, id, frame)
  end

  def bltRegularTile(bitmap, x, y, id)
    bltSmallRegularTile(bitmap, x, y, 32, 32, id)
  end

  def bltTile(bitmap, x, y, id, frame = 0)
    if id >= 384
      bltRegularTile(bitmap, x, y, id)
    elsif id > 0
      bltAutotile(bitmap, x, y, id, frame)
    end
  end
end

#===============================================================================
#
#===============================================================================
def createMinimap(mapid)
  map = load_data(sprintf("Data/Map%03d.rxdata", mapid)) rescue nil
  return Bitmap.new(32, 32) if !map
  bitmap = Bitmap.new(map.width * 4, map.height * 4)
  black = Color.black
  tilesets = $data_tilesets
  tileset = tilesets[map.tileset_id]
  return bitmap if !tileset
  helper = TileDrawingHelper.fromTileset(tileset)
  map.height.times do |y|
    map.width.times do |x|
      3.times do |z|
        id = map.data[x, y, z]
        id = 0 if !id
        helper.bltSmallTile(bitmap, x * 4, y * 4, 4, 4, id)
      end
    end
  end
  bitmap.fill_rect(0, 0, bitmap.width, 1, black)
  bitmap.fill_rect(0, bitmap.height - 1, bitmap.width, 1, black)
  bitmap.fill_rect(0, 0, 1, bitmap.height, black)
  bitmap.fill_rect(bitmap.width - 1, 0, 1, bitmap.height, black)
  return bitmap
end

def bltMinimapAutotile(dstBitmap, x, y, srcBitmap, id)
  return if id >= 48 || !srcBitmap || srcBitmap.disposed?
  anim = 0
  cxTile = 3
  cyTile = 3
  tiles = TileDrawingHelper::AUTOTILE_PATTERNS[id >> 3][id & 7]
  src = Rect.new(0, 0, 0, 0)
  4.times do |i|
    tile_position = tiles[i] - 1
    src.set((tile_position % 6 * cxTile) + anim,
            tile_position / 6 * cyTile, cxTile, cyTile)
    dstBitmap.blt((i % 2 * cxTile) + x, (i / 2 * cyTile) + y, srcBitmap, src)
  end
end

def passable?(passages, tile_id)
  return false if tile_id.nil?
  passage = passages[tile_id]
  return (passage && passage < 15)
end

# Unused
def getPassabilityMinimap(mapid)
  map = load_data(sprintf("Data/Map%03d.rxdata", mapid))
  tileset = $data_tilesets[map.tileset_id]
  minimap = AnimatedBitmap.new("Graphics/UI/minimap_tiles")
  ret = Bitmap.new(map.width * 6, map.height * 6)
  passtable = Table.new(map.width, map.height)
  passages = tileset.passages
  map.width.times do |i|
    map.height.times do |j|
      pass = true
      [2, 1, 0].each do |z|
        if !passable?(passages, map.data[i, j, z])
          pass = false
          break
        end
      end
      passtable[i, j] = pass ? 1 : 0
    end
  end
  neighbors = TileDrawingHelper::NEIGHBORS_TO_AUTOTILE_INDEX
  map.width.times do |i|
    map.height.times do |j|
      next if passtable[i, j] != 0
      nb = TileDrawingHelper.tableNeighbors(passtable, i, j)
      tile = neighbors[nb]
      bltMinimapAutotile(ret, i * 6, j * 6, minimap.bitmap, tile)
    end
  end
  minimap.dispose
  return ret
end
