local Map = {
	_LICENSE = "STI is distributed under the terms of the MIT license. See LICENSE.md.",
	_URL = "https://github.com/karai17/Simple-Tiled-Implementation",
	_VERSION = "0.9.7",
	_DESCRIPTION = "Simple Tiled Implementation is a Tiled Map Editor library designed for the *awesome* LÖVE framework."
}

-- modified here
local function getFramework()
	local lf = love.filesystem
	local lg = love.graphics
	local lm = love.math
	local lp = love.physics
	local framework = {}

	framework.version = "LOVE"

	assert(lf, "The love.filesystem module is required for this library.")
	assert(lg, "The love.graphics module is required for this library.")
	assert(lm, "The love.math module is required for this library.")

	function framework.load(file)
		return assert(lf.load(file), "File not found: " .. file)
	end

	function framework.newImage(path)
		local image = lg.newImage(path)
		image:setFilter("nearest", "nearest")
		return image
	end

	function framework:newCanvas(w, h)
		w = w or self.getWidth()
		h = h or self.getHeight()
		local canvas = lg.newCanvas(w, h)
		canvas:setFilter("nearest", "nearest")
		return canvas
	end

	-- Graphics Calls
	framework.draw				= lg.draw
	framework.getCanvas			= lg.getCanvas
	framework.getHeight			= lg.getHeight
	framework.getWidth			= lg.getWidth
	framework.line				= lg.line
	framework.newSpriteBatch	= lg.newSpriteBatch
	framework.newQuad			= lg.newQuad
	framework.polygon			= lg.polygon
	framework.rectangle			= lg.rectangle
	framework.setColor			= lg.setColor
	framework.setCanvas			= lg.setCanvas
	framework.origin			= lg.origin
	framework.pop				= lg.pop
	framework.push				= lg.push

	-- Math Calls
	framework.isConvex			= lm.isConvex
	framework.triangulate		= lm.triangulate

	-- Physics Calls
	if lp then
		framework.getMeter			= lp.getMeter
		framework.newBody			= lp.newBody
		framework.newChainShape		= lp.newChainShape
		framework.newFixture		= lp.newFixture
		framework.newPolygonShape	= lp.newPolygonShape
	end
	return framework
end

local framework = getFramework()
-- to here

function string.split(s, d)
	local magic = { "(", ")", ".", "%", "+", "-", "*", "?", "[", "^", "$" }

	for _, v in ipairs(magic) do
		if d == v then
			d = "%"..d
			break
		end
	end

	local t = {}
	local i = 0
	local f
	local match = '(.-)' .. d .. '()'

	if string.find(s, d) == nil then
		return {s}
	end

	for sub, j in string.gmatch(s, match) do
		i = i + 1
		t[i] = sub
		f = j
	end

	if i ~= 0 then
		t[i+1] = string.sub(s, f)
	end

	return t
end

-- https://github.com/stevedonovan/Penlight/blob/master/lua/pl/path.lua#L286
local function formatPath(path)
	local np_gen1,np_gen2	= '[^SEP]+SEP%.%.SEP?','SEP+%.?SEP'
	local np_pat1, np_pat2	= np_gen1:gsub('SEP','/'), np_gen2:gsub('SEP','/')
	local k

	repeat -- /./ -> /
		path,k = path:gsub(np_pat2,'/')
	until k == 0

	repeat -- A/../ -> (empty)
		path,k = path:gsub(np_pat1,'')
	until k == 0

	if path == '' then path = '.' end

	return path
end

local function rotateVertex(v, x, y, cos, sin)
	local vertex = {
		x = v.x,
		y = v.y,
	}

	vertex.x = vertex.x - x
	vertex.y = vertex.y - y

	local vx = cos * vertex.x - sin * vertex.y
	local vy = sin * vertex.x + cos * vertex.y

	return vx + x, vy + y
end

local function convertEllipseToPolygon(x, y, w, h, max_segments)
	local function calc_segments(segments)
		local function vdist(a, b)
			local c = {
				x = a.x - b.x,
				y = a.y - b.y,
			}

			return c.x * c.x + c.y * c.y
		end

		segments = segments or 64
		local vertices = {}

		local v = { 1, 2, math.ceil(segments/4-1), math.ceil(segments/4) }

		local m
		if framework.getMeter then
			m = framework.getMeter()
		else
			m = 32
		end

		for _, i in ipairs(v) do
			local angle = (i / segments) * math.pi * 2
			local px = x + w / 2 + math.cos(angle) * w / 2
			local py = y + h / 2 + math.sin(angle) * h / 2

			table.insert(vertices, { x = px / m, y = py / m })
		end

		local dist1 = vdist(vertices[1], vertices[2])
		local dist2 = vdist(vertices[3], vertices[4])

		-- Box2D hard-coded threshold
		if dist1 < 0.0025 or dist2 < 0.0025 then
			return calc_segments(segments-2)
		end

		return segments
	end

	local segments = calc_segments(max_segments)
	local vertices = {}

	table.insert(vertices, { x = x + w / 2, y = y + h / 2 })

	for i=0, segments do
		local angle = (i / segments) * math.pi * 2
		local px = x + w / 2 + math.cos(angle) * w / 2
		local py = y + h / 2 + math.sin(angle) * h / 2

		table.insert(vertices, { x = px, y = py })
	end

	return vertices
end

function Map:init(path, fw)
	framework = fw

	self.tiles			= {}
	self.tileInstances	= {}
	self.drawRange		= {
		sx = 1,
		sy = 1,
		ex = self.width,
		ey = self.height,
	}

	-- Set tiles, images
	local gid = 1
	for i, tileset in ipairs(self.tilesets) do
		local image = formatPath(path .. tileset.image)
		tileset.image = framework.newImage(image)
		gid = self:setTiles(i, tileset, gid)
	end

	-- Set layers
	for i, layer in ipairs(self.layers) do
		self:setLayer(layer, path)
	end
end

function Map:initWorldCollision(world)
	assert(framework.newBody, "To use the built-in collision system, please enable the physics module.")

	local body = framework.newBody(world)
	local collision = {
		body = body,
	}

	local function addObjectToWorld(objshape, vertices, userdata)
		local shape

		if objshape == "polyline" then
			shape = framework.newChainShape(false, unpack(vertices))
		else
			shape = framework.newPolygonShape(unpack(vertices))
		end

		local fixture = framework.newFixture(body, shape)

		fixture:setUserData(userdata)

		local obj = {
			shape = shape,
			fixture = fixture,
		}

		table.insert(collision, obj)
	end

	local function getPolygonVertices(object, tile, precalc)
		local ox, oy = 0, 0

		if not precalc then
			ox = object.x
			oy = object.y
		end

		local vertices = {}
		for _, vertex in ipairs(object.polygon) do
			table.insert(vertices, tile.x + ox + vertex.x)
			table.insert(vertices, tile.y + oy + vertex.y)
		end

		return vertices
	end

	local function calculateObjectPosition(object, tile)
		local o = {
			shape	= object.shape,
			x		= object.x,
			y		= object.y,
			w		= object.width,
			h		= object.height,
			polygon	= object.polygon or object.polyline or object.ellipse or object.rectangle
		}
		local t		= tile or { x=0, y=0 }

		local userdata = {
			object = object,
			instance = t,
			tile = t.gid and self.tiles[t.gid]
		}

		if o.shape == "rectangle" then
			o.r = object.rotation or 0
			local cos = math.cos(math.rad(o.r))
			local sin = math.sin(math.rad(o.r))

			if object.gid then
				local tileset = self.tiles[object.gid].tileset
				local lid = object.gid - self.tilesets[tileset].firstgid
				local tile = {}

				-- This fixes a height issue
				 o.y = o.y + self.tiles[object.gid].offset.y

				for _, t in ipairs(self.tilesets[tileset].tiles) do
					if t.id == lid then
						tile = t
						break
					end
				end

				if tile.objectGroup then
					for _, obj in ipairs(tile.objectGroup.objects) do
						-- Every object in the tile
						calculateObjectPosition(obj, object)
					end

					return
				else
					o.w = self.tiles[object.gid].width
					o.h = self.tiles[object.gid].height
				end
			end

			o.polygon = {
				{ x=o.x,		y=o.y },
				{ x=o.x + o.w,	y=o.y },
				{ x=o.x + o.w,	y=o.y + o.h },
				{ x=o.x,		y=o.y + o.h },
			}

			for _, vertex in ipairs(o.polygon) do
				if self.orientation == "isometric" then
					vertex.x, vertex.y = self:convertIsometricToScreen(vertex.x, vertex.y)
				end

				vertex.x, vertex.y = rotateVertex(vertex, o.x, o.y, cos, sin)
			end

			local vertices = getPolygonVertices(o, t, true)
			addObjectToWorld(o.shape, vertices, userdata)
		elseif o.shape == "ellipse" then
			if not o.polygon then
				o.polygon = convertEllipseToPolygon(o.x, o.y, o.w, o.h)
			end
			local vertices	= getPolygonVertices(o, t, true)
			local triangles	= framework.triangulate(vertices)

			for _, triangle in ipairs(triangles) do
				addObjectToWorld(o.shape, triangle, userdata)
			end
		elseif o.shape == "polygon" then
			local precalc = false
			if not t.gid then precalc = true end

			local vertices	= getPolygonVertices(o, t, precalc)
			local triangles	= framework.triangulate(vertices)

			for _, triangle in ipairs(triangles) do
				addObjectToWorld(o.shape, triangle, userdata)
			end
		elseif o.shape == "polyline" then
			local precalc = false
			if not t.gid then precalc = true end

			local vertices	= getPolygonVertices(o, t, precalc)
			addObjectToWorld(o.shape, vertices, userdata)
		end
	end

	for _, tileset in ipairs(self.tilesets) do
		for _, tile in ipairs(tileset.tiles) do
			local gid = tileset.firstgid + tile.id

			if tile.objectGroup then
				if self.tileInstances[gid] then
					for _, instance in ipairs(self.tileInstances[gid]) do
						for _, object in ipairs(tile.objectGroup.objects) do
							-- Every object in every instance of a tile
							calculateObjectPosition(object, instance)
						end
					end
				end
			elseif tile.properties.collidable == "true" and self.tileInstances[gid] then
				for _, instance in ipairs(self.tileInstances[gid]) do
					-- Every instance of a tile
					local object = {
						shape	= "rectangle",
						x		= 0,
						y		= 0,
						width	= tileset.tilewidth,
						height	= tileset.tileheight,
					}

					calculateObjectPosition(object, instance)
				end
			end
		end
	end

	for _, layer in ipairs(self.layers) do
		if layer.properties.collidable == "true" then
			-- Entire layer
			if layer.type == "tilelayer" then
				for y, tiles in ipairs(layer.data) do
					for x, tile in pairs(tiles) do
						local object = {
							shape	= "rectangle",
							x		= x * self.tilewidth  + tile.offset.x,
							y		= y * self.tileheight + tile.offset.y,
							width	= tile.width,
							height	= tile.height,
						}
						calculateObjectPosition(object)
					end
				end
			elseif layer.type == "objectgroup" then
				for _, object in ipairs(layer.objects) do
					calculateObjectPosition(object)
				end
			elseif layer.type == "imagelayer" then
				local object = {
					shape	= "rectangle",
					x		= layer.x or 0,
					y		= layer.y or 0,
					width	= layer.width,
					height	= layer.height,
				}
				calculateObjectPosition(object)
			end
		end

		if layer.type == "objectgroup" then
			for _, object in ipairs(layer.objects) do
				if object.properties.collidable == "true" then
					-- Individual objects
					calculateObjectPosition(object)
				end
			end
		end
	end

	return collision
end

function Map:setTiles(index, tileset, gid)
	local function getTiles(i, t, m, s)
		i = i - m
		local n = 0

		while i >= t do
			i = i - t
			if n ~= 0 then i = i - s end
			if i >= 0 then n = n + 1 end
		end

		return n
	end

	local quad	= framework.newQuad
	local mw	= self.tilewidth
	local iw	= tileset.imagewidth
	local ih	= tileset.imageheight
	local tw	= tileset.tilewidth
	local th	= tileset.tileheight
	local s		= tileset.spacing
	local m		= tileset.margin
	local w		= getTiles(iw, tw, m, s)
	local h		= getTiles(ih, th, m, s)

	for y = 1, h do
		for x = 1, w do
			local id = gid - tileset.firstgid
			local qx = (x - 1) * tw + m + (x - 1) * s
			local qy = (y - 1) * th + m + (y - 1) * s
			local properties
			local terrain
			local animation

			for _, tile in pairs(tileset.tiles) do
				if tile.id == id then
					properties = tile.properties
					animation = tile.animation
					if tile.terrain then
						terrain = {}
						for i=1,#tile.terrain do
							terrain[i] = tileset.terrains[tile.terrain[i] + 1]
						end
					end
				end
			end

			local tile = {
				id 			= id,
				gid			= gid,
				tileset		= index,
				quad		= quad(qx, qy, tw, th, iw, ih),
				properties	= properties,
				terrain     = terrain,
				animation   = animation,
				frame       = 1,
				time        = 0,
				width		= tw,
				height		= th,
				sx			= 1,
				sy			= 1,
				r			= 0,
				offset		= {
					x = -mw + tileset.tileoffset.x,
					y = -th + tileset.tileoffset.y,
				},
			}

			if self.orientation == "isometric" then
				tile.offset.x = -mw / 2
			end

			self.tiles[gid] = tile
			gid = gid + 1
		end
	end

	return gid
end

function Map:setLayer(layer, path)
	layer.x = layer.x or 0
	layer.y = layer.y or 0
	layer.update = function(dt) return end

	if layer.type == "tilelayer" then
		self:setTileData(layer)
		self:setSpriteBatches(layer)
		layer.draw = function() self:drawTileLayer(layer) end
	elseif layer.type == "objectgroup" then
		self:setObjectCoordinates(layer)
		self:setObjectSpriteBatches(layer)
		layer.draw = function() self:drawObjectLayer(layer) end
	elseif layer.type == "imagelayer" then
		layer.draw = function() self:drawImageLayer(layer) end

		if layer.image ~= "" then
			local image = formatPath(path..layer.image)
			layer.image = framework.newImage(image)
			layer.width = layer.image:getWidth()
			layer.height = layer.image:getHeight()
		end
	end

	self.layers[layer.name] = layer
end

function Map:setTileData(layer)
	local i = 1
	local map = {}

	for y = 1, layer.height do
		map[y] = {}
		for x = 1, layer.width do
			local gid = layer.data[i]

			if gid > 0 then
				map[y][x] = self.tiles[gid] or self:setFlippedGID(gid)
			end

			i = i + 1
		end
	end

	layer.data = map
end

function Map:setObjectCoordinates(layer)
	local function updateVertex(vertex, x, y, cos, sin)
		if self.orientation == "isometric" then
			x, y = self:convertIsometricToScreen(x, y)
			vertex.x, vertex.y = self:convertIsometricToScreen(vertex.x, vertex.y)
		end

		return rotateVertex(vertex, x, y, cos, sin)
	end

	for _, object in ipairs(layer.objects) do
		local x = layer.x + object.x
		local y = layer.y + object.y
		local w = object.width
		local h = object.height
		local r = object.rotation
		local cos = math.cos(math.rad(r))
		local sin = math.sin(math.rad(r))

		if object.shape == "rectangle" then
			object.rectangle = {}

			local vertices

			if object.gid then
				vertices = {
					{ x=x,		y=y - h },
					{ x=x + w,	y=y - h },
					{ x=x + w,	y=y },
					{ x=x,		y=y },
				}
			else
				vertices = {
					{ x=x,		y=y },
					{ x=x + w,	y=y },
					{ x=x + w,	y=y + h },
					{ x=x,		y=y + h },
				}
			end

			for _, vertex in ipairs(vertices) do
				vertex.x, vertex.y = updateVertex(vertex, x, y, cos, sin)
				table.insert(object.rectangle, { x = vertex.x, y = vertex.y })
			end
		elseif object.shape == "ellipse" then
			object.ellipse = {}

			local vertices = convertEllipseToPolygon(x, y, w, h)

			for _, vertex in ipairs(vertices) do
				vertex.x, vertex.y = updateVertex(vertex, x, y, cos, sin)
				table.insert(object.ellipse, { x = vertex.x, y = vertex.y })
			end
		elseif object.shape == "polygon" then
			for _, vertex in ipairs(object.polygon) do
				vertex.x = x + vertex.x
				vertex.y = y + vertex.y
				vertex.x, vertex.y = updateVertex(vertex, x, y, cos, sin)
			end
		elseif object.shape == "polyline" then
			for _, vertex in ipairs(object.polyline) do
				vertex.x = x + vertex.x
				vertex.y = y + vertex.y
				vertex.x, vertex.y = updateVertex(vertex, x, y, cos, sin)
			end
		end
	end
end

function Map:setSpriteBatches(layer)
	local newBatch	= framework.newSpriteBatch
	local w			= framework.getWidth()
	local h			= framework.getHeight()
	local tw		= self.tilewidth
	local th		= self.tileheight
	local bw		= math.ceil(w / tw)
	local bh		= math.ceil(h / th)

	-- Minimum of 400 tiles per batch
	if bw < 20 then bw = 20 end
	if bh < 20 then bh = 20 end

	local size		= bw * bh
	local batches	= {
		width	= bw,
		height	= bh,
		data	= {},
	}

	for y = 1, layer.height do
		local by = math.ceil(y / bh)

		for x = 1, layer.width do
			local tile	= layer.data[y][x]
			local bx	= math.ceil(x / bw)
			local id

			if tile then
				local ts = tile.tileset
				local image = self.tilesets[tile.tileset].image

				batches.data[ts] = batches.data[ts] or {}
				batches.data[ts][by] = batches.data[ts][by] or {}
				batches.data[ts][by][bx] = batches.data[ts][by][bx] or newBatch(image, size)

				local batch = batches.data[ts][by][bx]
				local tx, ty, origx, origy

				if self.orientation == "orthogonal" then
					tx = x * tw + tile.offset.x
					ty = y * th + tile.offset.y

					origx = tx
					origy = ty

					-- Compensation for scale/rotation shift
					local compx = 0
					local compy = 0
					if tile.sx < 0 then compx = tw end
					if tile.sy < 0 then compy = th end

					if tile.r > 0 then
						tx = tx + th - compy
						ty = ty + th - tw + compx
					elseif tile.r < 0 then
						tx = tx + compy
						ty = ty + th - compx
					else
						tx = tx + compx
						ty = ty + compy
					end
				elseif self.orientation == "isometric" then
					tx = (x - y) * (tw / 2) + tile.offset.x + layer.width * tw / 2
					ty = (x + y) * (th / 2) + tile.offset.y
				elseif self.orientation == "staggered" then
					if y % 2 == 0 then
						tx = x * tw + tw / 2 + tile.offset.x
					else
						tx = x * tw + tile.offset.x
					end

					ty = y * th / 2 + tile.offset.y + th / 2
				end

				id = batch:add(tile.quad, tx, ty, tile.r, tile.sx, tile.sy)
				self.tileInstances[tile.gid] = self.tileInstances[tile.gid] or {}
				table.insert(self.tileInstances[tile.gid], { batch=batch, id=id, gid=tile.gid, x=origx or tx, y=origy or ty })
			end
		end
	end

	layer.batches = batches
end

function Map:setObjectSpriteBatches(layer)
	local newBatch	= framework.newSpriteBatch
	local tw		= self.tilewidth
	local th		= self.tileheight
	local batches	= {}

	for _, object in ipairs(layer.objects) do
		if object.gid then
			local tile = self.tiles[object.gid] or self:setFlippedGID(object.gid)
			local ts = tile.tileset
			local image = self.tilesets[tile.tileset].image

			batches[ts] = batches[ts] or newBatch(image, 100)

			local batch = batches[ts]
			local tx = object.x + tw + tile.offset.x
			local ty = object.y + tile.offset.y

			-- Compensation for scale/rotation shift
			if tile.sx	< 0 then tx = tx + tw end
			if tile.sy	< 0 then ty = ty + th end
			if tile.r	> 0 then tx = tx + tw end
			if tile.r	< 0 then ty = ty + th end

			id = batch:add(tile.quad, tx, ty, tile.r, tile.sx, tile.sy)
			self.tileInstances[tile.gid] = self.tileInstances[tile.gid] or {}
			table.insert(self.tileInstances[tile.gid], { batch=batch, id=id, gid=tile.gid, x=tx, y=ty })
		end
	end

	layer.batches = batches
end

function Map:setDrawRange(tx, ty, w, h)
	tx = -tx
	ty = -ty
	local tw, th = self.tilewidth, self.tileheight
	local sx, sy, ex, ey

	if self.orientation == "orthogonal" then
		sx = math.ceil(tx / tw)
		sy = math.ceil(ty / th)
		ex = math.ceil(sx + w / tw)
		ey = math.ceil(sy + h / th)
	elseif self.orientation == "isometric" then
		sx = math.ceil(((ty / (th / 2)) + (tx / (tw / 2))) / 2)
		sy = math.ceil(((ty / (th / 2)) - (tx / (tw / 2))) / 2 - h / th)
		ex = math.ceil(sx + (h / th) + (w / tw))
		ey = math.ceil(sy + (h / th) * 2 + (w / tw))
	elseif self.orientation == "staggered" then
		sx = math.ceil(tx / tw - 1)
		sy = math.ceil(ty / th)
		ex = math.ceil(sx + w / tw + 1)
		ey = math.ceil(sy + h / th * 2)
	end

	self.drawRange = {
		sx = sx,
		sy = sy,
		ex = ex,
		ey = ey,
	}
end

function Map:addCustomLayer(name, index)
	local layer = {
      type = "customlayer",
      name = name,
      visible = true,
      opacity = 1,
      properties = {},
    }

	function layer:draw() return end
	function layer:update(dt) return end

	table.insert(self.layers, index, layer)
	self.layers[name] = self.layers[index]

	return layer
end

function Map:convertToCustomLayer(index)
	local layer = assert(self.layers[index], "Layer not found: " .. index)

	layer.type		= "customlayer"
	layer.x			= nil
	layer.y			= nil
	layer.width		= nil
	layer.height	= nil
	layer.encoding	= nil
	layer.data		= nil
	layer.objects	= nil
	layer.image		= nil

	function layer:draw() return end
	function layer:update(dt) return end
end

function Map:removeLayer(index)
	local layer = assert(self.layers[index], "Layer not found: " .. index)

	if type(index) == "string" then
		for i, layer in ipairs(self.layers) do
			if layer.name == index then
				table.remove(self.layers, i)
				self.layers[index] = nil
				break
			end
		end
	else
		local name = self.layers[index].name
		table.remove(self.layers, index)
		self.layers[name] = nil
	end
end

function Map:update(dt)
	for gid, tile in pairs( self.tiles ) do
		local update
		local t

		if tile.animation then
			update = false

			tile.time = tile.time + dt * 1000
			while tile.time > tonumber(tile.animation[tile.frame].duration) do
				tile.time = tile.time - tonumber(tile.animation[tile.frame].duration)
				tile.frame = tile.frame + 1
				if tile.frame > #tile.animation then tile.frame = 1 end
				update = true
			end
			if update == true and self.tileInstances[gid] ~= nil then
				for _, j in pairs(self.tileInstances[gid]) do
					t = self.tiles[tile.animation[tile.frame].tileid + self.tilesets[tile.tileset].firstgid]
					j.batch:set( j.id, t.quad, j.x, j.y, 0 )
				end
			end
		end
	end


	for _, layer in ipairs(self.layers) do
		layer:update(dt)
	end
end

-- modified
function Map:draw()
	for _, layer in ipairs(self.layers) do
		if layer.visible and layer.opacity > 0 then
			self:drawLayer(layer)
		end
	end
end

function Map:drawLayer(layer)
	framework.setColor(255, 255, 255, 255 * layer.opacity)
	layer:draw()
	framework.setColor(255, 255, 255, 255)
end

function Map:drawTileLayer(layer)
	if type(layer) == "string" or type(layer) == "number" then
		layer = self.layers[layer]
	end

	assert(layer.type == "tilelayer", "Invalid layer type: " .. layer.type .. ". Layer must be of type: tilelayer")

	local bw = layer.batches.width
	local bh = layer.batches.height
	local sx = math.ceil((self.drawRange.sx - layer.x / self.tilewidth	- 1) / bw)
	local sy = math.ceil((self.drawRange.sy - layer.y / self.tileheight	- 1) / bh)
	local ex = math.ceil((self.drawRange.ex - layer.x / self.tilewidth	+ 1) / bw)
	local ey = math.ceil((self.drawRange.ey - layer.y / self.tileheight	+ 1) / bh)
	local mx = math.ceil(self.width / bw)
	local my = math.ceil(self.height / bh)

	for by=sy, ey do
		for bx=sx, ex do
			if bx >= 1 and bx <= mx and by >= 1 and by <= my then
				for _, batches in pairs(layer.batches.data) do
					local batch = batches[by] and batches[by][bx]

					if batch then
						framework.draw(batch, math.floor(layer.x), math.floor(layer.y))
					end
				end
			end
		end
	end
end

function Map:drawObjectLayer(layer)
	if type(layer) == "string" or type(layer) == "number" then
		layer = self.layers[layer]
	end

	assert(layer.type == "objectgroup", "Invalid layer type: " .. layer.type .. ". Layer must be of type: objectgroup")

	local line		= { 160, 160, 160, 255 * layer.opacity }
	local fill		= { 160, 160, 160, 255 * layer.opacity * 0.2 }
	local shadow	= { 0, 0, 0, 255 * layer.opacity }
	local reset		= { 255, 255, 255, 255 * layer.opacity }

	local function sortVertices(obj)
		local vertices = {{},{}}

		for _, vertex in ipairs(obj) do
			table.insert(vertices[1], vertex.x)
			table.insert(vertices[1], vertex.y)
			table.insert(vertices[2], vertex.x+1)
			table.insert(vertices[2], vertex.y+1)
		end

		return vertices
	end

	local function drawShape(obj, shape)
		local vertices = sortVertices(obj)

		if shape == "polyline" then
			framework.setColor(shadow)
			framework.line(vertices[2])
			framework.setColor(line)
			framework.line(vertices[1])

			return
		elseif shape == "polygon" then
			framework.setColor(fill)
			if not framework.isConvex(vertices[1]) then
				local triangles = framework.triangulate(vertices[1])
				for _, triangle in ipairs(triangles) do
					framework.polygon("fill", triangle)
				end
			else
				framework.polygon("fill", vertices[1])
			end
		else
			framework.setColor(fill)
			framework.polygon("fill", vertices[1])
		end

		framework.setColor(shadow)
		framework.polygon("line", vertices[2])
		framework.setColor(line)
		framework.polygon("line", vertices[1])
	end

	for _, object in ipairs(layer.objects) do
		if object.shape == "rectangle" then
			drawShape(object.rectangle, "rectangle")
		elseif object.shape == "ellipse" then
			drawShape(object.ellipse, "ellipse")
		elseif object.shape == "polygon" then
			drawShape(object.polygon, "polygon")
		elseif object.shape == "polyline" then
			drawShape(object.polyline, "polyline")
		end
	end

	framework.setColor(reset)
	for _, batch in pairs(layer.batches) do
		framework.draw(batch, 0, 0)
	end
end

function Map:drawImageLayer(layer)
	if type(layer) == "string" or type(layer) == "number" then
		layer = self.layers[layer]
	end

	assert(layer.type == "imagelayer", "Invalid layer type: " .. layer.type .. ". Layer must be of type: imagelayer")

	if layer.image ~= "" then
		framework.draw(layer.image, layer.x, layer.y)
	end
end

function Map:drawWorldCollision(collision)
	for _, obj in ipairs(collision) do
		framework.polygon("line", collision.body:getWorldPoints(obj.shape:getPoints()))
	end
end

function Map:setFlippedGID(gid)
	local bit31		= 2147483648
	local bit30		= 1073741824
	local bit29		= 536870912
	local flipX		= false
	local flipY		= false
	local flipD		= false
	local realgid	= gid

	if realgid >= bit31 then
		realgid = realgid - bit31
		flipX = not flipX
	end

	if realgid >= bit30 then
		realgid = realgid - bit30
		flipY = not flipY
	end

	if realgid >= bit29 then
		realgid = realgid - bit29
		flipD = not flipD
	end

	local tile = self.tiles[realgid]
	local data = {
		id			= tile.id,
		gid			= tile.gid,
		tileset		= tile.tileset,
		frame       = tile.frame,
		time        = tile.time,
		width		= tile.width,
		height		= tile.height,
		offset		= tile.offset,
		quad		= tile.quad,
		properties	= tile.properties,
		terrain     = tile.terrain,
		animation   = tile.animation,
		sx			= tile.sx,
		sy			= tile.sy,
		r			= tile.r,
	}

	if flipX then
		if flipY and flipD then
			data.r = math.rad(-90)
			data.sy = -1
		elseif flipY then
			data.sx = -1
			data.sy = -1
		elseif flipD then
			data.r = math.rad(90)
		else
			data.sx = -1
		end
	elseif flipY then
		if flipD then
			data.r = math.rad(-90)
		else
			data.sy = -1
		end
	elseif flipD then
		data.r = math.rad(90)
		data.sy = -1
	end

	self.tiles[gid] = data

	return self.tiles[gid]
end

function Map:getLayerProperties(layer)
	local l = self.layers[layer]

	if not l then return {} end

	return l.properties
end

function Map:getTileProperties(layer, x, y)
	local tile = self.layers[layer].data[y][x]

	if not tile then return {} end

	return tile.properties
end

function Map:getObjectProperties(layer, object)
	local o = self.layers[layer].objects

	if type(object) == "number" then
		o = o[object]
	else
		for _, v in ipairs(o) do
			if v.name == object then
				o = v
				break
			end
		end
	end

	if not o then return {} end

	return o.properties
end

function Map:convertIsometricToScreen(x, y)
	local mw = self.width
	local tw, th = self.tilewidth, self.tileheight
	local ox = mw * tw / 2

	local sx = (x - y) + ox
	local sy = (x + y) / 2

	return sx, sy
end

function Map:convertScreenToIsometric(x, y)
	local mw, mh = self.width, self.height
	local tw, th = self.tilewidth, self.tileheight
	local ox = mw * tw / 2
	local oy = mh * th / 2

	local tx = (x / 2 + y) - ox / 2
	local ty = (-x / 2 + y) + oy

	return tx, ty
end

function Map:convertTileToScreen(x, y)
	local tw, th = self.tilewidth, self.tileheight

	local sx = x * tw
	local sy = y * th

	return sx, sy
end

function Map:convertScreenToTile(x, y)
	local tw, th = self.tilewidth, self.tileheight

	local tx = x / tw
	local ty = y / th

	return tx, ty
end

function Map:convertIsometricTileToScreen(x, y)
	local mw = self.width
	local tw, th = self.tilewidth, self.tileheight
	local ox = mw * tw / 2

	local sx = (x - y) * tw / 2 + ox
	local sy = (x + y) * th / 2

	return sx, sy
end

function Map:convertScreenToIsometricTile(x, y)
	local mw = self.width
	local tw, th = self.tilewidth, self.tileheight
	local ox = mw * tw / 2

	local tx = y / th + (x - ox) / tw
	local ty = y / th - (x - ox) / tw

	return tx, ty
end

function Map:convertStaggeredTileToScreen(x, y)
	local tw, th = self.tilewidth, self.tileheight

	local sx = x * tw + math.abs(math.ceil(y) % 2) * (tw / 2) - (math.ceil(y) % 2 * tw/2)
	local sy = y * (th / 2) + th/2

	return sx, sy
end

function Map:convertScreenToStaggeredTile(x, y)
	local function topLeft(x, y)
		if (math.ceil(y) % 2) then
			return x, y - 1
		else
			return x - 1, y - 1
		end
	end

	local function topRight(x, y)
		if (math.ceil(y) % 2) then
			return x + 1, y - 1
		else
			return x, y - 1
		end
	end

	local function bottomLeft(x, y)
		if (math.ceil(y) % 2) then
			return x, y + 1
		else
			return x - 1, y + 1
		end
	end

	local function bottomRight(x, y)
		if (math.ceil(y) % 2) then
			return x + 1, y + 1
		else
			return x, y + 1
		end
	end

	local tw, th = self.tilewidth, self.tileheight
	local hh = th / 2
	local ratio = th / tw

	local tx = x / tw
	local ty = y / th * 2

	local ctx = math.ceil(x / tw)
	local cty = math.ceil(y / th) * 2

	local rx = x - ctx * tw
	local ry = y - (cty / 2) * th

	if (hh - rx * ratio > ry) then
		return topLeft(tx, ty)
	elseif (-hh + rx * ratio > ry) then
		return topRight(tx, ty)
	elseif (hh + rx * ratio < ry) then
		return bottomLeft(tx, ty)
	elseif (hh * 3 - rx * ratio < ry) then
		return bottomRight(tx, ty)
	end

	return tx, ty
end

function Map.new(map)
	map = map .. ".lua"

	-- Get path to map
	local path = map:reverse():find("[/\\]") or ""
	if path ~= "" then
		path = map:sub(1, 1 + (#map - path))
	end

	-- Load map
	map = framework.load(map)
	setfenv(map, {})
	map = setmetatable(map(), {__index = Map})

	map:init(path, framework)

	return map
end

return Map
