import bpy
import random
from . import commun
from . import utils


class SC_OT_generer_batiment_aleatoire(bpy.types.Operator):
	bl_idname = 'scene.sc_op_generer_batiment_aleatoire'
	bl_description = 'Regenerate the building mesh using the chosen building algorithm and settings. CAUTION: this operation will overwrite this mesh!'
	bl_label = 'Generate building (overwrites current mesh!)'
	bl_options = {'REGISTER', 'UNDO'}

	@classmethod
	def poll(cls, context):
		return context.object is not None

	def execute(self, context):
		# clear mesh
		mesh = context.object.__data
		# bpy.context.scene.objects.active = context.object
		bpy.context.view_layer.objects.active = context.object
		utils.definir_blender_mode('EDIT')
		bpy.ops.mesh.select_all(action='SELECT')
		bpy.ops.mesh.delete(type='VERT')
		utils.definir_blender_mode('OBJECT')

		verts = []
		faces = []
		no_material_slot_par_no_face = []

		# éléments communs à tous les types de batiments procéduraux
		random.seed(mesh.SC_proc_building.bati_procedural_seed if mesh.SC_proc_building.bati_procedural_seed != 0 else None)
		hauteur_sol = random.uniform(mesh.SC_proc_building.bati_procedural_sol_hauteur[0],
			mesh.SC_proc_building.bati_procedural_sol_hauteur[1]) if mesh.SC_proc_building.bati_procedural_mettre_sol else 0
		surface_constructible = (
			(-mesh.SceneCity.batiment_surface_10m_x * 10 / 2, -mesh.SceneCity.batiment_surface_10m_y * 10 / 2),
			(mesh.SceneCity.batiment_surface_10m_x * 10 / 2, mesh.SceneCity.batiment_surface_10m_y * 10 / 2))

		for no_building in range(mesh.SC_proc_building.bati_procedural_total_buildings):
			if mesh.SceneCity_mesh.batiment_type == 'procedural cube':
				generer_bati_cubes(
					# mesh_datablock = mesh,
					verts=verts,
					faces=faces,
					niveaux_à_construire=mesh.SC_proc_building.bati_procedural_total_niveaux,
					taille_min_xy=mesh.SC_proc_building.bati_procedural_cube_taille_xy[0],
					taille_max_xy=mesh.SC_proc_building.bati_procedural_cube_taille_xy[1],
					hauteur_min=mesh.SC_proc_building.bati_procedural_cube_taille_z[0],
					hauteur_max=mesh.SC_proc_building.bati_procedural_cube_taille_z[1],
					surface_constructible=surface_constructible,
					mettre_cubes_sur_toit=mesh.SC_proc_building.bati_procedural_cubes_mettre_sur_toit,
					cubes_sur_toit_min=mesh.SC_proc_building.bati_procedural_cubes_toits_total[0],
					cubes_sur_toit_max=mesh.SC_proc_building.bati_procedural_cubes_toits_total[1],
					cubes_sur_toit_taille_min=(
						mesh.SC_proc_building.bati_procedural_cubes_toits_taille_xy[0], mesh.SC_proc_building.bati_procedural_cubes_toits_taille_z[0]),
					cubes_sur_toit_taille_max=(
						mesh.SC_proc_building.bati_procedural_cubes_toits_taille_xy[1], mesh.SC_proc_building.bati_procedural_cubes_toits_taille_z[1]),
					hauteur_cube_sol=hauteur_sol if no_building == 0 else 0,
					protection_piétons_activée=mesh.SC_proc_building.bati_procedural_prot_piétons_activée,
					largeur_protection_piétons=random.uniform(mesh.SC_proc_building.bati_procedural_prot_piétons_taille_xy[0],
						mesh.SC_proc_building.bati_procedural_prot_piétons_taille_xy[1]),
					hauteur_protection_piétons=random.uniform(mesh.SC_proc_building.bati_procedural_prot_piétons_taille_z[0],
						mesh.SC_proc_building.bati_procedural_prot_piétons_taille_z[1]),
					hauteur_boutiques=random.uniform(mesh.SC_proc_building.bati_procedural_cube_boutiques_taille_z[0],
						mesh.SC_proc_building.bati_procedural_cube_boutiques_taille_z[1]),
					hauteur_départ=0 if no_building == 0 else hauteur_sol,
					no_material_slot_par_no_face=no_material_slot_par_no_face,
					réduction_taille_niveaux_sup_xy=mesh.SC_proc_building.bati_procedural_cube_size_reduc_xy / 100,
					réduction_taille_niveaux_sup_z=mesh.SC_proc_building.bati_procedural_cube_size_reduc_z / 100,
				)

			'''elif mesh.SceneCity_mesh.batiment_type == 'procedural skyscraper':
				generer_bati_skyscraper_03(
					verts = verts, 
					faces = faces, 
					position_dernière_construction = [0,0,hauteur_sol if i != 0 else 0], 
					hauteur_sol = 0,
					rotZ = 0, 
					total_floors = random.randint(mesh.SC_proc_building.bati_procedural_03_total_floors_min, mesh.SC_proc_building.bati_procedural_03_total_floors_max), 
					surface_constructible = surface_constructible,
					hauteur_cube_sol = hauteur_sol if i == 0 else 0,
					taille_floor_min = (
						mesh.SC_proc_building.bati_procedural_03_floor_size_xy_min, 
						mesh.SC_proc_building.bati_procedural_03_floor_size_xy_min, 
						mesh.SC_proc_building.bati_procedural_03_floor_size_z_min), 
					taille_floor_max = (
						mesh.SC_proc_building.bati_procedural_03_floor_size_xy_max, 
						mesh.SC_proc_building.bati_procedural_03_floor_size_xy_max, 
						mesh.SC_proc_building.bati_procedural_03_floor_size_z_max), 
					niveaux_à_construire = mesh.SC_proc_building.bati_procedural_total_niveaux,
					colonnes_cotés_mode = mesh.SC_proc_building.bati_procedural_03_colonnes_mode,
					colonnes_ont_même_taille = mesh.SC_proc_building.bati_procedural_03_colonnes_même_taille,
					taille_colonnes_min = mesh.SC_proc_building.bati_procedural_03_taille_colonnes_min,
					taille_colonnes_max = mesh.SC_proc_building.bati_procedural_03_taille_colonnes_max,
					espace_colonnes_min = mesh.SC_proc_building.bati_procedural_03_espace_colonnes_min,
					espace_colonnes_max = mesh.SC_proc_building.bati_procedural_03_espace_colonnes_max,
					profondeur_fenetres_min = mesh.SC_proc_building.bati_procedural_03_profondeur_fenetres_min,
					profondeur_fenetres_max = mesh.SC_proc_building.bati_procedural_03_profondeur_fenetres_max,
					espace_floors_min = mesh.SC_proc_building.bati_procedural_03_espace_floors_min,
					espace_floors_max = mesh.SC_proc_building.bati_procedural_03_espace_floors_max,
					réduction_floors_par_niveau_pourcent_min = mesh.SC_proc_building.bati_procedural_03_réduction_floors_par_niveau_min,
					réduction_floors_par_niveau_pourcent_max = mesh.SC_proc_building.bati_procedural_03_réduction_floors_par_niveau_max,
					réduction_taille_floors_par_niveau_pourcent_min = mesh.SC_proc_building.bati_procedural_03_réduction_taille_floors_par_niveau_min,
					réduction_taille_floors_par_niveau_pourcent_max = mesh.SC_proc_building.bati_procedural_03_réduction_taille_floors_par_niveau_max,
					hauteur_toit_min = mesh.SC_proc_building.bati_procedural_03_hauteur_toit_min,
					hauteur_toit_max = mesh.SC_proc_building.bati_procedural_03_hauteur_toit_max,
					doit_centrer_niveaux = mesh.SC_proc_building.bati_procedural_03_doit_centrer_niveaux,
					doit_créer_cubes_floors = mesh.SC_proc_building.bati_procedural_03_doit_créer_cubes_floors,
					doit_créer_floors_plus_gros = mesh.SC_proc_building.bati_procedural_03_doit_créer_floors_plus_gros,
					niveaux_commencent_sous_toit = mesh.SC_proc_building.bati_procedural_03_niveaux_commencent_sous_toit,
					floors_plus_gros_taille_additionelle = mesh.SC_proc_building.bati_procedural_03_floors_plus_gros_taille,
					cubes_sur_toit_min = mesh.SC_proc_building.bati_procedural_cubes_toits_min,
					cubes_sur_toit_max = mesh.SC_proc_building.bati_procedural_cubes_toits_max,
					cubes_sur_toit_taille_min = mesh.SC_proc_building.bati_procedural_cubes_toits_taille_min,
					cubes_sur_toit_taille_max = mesh.SC_proc_building.bati_procedural_cubes_toits_taille_max,
					largeur_protection_piétons = 4,
					hauteur_protection_piétons = .4,
				)'''
		mesh.from_pydata(verts, [], faces)
		# mesh.update(calc_tessface=True)
		mesh.update()

		# UV unwrap
		utils.definir_blender_mode('EDIT')
		bpy.ops.mesh.select_all(action='SELECT')
		bpy.ops.uv.cube_project()
		utils.definir_blender_mode('OBJECT')
		# context.scene.objects.active = context.object
		context.view_layer.objects.active = context.object

		# material slots
		for face_nb_mat_slot_nb in no_material_slot_par_no_face:
			while len(context.object.material_slots) <= face_nb_mat_slot_nb[1]:
				bpy.ops.object.material_slot_add()
			mesh.polygons[face_nb_mat_slot_nb[0]].material_index = face_nb_mat_slot_nb[1]

		return {'FINISHED'}


def dessiner_options(datablock, layout):
	if type(datablock) is bpy.types.Mesh:
		mesh = datablock

		# COMMUNS AUX BATIMENTS PROCEDURAUX
		if mesh.SceneCity_mesh.batiment_type in ['procedural skyscraper', 'procedural cube']:
			row = layout.row();
			row.prop(mesh.SC_proc_building, 'bati_procedural_seed')
			row = layout.row();
			row.prop(mesh.SC_proc_building, 'bati_procedural_total_buildings');
			row.prop(mesh.SC_proc_building, 'bati_procedural_total_niveaux')
			# sol
			subbox = layout.box()
			row = subbox.row();
			row.prop(mesh.SC_proc_building, 'bati_procedural_mettre_sol')
			if mesh.SC_proc_building.bati_procedural_mettre_sol:
				row = subbox.row();
				row.prop(mesh.SC_proc_building, 'bati_procedural_sol_hauteur');
			# cubes sur toit
			subbox = layout.box()
			row = subbox.row();
			row.prop(mesh.SC_proc_building, 'bati_procedural_cubes_mettre_sur_toit');
			if mesh.SC_proc_building.bati_procedural_cubes_mettre_sur_toit:
				row = subbox.row();
				row.prop(mesh.SC_proc_building, 'bati_procedural_cubes_toits_total');
				row = subbox.row();
				row.prop(mesh.SC_proc_building, 'bati_procedural_cubes_toits_taille_xy');
				row = subbox.row();
				row.prop(mesh.SC_proc_building, 'bati_procedural_cubes_toits_taille_z');
			# protection piétons
			subbox = layout.box()
			row = subbox.row();
			row.prop(mesh.SC_proc_building, 'bati_procedural_prot_piétons_activée');
			if mesh.SC_proc_building.bati_procedural_prot_piétons_activée:
				row = subbox.row();
				row.prop(mesh.SC_proc_building, 'bati_procedural_prot_piétons_taille_z');
			row = subbox.row();
			row.prop(mesh.SC_proc_building, 'bati_procedural_prot_piétons_taille_xy');

		# PROCEDURAL CUBE
		if mesh.SceneCity_mesh.batiment_type == 'procedural cube':
			row = layout.row();
			row.prop(mesh.SC_proc_building, 'bati_procedural_cube_taille_xy');
			row = layout.row();
			row.prop(mesh.SC_proc_building, 'bati_procedural_cube_taille_z');
			row = layout.row();
			row.prop(mesh.SC_proc_building, 'bati_procedural_cube_boutiques_taille_z');
			row = layout.row();
			row.prop(mesh.SC_proc_building, 'bati_procedural_cube_size_reduc_xy');
			row = layout.row();
			row.prop(mesh.SC_proc_building, 'bati_procedural_cube_size_reduc_z');

		# PROCEDURAL SKYSCRAPER
		elif mesh.SceneCity_mesh.batiment_type == 'procedural skyscraper':
			row = layout.row();
			row.prop(mesh.SC_proc_building, 'bati_procedural_03_catégorie_à_afficher', expand=True)
			if mesh.SC_proc_building.bati_procedural_03_catégorie_à_afficher == 'other':
				row = layout.row();
				row.label(text='Windows depth');
				row.prop(mesh.SC_proc_building, 'bati_procedural_03_profondeur_fenetres_min');
				row.prop(mesh.SC_proc_building, 'bati_procedural_03_profondeur_fenetres_max')
				# roof
				row = layout.row();
				row.label(text='Roof height');
				row.prop(mesh.SC_proc_building, 'bati_procedural_03_hauteur_toit_min');
				row.prop(mesh.SC_proc_building, 'bati_procedural_03_hauteur_toit_max')

				row = layout.row();
				row.prop(mesh.SC_proc_building, 'bati_procedural_03_doit_centrer_niveaux');
				row = layout.row();
				row.prop(mesh.SC_proc_building, 'bati_procedural_03_niveaux_commencent_sous_toit');

			elif mesh.SC_proc_building.bati_procedural_03_catégorie_à_afficher == 'floors':
				row = layout.row();
				row.label(text='Floors per level');
				row.prop(mesh.SC_proc_building, 'bati_procedural_03_total_floors_min');
				row.prop(mesh.SC_proc_building, 'bati_procedural_03_total_floors_max')
				row = layout.row();
				row.label(text='Floors horizontal size');
				row.prop(mesh.SC_proc_building, 'bati_procedural_03_floor_size_xy_min');
				row.prop(mesh.SC_proc_building, 'bati_procedural_03_floor_size_xy_max')
				row = layout.row();
				row.label(text='Floors height');
				row.prop(mesh.SC_proc_building, 'bati_procedural_03_floor_size_z_min');
				row.prop(mesh.SC_proc_building, 'bati_procedural_03_floor_size_z_max')
				row = layout.row();
				row.label(text='Floors gap');
				row.prop(mesh.SC_proc_building, 'bati_procedural_03_espace_floors_min');
				row.prop(mesh.SC_proc_building, 'bati_procedural_03_espace_floors_max')
				row = layout.row();
				row.label(text='Floors reduction per level');
				row.prop(mesh.SC_proc_building, 'bati_procedural_03_réduction_floors_par_niveau_min');
				row.prop(mesh.SC_proc_building, 'bati_procedural_03_réduction_floors_par_niveau_max');
				row = layout.row();
				row.label(text='Floors size reduction/level');
				row.prop(mesh.SC_proc_building, 'bati_procedural_03_réduction_taille_floors_par_niveau_min');
				row.prop(mesh.SC_proc_building, 'bati_procedural_03_réduction_taille_floors_par_niveau_max')
				row = layout.row();
				row.prop(mesh.SC_proc_building, 'bati_procedural_03_doit_créer_cubes_floors')
				row = layout.row();
				row.prop(mesh.SC_proc_building, 'bati_procedural_03_doit_créer_floors_plus_gros')
				if mesh.SC_proc_building.bati_procedural_03_doit_créer_floors_plus_gros:
					row = layout.row();
					row.label(text='Bigger floors additional size');
					row = layout.row();
					row.prop(mesh.SC_proc_building, 'bati_procedural_03_floors_plus_gros_taille')

			elif mesh.SC_proc_building.bati_procedural_03_catégorie_à_afficher == 'side columns':
				# colonnes
				row = layout.row();
				row.prop(mesh.SC_proc_building, 'bati_procedural_03_colonnes_mode')
				if mesh.SC_proc_building.bati_procedural_03_colonnes_mode != 'aucune colonne':
					row = layout.row();
					row.label(text='Columns size');
					row.prop(mesh.SC_proc_building, 'bati_procedural_03_taille_colonnes_min');
					row.prop(mesh.SC_proc_building, 'bati_procedural_03_taille_colonnes_max')
					row = layout.row();
					row.label(text='Columns gap size');
					row.prop(mesh.SC_proc_building, 'bati_procedural_03_espace_colonnes_min');
					row.prop(mesh.SC_proc_building, 'bati_procedural_03_espace_colonnes_max')
					row = layout.row();
					row.prop(mesh.SC_proc_building, 'bati_procedural_03_colonnes_même_taille')

		# bouton pour générer
		if mesh.SceneCity_mesh.batiment_type == 'procedural cube':
			row = layout.row();
			row.label(text='')
			if mesh.SceneCity_mesh.méthode_placement == 'merged mesh':
				row = layout.row();
				row.prop(mesh.SC_proc_building, 'bati_procedural_make_unique')
				if mesh.SC_proc_building.bati_procedural_make_unique:
					row = layout.row();
					row.prop(mesh.SC_proc_building, 'bati_procedural_nom_size_texture')
					if mesh.SC_proc_building.bati_procedural_nom_size_texture != '':
						if mesh.SC_proc_building.bati_procedural_nom_size_texture not in bpy.data.textures: row.label(text='', icon='CANCEL')
						else: row.label(text='', icon='FILE_TICK')
			# row = layout.row(); row.operator('scene.scenecity_op_generer_batiment_aleatoire', icon='MESH_DATA')
			row = layout.row();
			row.operator(SC_OT_generer_batiment_aleatoire.bl_idname, icon='MESH_DATA')


# class SceneCity_PropertyGroup_Mesh_procedural_building(bpy.types.PropertyGroup):
class SC_procedural_building(bpy.types.PropertyGroup):
	bati_procedural_total_niveaux: bpy.props.IntProperty(name='Recursions per building',
		description='How many times the same kind of building is repeated on the roof, recursively', default=2, min=1)
	bati_procedural_seed: bpy.props.IntProperty(name='Seed',
		description='0 to have a different building each time, other values to always have the same building corresponding to that seed and other attribute values.',
		default=0)
	# sol
	bati_procedural_mettre_sol: bpy.props.BoolProperty(name='Put ground', description='', default=True)
	bati_procedural_sol_hauteur: bpy.props.FloatVectorProperty(name='Size Z', description='Min / max', default=(.1, .2), size=2)
	# cubes sur toits
	bati_procedural_cubes_mettre_sur_toit: bpy.props.BoolProperty(name='Roof cubes', description='Activate if you want cubes on the roof', default=True)
	bati_procedural_cubes_toits_total: bpy.props.IntVectorProperty(name='Total', description='Min / max', default=(0, 2), size=2)
	bati_procedural_cubes_toits_taille_xy: bpy.props.FloatVectorProperty(name='Size XY', description='Min / max', default=(.5, 10), size=2)
	bati_procedural_cubes_toits_taille_z: bpy.props.FloatVectorProperty(name='Size Z', description='Min / max', default=(.5, 3), size=2)
	# protection piétons
	bati_procedural_prot_piétons_activée: bpy.props.BoolProperty(name='Put pedestrian roof', description='', default=True)
	bati_procedural_prot_piétons_taille_z: bpy.props.FloatVectorProperty(name='Size Z', description='Min / max', default=(.1, .4), size=2)
	bati_procedural_prot_piétons_taille_xy: bpy.props.FloatVectorProperty(name='Space around building', description='Min / max', default=(1, 3), size=2)
	# CUBE
	bati_procedural_cube_taille_xy: bpy.props.FloatVectorProperty(name='Recursion 1 size XY', description='Min / max', default=(5, 10), size=2)
	bati_procedural_cube_taille_z: bpy.props.FloatVectorProperty(name='Recursion 1 size Z', description='Min / max', default=(5, 10), size=2)
	bati_procedural_cube_boutiques_taille_z: bpy.props.FloatVectorProperty(name='Ground floor size Z', description='Min / max', default=(2.5, 4), size=2)
	bati_procedural_cube_size_reduc_z: bpy.props.FloatProperty(name='Size reduction per recursion Z',
		description='Size of a recursion on Z in comparison to the recursion below, in percentage', default=50, min=0)
	bati_procedural_cube_size_reduc_xy: bpy.props.FloatProperty(name='Size reduction per recursion XY',
		description='Size of a recursion on XY in comparison to the recursion below, in percentage', default=50, min=0)
	bati_procedural_make_unique: bpy.props.BoolProperty(
		name='Make unique variations in city',
		description='When enabled, each building will be unique in the city, but using the same generation parameters. The placement method must be "merged mesh"')
	bati_procedural_nom_size_texture: bpy.props.StringProperty(
		name='Size control texture',
		description='(Optional) Name of the BI texture to control the size of each unique variation of this building in the city. The buildings on white pixels will be larger than the ones on darker pixels, but the exact effect depends on the procedural building algorithm used. The height is affected, but the horizontal size might be also.')
	# SKYSCRAPERS
	bati_procedural_03_catégorie_à_afficher: bpy.props.EnumProperty(
		name='Display category',
		description='',
		items=[
			('other', 'Main', '', 1),
			('floors', 'Floors', '', 2),
			('side columns', 'Side columns', '', 3),

		])
	# floors
	bati_procedural_03_total_floors_min: bpy.props.IntProperty(name='min', description='', default=20, min=1)
	bati_procedural_03_total_floors_max: bpy.props.IntProperty(name='max', description='', default=30, min=1)
	bati_procedural_03_floor_size_xy_min: bpy.props.FloatProperty(name='min', description='The minimum size of each floor on the xy plane', default=10, min=1)
	bati_procedural_03_floor_size_xy_max: bpy.props.FloatProperty(name='max', description='The maximum size of each floor on the xy plane', default=20, min=1)
	bati_procedural_03_floor_size_z_min: bpy.props.FloatProperty(name='min', description='The minimum height of each floor', default=2, min=.1)
	bati_procedural_03_floor_size_z_max: bpy.props.FloatProperty(name='max', description='The maximum height of each floor', default=4, min=.1)
	bati_procedural_03_espace_floors_min: bpy.props.FloatProperty(name='min', description='', default=1, min=.1)
	bati_procedural_03_espace_floors_max: bpy.props.FloatProperty(name='max', description='', default=2, min=.1)
	bati_procedural_03_réduction_floors_par_niveau_min: bpy.props.FloatProperty(name='min', description='', default=.4, min=.1)
	bati_procedural_03_réduction_floors_par_niveau_max: bpy.props.FloatProperty(name='max', description='', default=.6, min=.1)
	bati_procedural_03_réduction_taille_floors_par_niveau_min: bpy.props.FloatProperty(name='min',
		description='This affects only the xy size, not the height of the floors', default=.5, min=.1)
	bati_procedural_03_réduction_taille_floors_par_niveau_max: bpy.props.FloatProperty(name='max',
		description='This affects only the xy size, not the height of the floors', default=.8, min=.1)
	bati_procedural_03_hauteur_toit_min: bpy.props.FloatProperty(name='min', description='', default=3, min=.1)
	bati_procedural_03_hauteur_toit_max: bpy.props.FloatProperty(name='max', description='', default=10, min=.1)
	bati_procedural_03_doit_centrer_niveaux: bpy.props.BoolProperty(name='Center levels',
		description='If true, each level of the building will be centered, otherwise they will be randomly translated horizontally', default=False)
	bati_procedural_03_doit_créer_cubes_floors: bpy.props.BoolProperty(name='Create floor cubes',
		description='If enabled, the floors will be represented as cubes', default=True)
	bati_procedural_03_niveaux_commencent_sous_toit: bpy.props.BoolProperty(name='Next levels start below roof',
		description='If enabled, subsequent levels will start at the base of the roofs, and not at their top. This can break the monotony of the roofs sides, at the cost of more hidden geometry inside the roofs',
		default=False)
	bati_procedural_03_doit_créer_floors_plus_gros: bpy.props.BoolProperty(name='Create bigger floors',
		description='If enabled, some floors will be larger than normal, randomly', default=True)
	bati_procedural_03_floors_plus_gros_taille: bpy.props.FloatVectorProperty(name='',
		description='This will be added to the normal floor size. The values are on x, y and z')

	bati_procedural_03_profondeur_fenetres_min: bpy.props.FloatProperty(name='min', description='', default=.1, min=.1)
	bati_procedural_03_profondeur_fenetres_max: bpy.props.FloatProperty(name='max', description='', default=1, min=.1)
	# colonnes
	bati_procedural_03_colonnes_mode: bpy.props.EnumProperty(name='Columns mode', description='Choose how side columns will be placed',
		items=[('colonnes aléatoires', 'On random sides', '', 1),
			   ('colonnes sur chaque côté', 'On all sides', '', 2),
			   ('aucune colonne', 'No columns', '', 3), ])
	bati_procedural_03_taille_colonnes_min: bpy.props.FloatProperty(name='min', description='', default=1, min=.1)
	bati_procedural_03_taille_colonnes_max: bpy.props.FloatProperty(name='max', description='', default=2, min=.1)
	bati_procedural_03_espace_colonnes_min: bpy.props.FloatProperty(name='min', description='', default=1, min=.1)
	bati_procedural_03_espace_colonnes_max: bpy.props.FloatProperty(name='max', description='', default=2, min=.1)
	bati_procedural_total_buildings: bpy.props.IntProperty(name='Nb of buildings',
		description='How many buildings will be created on the same surface. They will probably collide.', default=1, min=1)
	bati_procedural_03_colonnes_même_taille: bpy.props.BoolProperty(name='All columns have same size and gap', description='', default=False)


def generer_bati_cubes(
		# mesh_datablock,
		verts,
		faces,
		hauteur_min,
		hauteur_max,
		taille_min_xy=None,
		taille_max_xy=None,
		surface_constructible=None,  # les x et y min et max qui décrivent sur quelle surface on peut construire,et la hauteur sur laquelle on commence
		niveaux_à_construire=1,
		hauteur_départ=0,
		mettre_cubes_sur_toit=True,
		cubes_sur_toit_min=0,
		cubes_sur_toit_max=10,
		cubes_sur_toit_taille_min=(.5, .5),
		cubes_sur_toit_taille_max=(10, 3),
		hauteur_cube_sol=0,  # 0 pour aucun cube de sol
		protection_piétons_activée=True,
		largeur_protection_piétons=0,
		hauteur_protection_piétons=0,
		hauteur_boutiques=0,
		no_material_slot_par_no_face=None,
		réduction_taille_niveaux_sup_xy=.6,
		réduction_taille_niveaux_sup_z=.5,
		est_cube_sur_toit=False,
):
	if niveaux_à_construire <= 0: return
	hauteur_dernière_construction = hauteur_départ
	face_actuelle = len(faces)

	# sol
	if hauteur_cube_sol:
		taille_cube_sol = (
			surface_constructible[1][0] - surface_constructible[0][0],
			surface_constructible[1][1] - surface_constructible[0][1],
			hauteur_cube_sol)
		pos_cube_sol = (
			(surface_constructible[0][0] + surface_constructible[1][0]) / 2,
			(surface_constructible[0][1] + surface_constructible[1][1]) / 2,
			0)
		utils.createCube(verts, faces,
			pos=pos_cube_sol,
			size=taille_cube_sol,
			bottomFace=True)
		hauteur_dernière_construction += hauteur_cube_sol
		if no_material_slot_par_no_face != None:
			for face_nb in range(6): no_material_slot_par_no_face.append((face_actuelle + face_nb, 0))
			face_actuelle += 6

	# mesures et location du corps, mais on le créé pas encore
	taille_max_corps = (
		surface_constructible[1][0] - surface_constructible[0][0] - largeur_protection_piétons * 2,
		surface_constructible[1][1] - surface_constructible[0][1] - largeur_protection_piétons * 2,)
	taille_corps = (
		min(taille_max_corps[0], random.uniform(taille_min_xy, taille_max_xy)),
		min(taille_max_corps[1], random.uniform(taille_min_xy, taille_max_xy)),
		random.uniform(hauteur_min, hauteur_max))
	position_boutiques = (
		random.uniform(surface_constructible[0][0] + taille_corps[0] / 2 + largeur_protection_piétons,
					   surface_constructible[1][0] - taille_corps[0] / 2 - largeur_protection_piétons),
		random.uniform(surface_constructible[0][1] + taille_corps[1] / 2 + largeur_protection_piétons,
					   surface_constructible[1][1] - taille_corps[1] / 2 - largeur_protection_piétons),
		hauteur_dernière_construction)

	# boutiques
	if hauteur_boutiques > 0:
		taille_boutiques = (taille_corps[0], taille_corps[1], hauteur_boutiques)
		utils.createCube(verts, faces, position_boutiques, taille_boutiques)
		hauteur_dernière_construction += hauteur_boutiques
		if no_material_slot_par_no_face != None:
			for face_nb in range(6): no_material_slot_par_no_face.append((face_actuelle + face_nb, 1))
			face_actuelle += 6

		# protection piétons
		if protection_piétons_activée and hauteur_protection_piétons > 0 and largeur_protection_piétons > 0:
			taille_piétons = (taille_corps[0] + largeur_protection_piétons * 2, taille_corps[1] + largeur_protection_piétons * 2, hauteur_protection_piétons)
			position_piétons = (position_boutiques[0], position_boutiques[1], hauteur_dernière_construction)
			utils.createCube(verts, faces, position_piétons, taille_piétons)
			if no_material_slot_par_no_face != None:
				for face_nb in range(6): no_material_slot_par_no_face.append((face_actuelle + face_nb, 2))
				face_actuelle += 6

	# créé le corps
	position_corps = (position_boutiques[0], position_boutiques[1], hauteur_dernière_construction)
	utils.createCube(verts, faces, position_corps, taille_corps)
	hauteur_dernière_construction += taille_corps[2]
	if no_material_slot_par_no_face != None:
		no_material_slot_par_no_face.append((face_actuelle + 0, 5 if est_cube_sur_toit else 3))
		no_material_slot_par_no_face.append((face_actuelle + 1, 5 if est_cube_sur_toit else 3))
		no_material_slot_par_no_face.append((face_actuelle + 2, 5 if est_cube_sur_toit else 3))
		no_material_slot_par_no_face.append((face_actuelle + 3, 5 if est_cube_sur_toit else 3))
		no_material_slot_par_no_face.append((face_actuelle + 4, 4))  # face du dessus
		no_material_slot_par_no_face.append((face_actuelle + 5, 5 if est_cube_sur_toit else 3))
		face_actuelle += 6

	# cubes sur toit
	if mettre_cubes_sur_toit:
		total_cubes_sur_toit = round(random.uniform(cubes_sur_toit_min, cubes_sur_toit_max))
		for i in range(int(total_cubes_sur_toit)):
			generer_bati_cubes(
				# mesh_datablock = None,
				verts=verts,
				faces=faces,
				hauteur_min=cubes_sur_toit_taille_min[1],
				hauteur_max=cubes_sur_toit_taille_max[1],
				taille_min_xy=cubes_sur_toit_taille_min[0],
				taille_max_xy=cubes_sur_toit_taille_max[0],
				surface_constructible=(
					(position_corps[0] - taille_corps[0] / 2, position_corps[1] - taille_corps[1] / 2),
					(position_corps[0] + taille_corps[0] / 2, position_corps[1] + taille_corps[1] / 2)),
				niveaux_à_construire=1,
				hauteur_départ=hauteur_dernière_construction,
				cubes_sur_toit_min=0,
				cubes_sur_toit_max=0,
				no_material_slot_par_no_face=no_material_slot_par_no_face,
				est_cube_sur_toit=True,
			)

	# niveaux suivants		
	generer_bati_cubes(
		# mesh_datablock = mesh_datablock,
		verts=verts,
		faces=faces,
		taille_min_xy=taille_min_xy * réduction_taille_niveaux_sup_xy,
		taille_max_xy=taille_max_xy * réduction_taille_niveaux_sup_xy,
		hauteur_min=hauteur_min * réduction_taille_niveaux_sup_z,
		hauteur_max=hauteur_max * réduction_taille_niveaux_sup_z,
		surface_constructible=(
			(position_corps[0] - taille_corps[0] / 2, position_corps[1] - taille_corps[1] / 2),
			(position_corps[0] + taille_corps[0] / 2, position_corps[1] + taille_corps[1] / 2)),
		niveaux_à_construire=niveaux_à_construire - 1,
		hauteur_départ=hauteur_dernière_construction,
		mettre_cubes_sur_toit=mettre_cubes_sur_toit,
		cubes_sur_toit_min=cubes_sur_toit_min,
		cubes_sur_toit_max=cubes_sur_toit_max,
		cubes_sur_toit_taille_min=cubes_sur_toit_taille_min,
		cubes_sur_toit_taille_max=cubes_sur_toit_taille_max,
		protection_piétons_activée=False,
		largeur_protection_piétons=0,
		hauteur_protection_piétons=0,
		no_material_slot_par_no_face=no_material_slot_par_no_face,
		réduction_taille_niveaux_sup_xy=réduction_taille_niveaux_sup_xy,
		réduction_taille_niveaux_sup_z=réduction_taille_niveaux_sup_z,
	)


def generer_bati_skyscraper_03(
		verts,
		faces,
		hauteur_sol,
		position_dernière_construction,  # doit être un tableau, et non un tuple, relative à hauteur_sol
		rotZ,
		total_floors,
		taille_floor_min,
		taille_floor_max,
		espace_floors_min,
		espace_floors_max,
		niveaux_à_construire,
		hauteur_cube_sol,  # None pour aucun cube de sol
		colonnes_cotés_mode,  # soit 'aucune colonne', 'colonnes aléatoires' ou 'colonnes sur chaque côté'
		surface_constructible=((-10, -10), (10, 10)),
		# les x et y min et max qui décrivent sur quelle surface on peut construire,et la hauteur sur laquelle on commence
		taille_colonnes_min=.1,
		taille_colonnes_max=2,
		espace_colonnes_min=1,
		espace_colonnes_max=2,
		profondeur_fenetres_min=.1,
		profondeur_fenetres_max=1,
		sideCubesProba=0.5,
		sideCubesNbMin=1,
		sideCubesNbMax=10,
		réduction_floors_par_niveau_pourcent_min=0.4,
		réduction_floors_par_niveau_pourcent_max=0.6,  # the next level will have that much percentage of the nb of floors of its predecessor
		réduction_taille_floors_par_niveau_pourcent_min=.5,
		réduction_taille_floors_par_niveau_pourcent_max=.8,
		colonnes_ont_même_taille=False,
		hauteur_toit_min=3,
		hauteur_toit_max=10,
		doit_centrer_niveaux=True,
		doit_créer_cubes_floors=True,
		doit_créer_floors_plus_gros=True,
		floors_plus_gros_taille_additionelle=(.5, .5, 1),
		niveaux_commencent_sous_toit=True,
		cubes_sur_toit_min=0,
		cubes_sur_toit_max=10,
		cubes_sur_toit_taille_min=(.5, .5),
		cubes_sur_toit_taille_max=(10, 3),
		largeur_protection_piétons=0,
		hauteur_protection_piétons=0,
		hauteur_boutiques=3,
):
	# choisir taille des floors, dans la limite autorisée
	taille_max_corps = (
		surface_constructible[1][0] - surface_constructible[0][0],
		surface_constructible[1][1] - surface_constructible[0][1],)
	taille_floor = (
		min(taille_max_corps[0], random.uniform(taille_floor_min[0], taille_floor_max[0])),
		min(taille_max_corps[1], random.uniform(taille_floor_min[1], taille_floor_max[1])),
		random.uniform(taille_floor_min[2], taille_floor_max[2]))
	if taille_floor[0] <= 2 or taille_floor[1] <= 2: return
	hauteur_entre_floors = random.uniform(espace_floors_min, espace_floors_max)
	# on décale le batiment horizontalement aléatoirement (attention à ne pas dépasser de la zone autorisée
	if doit_centrer_niveaux:
		position_corps = (
			(surface_constructible[0][0] + surface_constructible[1][0]) / 2,
			(surface_constructible[0][1] + surface_constructible[1][1]) / 2,
			hauteur_sol)
	else:
		position_corps = (
			random.uniform(surface_constructible[0][0] + taille_floor[0] / 2, surface_constructible[1][0] - taille_floor[0] / 2),
			random.uniform(surface_constructible[0][1] + taille_floor[1] / 2, surface_constructible[1][1] - taille_floor[1] / 2),
			hauteur_sol)

	# placer le sol
	if hauteur_cube_sol:
		taille_cube_sol = (
			surface_constructible[1][0] - surface_constructible[0][0],
			surface_constructible[1][1] - surface_constructible[0][1],
			hauteur_cube_sol)
		pos_cube_sol = (
			(surface_constructible[0][0] + surface_constructible[1][0]) / 2,
			(surface_constructible[0][1] + surface_constructible[1][1]) / 2,
			0)
		utils.createCube(verts, faces,
			pos=pos_cube_sol,
			size=taille_cube_sol,
			bottomFace=True,
			rotZ=rotZ,
			trans=[0, 0, 0])
		position_dernière_construction[2] += hauteur_cube_sol

	# protection piétons
	if hauteur_protection_piétons > 0 and largeur_protection_piétons > 0:
		taille_piétons = (taille_floor[0] + 2, taille_floor[1] + 2, .4)
		position_piétons = (position_corps[0], position_corps[1], 3 + position_dernière_construction[2])
		utils.createCube(verts, faces, position_piétons, taille_piétons)

	# floors
	bigFloorNb = 0
	floorBaseHeight = position_dernière_construction[2]
	for floor_nb in range(round(total_floors)):
		position_dernière_construction[2] += hauteur_entre_floors

		# make floor bigger, randomly
		if doit_créer_floors_plus_gros and bigFloorNb <= 0 and random.random() > 0.8:
			bigFloorNb = random.randint(2, 5)

		if bigFloorNb > 0:
			if doit_créer_cubes_floors:
				utils.createCube(verts, faces, position_corps,
					[taille_floor[0] + floors_plus_gros_taille_additionelle[0], taille_floor[1] + floors_plus_gros_taille_additionelle[1],
					 taille_floor[2] + floors_plus_gros_taille_additionelle[2]],
					True, rotZ, position_dernière_construction)
				position_dernière_construction[2] += taille_floor[2] + floors_plus_gros_taille_additionelle[2]
			bigFloorNb -= 1
		else:
			if doit_créer_cubes_floors:
				utils.createCube(verts, faces, position_corps,
					[taille_floor[0], taille_floor[1], taille_floor[2]],
					True, rotZ, position_dernière_construction)
				position_dernière_construction[2] += taille_floor[2]

	# toit
	hauteur_toit = random.uniform(hauteur_toit_min, hauteur_toit_max)
	utils.createCube(verts, faces, position_corps,
		[taille_floor[0], taille_floor[1], hauteur_toit],
		True, rotZ, position_dernière_construction)
	if not niveaux_commencent_sous_toit:
		position_dernière_construction[2] += hauteur_toit

	# cube intérieur
	profondeur_fenêtres = random.uniform(profondeur_fenetres_min, profondeur_fenetres_max)
	utils.createCube(verts, faces,
		pos=position_corps,
		size=[taille_floor[0] - profondeur_fenêtres * 2, taille_floor[1] - profondeur_fenêtres * 2, position_dernière_construction[2] - floorBaseHeight],
		bottomFace=True,
		rotZ=rotZ,
		trans=[position_dernière_construction[0], position_dernière_construction[1], floorBaseHeight], )

	# colonnes
	if colonnes_cotés_mode != 'aucune colonne' and int(total_floors) >= 1:
		hauteur_colonnes = position_dernière_construction[2] - floorBaseHeight
		if not niveaux_commencent_sous_toit:
			hauteur_colonnes = position_dernière_construction[2] - floorBaseHeight - hauteur_toit

		# choisir une taille des colonnes unique pour tous les côtés
		if colonnes_ont_même_taille:
			taille_colonnes_x = [
				random.uniform(taille_colonnes_min, taille_colonnes_max),
				random.uniform(taille_colonnes_min, taille_colonnes_max),
				hauteur_colonnes]
			taille_colonnes_y = [taille_colonnes_x[1], taille_colonnes_x[0], taille_colonnes_x[2]]
			espace_entre_chaque_colonne = random.uniform(espace_colonnes_min, espace_colonnes_max)

		for côté in ['nord', 'sud', 'est', 'ouest']:
			# ne pas placer certains côtés au hasard
			if colonnes_cotés_mode == 'colonnes aléatoires' and random.random() < sideCubesProba:
				continue

			# choisir une taille des colonnes diférente pour chaque côté
			if not colonnes_ont_même_taille:
				taille_colonnes_x = [
					random.uniform(taille_colonnes_min, taille_colonnes_max),
					random.uniform(taille_colonnes_min, taille_colonnes_max),
					hauteur_colonnes]
				taille_colonnes_y = [taille_colonnes_x[1], taille_colonnes_x[0], taille_colonnes_x[2]]
				espace_entre_chaque_colonne = random.uniform(espace_colonnes_min, espace_colonnes_max)

			# side cube initial pos
			sideCubePos = [
				position_dernière_construction[0],
				position_dernière_construction[1],
				floorBaseHeight]

			# north
			if côté == 'nord':
				total_colonnes_par_côté = taille_floor[0] // (taille_colonnes_x[0] + espace_entre_chaque_colonne)
				sideCubePos[0] += -taille_colonnes_x[0] / 4 - taille_floor[0] / 2
				sideCubePos[1] += taille_colonnes_x[1] / 4 + taille_floor[1] / 2
			# south
			elif côté == 'sud':
				total_colonnes_par_côté = taille_floor[0] // (taille_colonnes_x[0] + espace_entre_chaque_colonne)
				sideCubePos[0] += -taille_colonnes_x[0] / 4 - taille_floor[0] / 2
				sideCubePos[1] += -taille_colonnes_x[1] / 4 - taille_floor[1] / 2
			# east
			elif côté == 'est':
				total_colonnes_par_côté = taille_floor[1] // (taille_colonnes_y[1] + espace_entre_chaque_colonne)
				sideCubePos[0] += taille_colonnes_y[0] / 4 + taille_floor[0] / 2
				sideCubePos[1] += -taille_colonnes_y[1] / 4 - taille_floor[1] / 2
			# west
			elif côté == 'ouest':
				total_colonnes_par_côté = taille_floor[1] // (taille_colonnes_y[1] + espace_entre_chaque_colonne)
				sideCubePos[0] += -taille_colonnes_y[0] / 4 - taille_floor[0] / 2
				sideCubePos[1] += -taille_colonnes_y[1] / 4 - taille_floor[1] / 2

			# place les cubes
			for i in range(int(total_colonnes_par_côté)):
				taille_finale_colonne = taille_colonnes_x
				if côté == 'nord' or côté == 'sud':
					sideCubePos[0] += taille_finale_colonne[0] + espace_entre_chaque_colonne
				elif côté == 'est' or côté == 'ouest':
					taille_finale_colonne = taille_colonnes_y
					sideCubePos[1] += taille_finale_colonne[1] + espace_entre_chaque_colonne

				utils.createCube(verts, faces, position_corps, taille_finale_colonne, True, rotZ, trans=sideCubePos)

	# cubes sur toit
	total_cubes_sur_toit = round(random.uniform(cubes_sur_toit_min, cubes_sur_toit_max))
	for i in range(int(total_cubes_sur_toit)):
		generer_bati_cubes(
			verts,
			faces,
			cubes_sur_toit_taille_min[0],
			cubes_sur_toit_taille_max[0],
			cubes_sur_toit_taille_min[1],
			cubes_sur_toit_taille_max[1],
			surface_constructible=(
				(position_corps[0] - taille_floor[0] / 2, position_corps[1] - taille_floor[1] / 2),
				(position_corps[0] + taille_floor[0] / 2, position_corps[1] + taille_floor[1] / 2)),
			niveaux_à_construire=1,
			hauteur_départ=position_dernière_construction[2] + hauteur_toit if niveaux_commencent_sous_toit else position_dernière_construction[2],
			cubes_sur_toit_min=0,
			cubes_sur_toit_max=0,
		)

	if niveaux_à_construire <= 1 or int(total_floors) <= 0: return

	# repeat process on the roof
	réduction_taille_floors_niveau_suivant = random.uniform(réduction_taille_floors_par_niveau_pourcent_min, réduction_taille_floors_par_niveau_pourcent_max)
	# réduction_taille_floors_niveau_suivant = 1
	generer_bati_skyscraper_03(
		verts=verts,
		faces=faces,
		position_dernière_construction=position_dernière_construction,
		rotZ=rotZ,
		hauteur_sol=hauteur_sol,
		hauteur_cube_sol=None,
		réduction_floors_par_niveau_pourcent_min=réduction_floors_par_niveau_pourcent_min,
		réduction_floors_par_niveau_pourcent_max=réduction_floors_par_niveau_pourcent_max,
		réduction_taille_floors_par_niveau_pourcent_min=réduction_taille_floors_par_niveau_pourcent_min,
		réduction_taille_floors_par_niveau_pourcent_max=réduction_taille_floors_par_niveau_pourcent_max,
		surface_constructible=(
			(position_corps[0] - taille_floor[0] / 2, position_corps[1] - taille_floor[1] / 2),
			(position_corps[0] + taille_floor[0] / 2, position_corps[1] + taille_floor[1] / 2)),
		total_floors=total_floors * random.uniform(réduction_floors_par_niveau_pourcent_min, réduction_floors_par_niveau_pourcent_max),
		taille_floor_min=(
			taille_floor_min[0] * réduction_taille_floors_niveau_suivant,
			taille_floor_min[1] * réduction_taille_floors_niveau_suivant,
			taille_floor[2]),
		taille_floor_max=(
			taille_floor_max[0] * réduction_taille_floors_niveau_suivant,
			taille_floor_max[1] * réduction_taille_floors_niveau_suivant,
			taille_floor[2]),
		niveaux_à_construire=niveaux_à_construire - 1,
		colonnes_cotés_mode=colonnes_cotés_mode,
		taille_colonnes_min=taille_colonnes_min * .5,
		taille_colonnes_max=taille_colonnes_max * .5,
		espace_colonnes_min=espace_colonnes_min,
		espace_colonnes_max=espace_colonnes_max,
		colonnes_ont_même_taille=colonnes_ont_même_taille,
		profondeur_fenetres_min=profondeur_fenetres_min,
		profondeur_fenetres_max=profondeur_fenetres_max,
		espace_floors_min=espace_floors_min,
		espace_floors_max=espace_floors_max,
		hauteur_toit_min=hauteur_toit_min,
		hauteur_toit_max=hauteur_toit_max,
		doit_centrer_niveaux=doit_centrer_niveaux,
		doit_créer_cubes_floors=doit_créer_cubes_floors,
		doit_créer_floors_plus_gros=doit_créer_floors_plus_gros,
		floors_plus_gros_taille_additionelle=floors_plus_gros_taille_additionelle,
		niveaux_commencent_sous_toit=niveaux_commencent_sous_toit,
		cubes_sur_toit_min=cubes_sur_toit_min,
		cubes_sur_toit_max=cubes_sur_toit_max,
		cubes_sur_toit_taille_min=cubes_sur_toit_taille_min,
		cubes_sur_toit_taille_max=cubes_sur_toit_taille_max,
	)
