dofile("../defs/tileset_defs.lua")
TileMap = class("TileMap")

local TILES = TILES

function TileMap:new(cols, rows, colWidth, rowHeight)
	local map = instance(self)	
	map.cols = cols
	map.rows = rows
	map.colWidth = colWidth or 32
	map.rowHeight = rowHeight or 32
	map:init()
	return map
end

function TileMap:init()
	self:init_light()
	self:init_variations()
	self:init_used()
	self:init_layouted()
	self:init_tiles()
	self:init_edges()
end

function TileMap:init_used()
	self.used = self.used or {}
	for row=1,self:getTileRows() do
		self.used[row] = {}
	end
end

function TileMap:init_layouted()
	self.layouted = self.layouted or {}
	for row=1,self:getTileRows() do
		self.layouted[row] = {}
	end
end


function TileMap:init_light()
	self.light = self.light or {}
	for row=1,self:getTileRows() do
		self.light[row] = {}
		for col=1,self:getTileCols() do
			self.light[row][col] = ClampedColor:new(0,100,130,240)
		end
	end
end


function TileMap:adjust_light(col, row, color, factor)
	local col = self.light[row][col]
	col:adjust_with_color(color, factor)
	return col
end


function TileMap:init_variations()
	self.variations = self.variations or {}
	for row=1,self:getTileRows() do
		self.variations[row] = {}
	end
end

function TileMap:init_tiles()
	self.tiles = self.tiles or {}
	for row=1,self:getTileRows() do
		self.tiles[row] = {}
	end
end

local offset ={
	l = {-1, 0},
	r = {1, 0},
	t = {0, -1},
	b = {0, 1}, 
	tl= {-1, -1}, 
	tr= {1, -1}, 
	bl= {-1,1}, 
	br= {1, 1}
}		

function TileMap:offsetDirection(col, row, dir)
	return col + offset[dir][1],  row + offset[dir][2]
end

function TileMap:offsetInvertedDirection(col, row, dir)
	return col + offset[dir][1],  row + offset[dir][2]
end


local math = math
local dirs = TILESET_EDGES
local invDirs = TILESET_INVERTED_EDGES

function TileMap:updateEdgesAround(col, row)
	--self:updateEdges(col, row)
	--for index, dir in pairs(dirs) do
	--	local ncol, nrow = self:offsetDirection(col, row, invDirs[index])
		--self:updateEdges(ncol, nrow)
	--end
end

function TileMap:updateEdges(col, row)
	local center = self:getTile(col, row)
	if center then
		local tdef = assert(TILES[center], center)
		local order = tdef.edge_prio
		local mask = 0
		for index, dir in pairs(dirs) do
			local ncol, nrow = self:offsetDirection(col, row, invDirs[index])
			if self:tileExists(ncol, nrow) then
				local side = self:getTile(ncol, nrow)
				local sdef = TILES[side]
				if order < sdef.edge_prio then
					mask = math.setBit(mask, math.bit(index))
				end
			end
		end
		if mask ~= 0 then
			self:setEdge(col, row, mask)
		else
			self:setEdge(col, row, nil)
		end
	end
end
function TileMap:setLayouted(col, row, val)
	assert(col>=1 and col <= self:getTileCols())
	assert(row>=1 and row <= self:getTileRows())
	self.layouted[row][col] = val
end

function TileMap:setEdge(col, row, val)
	self.edges[row][col] = val
end
function TileMap:getEdge(col,row)
	return self.edges[row][col]
end


function TileMap:init_edges()
	self.edges = self.edges or {}
	for row=1,self:getTileRows() do
		self.edges[row] = {}
	end
end

function TileMap:clear_with_tile(tile)
	for row=1,self:getTileRows() do
		for col=1,self:getTileCols(row) do
			self:setTileRaw(col, row, tile)
		end
	end
end

function TileMap:render(camera, x, y, scale, angle, a, r, g, b)
	local startCol, endCol = 1,self.cols--self:getTilesRenderRangeCols(camera, x)
	local startRow, endRow = 1,self.rows--self:getTilesRenderRangeRows(camera, y)
	local TILES = TILES		
	local tiles = self.tiles
	local variations = self.variations
	local light_cells = self.light
	--local layed_cells = self.layouted
	local perspective = camera.perspective
	for row = startRow, endRow do
		local tile_row = tiles[row]
		local variations_row = variations[row]
		local light_row = light_cells[row]
		--local layed_row = layed_cells[row]
		for col = startCol, endCol do
			local rx = x + self:colToPixels(col)*scale
			local ry = y + self:rowToPixels(row)*scale*perspective
			local v = tile_row[col]
			local light = light_row[col]
			--local layed = layed_row[col]
			local var = variations_row[col]
			local def = TILES[v]

			local sprite = def.sprites.c[var]
			
			video.renderSpriteState(sprite, rx, ry, scale, 0, 255,light.r,light.g,light.b)
			
			--video.renderTextSprites(math.round(col),rx-1,ry,2,"small", 255, 128, 255, 0, nil, 0.5)
			--video.renderTextSprites(math.round(row),rx+1,ry,0,"small", 255, 255, 128, 0, nil, 0.5)
		end
	end
	--[[
	for row = startRow, endRow do
		local tile_row = edges[row]
		for col = startCol, endCol do
			local rx = x + self:colToPixels(col)*scale
			local ry = y + self:rowToPixels(row)*scale*perspective
			local v = tile_row[col]
			local def = TILES[v]

			local sprite = def.sprites.c[1]				
			video.renderSpriteState(sprite, rx, ry, scale, 0, 255,r,g,b)
			--video.renderTextSprites(math.round(col),rx-1,ry,2,"small", 255, 128, 255, 0, nil, 0.5)
			--video.renderTextSprites(math.round(row),rx+1,ry,0,"small", 255, 255, 128, 0, nil, 0.5)
		end
	end]]	
end


function TileMap:getClass()
	return TileMap
end

function TileMap:getCols()
	return self.cols
end

function TileMap:getWallCols()
	return self.cols
end

function TileMap:getCornerCols()
	return self.cols
end

local math = math
function TileMap:tileExists(col, row)
	return self.tiles[row] and self.tiles[row][col] ~= nil 
end

local offset = {l = {-1, 0}, r = {1, 0}, t = {0, -1}, b = {0, 1}, tl= {-1, -1}, tr= {1, -1}, bl= {-1,1}, br= {1, 1}}

function TileMap:offsetDirection(col, row, dir)
	return col + offset[dir][1],  row + offset[dir][2]
end

function TileMap:offsetInvertedDirection(col, row, dir)
	return col + offset[dir][1],  row + offset[dir][2]
end

function TileMap:get_width()
	return self:getTileCols() * self.colWidth
end
function TileMap:get_height()
	return self:getTileRows() * self.rowHeight
end



function TileMap:getTileCols()
	return self.cols
end
function TileMap:getTileRows()
	return self.rows
end

function TileMap:setTile(col, row, val)
	self.tiles[row][col] = val
	self:updateEdgesAround(col, row)
	local tdef = TILES[val]
	self.variations[row][col] = math.random(1,tdef.variations)
	self.used[row][col] = true
end

function TileMap:setTileRaw(col, row, val)
	self.tiles[row][col] = val
	local tdef = TILES[val]
	self.variations[row][col] = math.random(1,tdef.variations)
end
function TileMap:getTile(col,row)
	return self.tiles[row][col]
end
function TileMap:getLight(col,row)
	return self.light[row][col]
end
function TileMap:getUsed(col,row)
	local used = self.used[row]
	return used and used[col]
end
function TileMap:getLayouted(col,row)
	local layouted = self.layouted[row]
	return layouted and layouted[col]
end

function TileMap:colToPixels(col)
	return (col-1) * self.colWidth
end
function TileMap:rowToPixels(row)
	return (row-1) * self.rowHeight
end
function TileMap:tileColToCamera(col, camera)
	return col * self.colWidth - camera.cx
end
function TileMap:tileRowToCamera(row, camera)
	return row * self.rowHeight - camera.cy
end
function TileMap:xToTileCol(x, camera)
	return math.round((x + camera.cx)/self.colWidth)
end
function TileMap:yToTileRow(y, camera)
	return math.round((y + camera.cy)/self.rowHeight)
end
function TileMap:clampTileCol(col)
	return math.clamp(col, 1, self:getTileCols())
end
function TileMap:clampTileRow(row)
	return math.clamp(row, 1, self:getTileRows())
end

function TileMap:getTilesRenderRangeRows(camera)
	local startRow = self:clampTileRow(self:yToTileRow(camera.top, camera))
	local endRow = self:clampTileRow(self:yToTileRow(camera.bottom, camera))
	return startRow, endRow
end
function TileMap:getTilesRenderRangeCols(camera)
	local startCol = self:clampTileCol(self:xToTileCol(camera.left, camera))
	local endCol = self:clampTileCol(self:xToTileCol(camera.right, camera))
	return startCol, endCol
end


function TileMap:actorXToCamera(x, camera)
	return x - camera.cx
end
function TileMap:actorYToCamera(y, camera)
	return y - camera.cy
end
function TileMap:xToEntityX(x, camera)
	return x + camera.cx
end
function TileMap:yToEntityY(y, camera)
	return y + camera.cy
end