dofile("../defs/area_shape_defs.lua")
Area = class("Area")

function Area:new(def, level, index)
	local area = instance(self)
	area.def = def	
	area.index = index
	area.level = level
	area.col = 0
	area.row = 0
	area.bounds = Bounds:new(0,0,0,0)
	area.shapes = {}
	return area
end

function Area:update_bounds()
	local bounds = self.bounds
	bounds:set(0,0,0,0)
	for index, shape in pairs(self.shapes) do
		shape:update_bounds()
		bounds:expandFromOffset(shape.bounds, shape.col, shape.row)
	end
	--print(bounds:get_width(), bounds:get_height())
end

function Area:get_range()
	local bounds = self.bounds
	return self.col + bounds.l, self.col + bounds.r, self.row + bounds.top, self.row + bounds.bottom
end

function Area:get_hypo()
	local bounds = self.bounds
	return bounds:getHypo()
end

function Area:create_shape(col, row, tag)
	local shape = Shape:new(col, row, tag)
	self:add_shape(shape)
	return shape
end

function Area:set_rules(rules)
	self.rules = rules	
end

function Area:set_team(team)
	self.team = team	
end

function Area:set_location(location)
	self.location = location	
end


function Area:set_col_row(col, row)
	self.col = col
	self.row = row
end

function Area:add_shape(shape)
	table.insert(self.shapes, shape)
end

function Area:generate()
	local def = self.def
	local generator = assert(def.generator, "no generator for area!!")
	local gen_def = AREA_SHAPE_GENERATORS[generator]
	gen_def.generate(self, def)
	self.buffer = def.room_buffer or 0
	self:update_bounds()
end

function Area:is_area_intersecting_any_shape(area)
	local bounds = area.bounds
	local ax,ay = area.col, area.row
	local x,y = self.col, self.row
	for index, shape in pairs(self.shapes) do
		local sx = x+shape.col
		local sy = y+shape.row
		if shape.bounds:isBoundsInside(bounds, ax-sx, ay-sy, self.buffer) then
			return true
		end
	end
	return false
end


local function calculate_mask_pool(mask)
	local total = 0
	for col, cells in pairs(mask) do
		for row, v in pairs(cells) do
			if v then
				total = total + 1
			end
		end
	end
	return total
end

local function select_mask_pool(mask, roll)	
	for col, cells in pairs(mask) do		
		for row, v in pairs(cells) do
			if v then
				if roll > 1 then
					roll = roll - 1
				else						
					return col,row
				end
			end
		end
	end
end

function Area:apply_tiles_universe(universe)
	--print("decorating", self.index)
	local def = self.def
	local zones = def.zones
	local rules = self.rules

	local area_col = self.col
	local area_row = self.row
	local level = self.level
	local team = self.team
	local loot_team = team:get_loot_team()

	for index, shape in ipairs(self.shapes) do
		local shape_col = area_col + shape.col
		local shape_row = area_row + shape.row
		local zone_type = zones[shape.tag]
		if zone_type then
			local zone_rule = rules:generate_instance("zone", zone_type, level)
			local tile_pool = zone_rule.tiles
			if tile_pool then
				local total = table.calculate_total_pool(tile_pool)
				for col, cells in pairs(shape.mask) do
					for row, v in pairs(cells) do
						if v then
							local tile_type = table.roll_pool(tile_pool, total)
							local rcol = shape_col + col
							local rrow = shape_row + row
							if not universe:is_tile_generated(rcol, rrow) then
								universe:set_tile(rcol, rrow, tile_type)
							else
								cells[row] = nil--clear already used area
							end
						end
					end
				end
			end
		end
	end
end

local function is_mask_wall(mask, col, row)
	local pre_col = mask[col-1]
	local nex_col = mask[col+1]
	local cur_col = mask[col]
	if cur_col and cur_col[row] then
		if pre_col and pre_col[row] and nex_col and nex_col[row] then
			return true
		end
		
		if cur_col[row-1] and cur_col[row+1] then
			return true
		end
	end
	return false
end

function Area:apply_characters_universe(universe)
	--print(self.index)
	local def = self.def
	local zones = def.zones
	local rules = self.rules

	local area_col = self.col
	local area_row = self.row
	local level = self.level
	local team = self.team
	local loot_team = team:get_loot_team()
	local location = self.location


	for index, shape in ipairs(self.shapes) do
		local shape_col = area_col + shape.col
		local shape_row = area_row + shape.row
		local zone_type = zones[shape.tag]
		if zone_type then
			local zone_rule = rules:generate_instance("zone", zone_type, level)
			
			local walls_pool = zone_rule.walls
			local corners_pool = zone_rule.corners
			if walls_pool or corners_pool then
				local total = table.calculate_total_pool(walls_pool)
				local mask = shape.mask
				for col, cells in pairs(mask) do
					for row, v in pairs(cells) do
						local rcol = shape_col + col
						local rrow = shape_row + row

						local pool = is_mask_wall(mask, col, row) and walls_pool or corners_pool
						pool = pool or walls_pool

						if pool then							
							local char_type = table.roll_pool(pool, total)
							local ent = rules:generate_instance("character", char_type, 1)
							local x,y = universe:col_row_to_center_position(rcol,rrow)								
							ent:set_position(x,y,0)
							universe:add_ent(ent)
						end
					end
				end
			end

			local character_pool = zone_rule.characters
			if character_pool then
				local mask = shape.mask
				local total_tile_pool = calculate_mask_pool(mask)
				local pool = {}
				
				for i=1,total_tile_pool do
					table.insert(pool,i)
				end

				for character, num in pairs(character_pool) do
					for i=1,num do
						local tries = 0
						while true do
							tries = tries + 1
							local num_in_pool = #pool
							if num_in_pool <= 0 then
								--print("no more space in shape!")
								--ran out of space
								break
							end
							local sel = math.random(1,num_in_pool)
							local col,row = select_mask_pool(mask, pool[sel])
							table.remove(pool, sel)
							if col then
								local rcol = shape_col + col
								local rrow = shape_row + row
								if not universe:is_tile_blocking_at_col_row(rcol, rrow) then
									if not universe:is_tile_occupied(rcol, rrow) then
										local ent = rules:generate_instance("character", character, level)
										local x,y = universe:col_row_to_center_position(rcol,rrow)								
										ent:set_position(x,y,0)
										if team and ent.aspects.team_owned then
											if ent.container then
												ent:set_team(loot_team)
											else
												ent:set_team(team)
											end
										end
										if ent.aspects.home_location then
											ent:set_location(location)
										end
										universe:add_ent(ent)
										break	
									end
								end
							end
							--if tries > 20 then
							--	print("failed to place", character)
							--	break
							--end
						end
					end
				end
			end
			--print("ASSIGNING", zone_type)
		end
	end
end