import bpy
import random
import mathutils, mathutils.noise
# from mathutils.noise import types
import math


# large buildings can't be too high
# buildings can't be too thin
# size map

def create_cube(pos, size, verts, faces):
	half_size_x = size[0] / 2
	half_size_y = size[1] / 2
	pos_x = pos[0]
	pos_y = pos[1]
	pos_z = pos[2]
	height = size[2]
	total_verts = len(verts)
	faces.extend([
		(total_verts + 0, total_verts + 1, total_verts + 2, total_verts + 3),
		(total_verts + 1, total_verts + 6, total_verts + 5, total_verts + 2),
		(total_verts + 6, total_verts + 7, total_verts + 4, total_verts + 5),
		(total_verts + 0, total_verts + 3, total_verts + 4, total_verts + 7),
		(total_verts + 2, total_verts + 5, total_verts + 4, total_verts + 3),
	])
	height_below_pos = 30
	verts.extend([
		(pos_x - half_size_x, pos_y - half_size_y, pos_z + 0 - height_below_pos),
		(pos_x + half_size_x, pos_y - half_size_y, pos_z + 0 - height_below_pos),
		(pos_x + half_size_x, pos_y - half_size_y, pos_z + height),
		(pos_x - half_size_x, pos_y - half_size_y, pos_z + height),
		(pos_x - half_size_x, pos_y + half_size_y, pos_z + height),
		(pos_x + half_size_x, pos_y + half_size_y, pos_z + height),
		(pos_x + half_size_x, pos_y + half_size_y, pos_z + 0 - height_below_pos),
		(pos_x - half_size_x, pos_y + half_size_y, pos_z + 0 - height_below_pos),
	])


total_buildings = 20000
area_side_length_meters = 10000
verts = []
faces = []
bpy.ops.object.mode_set(mode='EDIT')
bpy.ops.mesh.delete()
bpy.ops.object.mode_set(mode='OBJECT')
max_x_y_ratio = 5
max_xy_z_ratio = 10

random.seed(1)


def get_red_pixel(pixels, image_width, x, y):
	indicePixel = y * image_width * 4 + x * 4
	r = pixels[indicePixel + 0]
	return r


heightmap_image = bpy.data.images['heightmap.exr']
heightmap_resolution = heightmap_image.size[0]
pixels = heightmap_image.pixels[:]
heightmap = [[get_red_pixel(pixels, heightmap_resolution, j, i) for i in range(heightmap_resolution)] for j in range(heightmap_resolution)]

random_pos = (random.uniform(-100, 100), random.uniform(-100, 100), random.uniform(-100, 100))
for buildingNb in range(total_buildings):
	building_pos_x = random.uniform(-area_side_length_meters / 2, area_side_length_meters / 2)
	building_pos_y = random.uniform(-area_side_length_meters / 2, area_side_length_meters / 2)
	# building_pos_x = random.uniform(0, area_side_length_meters)
	# building_pos_y = random.uniform(0, area_side_length_meters)
	building_pos_z = 0
	# building_pos_z = heightmap[
	# 	math.floor((building_pos_x + area_side_length_meters / 2) / area_side_length_meters * heightmap_resolution)][
	# 	math.floor((building_pos_y + area_side_length_meters / 2) / area_side_length_meters * heightmap_resolution)
	# ]
	# building_pos_z = heightmap[
	# 	math.floor(building_pos_x / area_side_length_meters * heightmap_resolution)][
	# 	math.floor(building_pos_y / area_side_length_meters * heightmap_resolution)
	# ]
	building_pos = (building_pos_x, building_pos_y, building_pos_z)

	fractal_size = .0005

	size_map_value = (0 + mathutils.noise.fractal(
		(random_pos[0] + building_pos[0] * fractal_size, random_pos[1] + building_pos[1] * fractal_size, random_pos[2]),
		.5, 2, 4, mathutils.noise.types.CELLNOISE)) * 2

	size_map_value = min(1, size_map_value)
	if size_map_value <= 0:
		continue
	# size_map_value = max(0.5, size_map_value)

	size_x = max(10, max(10, random.gauss(25, 50)) * size_map_value ** 1.5)
	# size_x = max(10, random.uniform(25, 50) * size_map_value ** 1.5)
	size_y = max(10, max(10, random.gauss(25, 50)) * size_map_value ** 1.5)
	# size_y = max(10, random.uniform(25, 50) * size_map_value ** 1.5)
	# if size_y < size_x / max_x_y_ratio:
	# 	size_y = size_x / max_x_y_ratio
	# elif size_y > size_x * max_x_y_ratio:
	# 	size_y = size_x * max_x_y_ratio

	size_z = max(10, max(10, random.gauss(50, 100)) * size_map_value ** 1.5)
	# size_z = max(10, random.uniform(50, 200) * size_map_value ** 1.5)
	# size_min_xy = min(size_x, size_y)
	# if size_z > size_min_xy * max_xy_z_ratio:
	# 	size_z = size_min_xy * max_xy_z_ratio

	building_size = (
		size_x,
		size_y,
		size_z
	)

	create_cube(building_pos, building_size, verts, faces)

mesh = bpy.data.meshes['city']
mesh.from_pydata(verts, [], faces)
mesh.update()
