import bpy
import os
from math import radians

# ---------------------------------------------------------------------------
# Parametros
# ---------------------------------------------------------------------------
BASE   = "/Users/danicosta/Desktop/Avatar-Platon"
IMG    = os.path.join(BASE, "plato-image.png")
BLEND  = os.path.join(BASE, "Plato_Avatar.blend")
PREVIEW = os.path.join(BASE, "Plato_preview.png")

LONG_SUBDIV   = 620      # subdivisiones en el lado largo (alto)
DISP_STRENGTH = 0.32     # profundidad del relieve
BEND_ANGLE    = 26       # curvatura suave del busto (grados)
SMOOTH_ITER   = 5        # suavizado del relieve

# ---------------------------------------------------------------------------
# Escena limpia
# ---------------------------------------------------------------------------
bpy.ops.wm.read_factory_settings(use_empty=True)
scene = bpy.context.scene
coll = scene.collection

# ---------------------------------------------------------------------------
# Imagen
# ---------------------------------------------------------------------------
img = bpy.data.images.load(IMG)
W, H = img.size[0], img.size[1]
aspect = H / W                      # alto / ancho
print("IMG size:", W, H, "aspect:", aspect)

x_sub = max(8, int(round(LONG_SUBDIV / aspect)))
y_sub = LONG_SUBDIV
print("grid subdiv:", x_sub, y_sub)

# ---------------------------------------------------------------------------
# Malla base (grid denso) -> aspecto correcto
# ---------------------------------------------------------------------------
bpy.ops.mesh.primitive_grid_add(x_subdivisions=x_sub, y_subdivisions=y_sub,
                                size=2.0, location=(0, 0, 0))
obj = bpy.context.active_object
obj.name = "Plato_Avatar"
# escalar el eje Y al aspecto de la imagen y aplicar
obj.scale = (1.0, aspect, 1.0)
bpy.ops.object.transform_apply(location=False, rotation=False, scale=True)
# poner de pie (de plano XY a plano vertical XZ, mirando a -Y)
obj.rotation_euler = (radians(90), 0, 0)

mesh = obj.data
for p in mesh.polygons:
    p.use_smooth = True

# ---------------------------------------------------------------------------
# Modificadores: curvatura -> desplazamiento (relieve) -> suavizado
# ---------------------------------------------------------------------------
bend = obj.modifiers.new("Curvatura", 'SIMPLE_DEFORM')
bend.deform_method = 'BEND'
bend.deform_axis = 'Y'
bend.angle = radians(BEND_ANGLE)

disp_tex = bpy.data.textures.new("Plato_Disp", type='IMAGE')
disp_tex.image = img
disp = obj.modifiers.new("Relieve", 'DISPLACE')
disp.texture = disp_tex
disp.texture_coords = 'UV'
disp.direction = 'NORMAL'
disp.mid_level = 0.5
disp.strength = DISP_STRENGTH

sm = obj.modifiers.new("Suavizado", 'SMOOTH')
sm.factor = 0.5
sm.iterations = SMOOTH_ITER

# ---------------------------------------------------------------------------
# Material con la textura
# ---------------------------------------------------------------------------
mat = bpy.data.materials.new("Plato_Mat")
mat.use_nodes = True
nt = mat.node_tree
bsdf = nt.nodes.get("Principled BSDF")
bsdf.inputs["Roughness"].default_value = 0.82
if "Specular IOR Level" in bsdf.inputs:
    bsdf.inputs["Specular IOR Level"].default_value = 0.18

coord = nt.nodes.new("ShaderNodeTexCoord")
coord.location = (-720, 300)
tex_node = nt.nodes.new("ShaderNodeTexImage")
tex_node.image = img
tex_node.location = (-420, 300)
nt.links.new(coord.outputs["UV"], tex_node.inputs["Vector"])
nt.links.new(tex_node.outputs["Color"], bsdf.inputs["Base Color"])

obj.data.materials.append(mat)

# ---------------------------------------------------------------------------
# Mundo (fondo oscuro)
# ---------------------------------------------------------------------------
world = bpy.data.worlds.new("Mundo")
scene.world = world
world.use_nodes = True
bg = world.node_tree.nodes["Background"]
bg.inputs["Color"].default_value = (0.018, 0.018, 0.022, 1.0)
bg.inputs["Strength"].default_value = 0.35

# ---------------------------------------------------------------------------
# Luces
# ---------------------------------------------------------------------------
def add_area(name, loc, energy, size):
    ld = bpy.data.lights.new(name, 'AREA')
    ld.energy = energy
    ld.size = size
    lo = bpy.data.objects.new(name, ld)
    coll.objects.link(lo)
    lo.location = loc
    c = lo.constraints.new('TRACK_TO')
    c.target = obj
    return lo

add_area("Key",  (-2.6, -4.2,  2.2), 2200, 4.0)
add_area("Fill", ( 3.0, -3.6,  0.4), 1100, 5.0)
add_area("Rim",  ( 2.2, -1.0,  3.2), 1500, 2.5)

# ---------------------------------------------------------------------------
# Camara
# ---------------------------------------------------------------------------
cam_data = bpy.data.cameras.new("Camara")
cam_data.lens = 50
cam_data.sensor_fit = 'VERTICAL'
cam = bpy.data.objects.new("Camara", cam_data)
coll.objects.link(cam)
cam.location = (-1.4, -8.6, 0.35)
cc = cam.constraints.new('TRACK_TO')
cc.target = obj
scene.camera = cam

# ---------------------------------------------------------------------------
# Render (preview)
# ---------------------------------------------------------------------------
try:
    scene.render.engine = 'BLENDER_EEVEE_NEXT'
except Exception as e:
    print("engine fallback:", e)
    scene.render.engine = 'BLENDER_EEVEE'
try:
    scene.eevee.taa_render_samples = 64
except Exception:
    pass

# color management: textura mas fiel a la foto
try:
    scene.view_settings.view_transform = 'Standard'
    scene.view_settings.look = 'None'
except Exception as e:
    print("view transform warn:", e)

scene.render.resolution_x = 620
scene.render.resolution_y = int(round(620 * aspect))
scene.render.film_transparent = False
scene.render.image_settings.file_format = 'PNG'
scene.render.filepath = PREVIEW
bpy.ops.render.render(write_still=True)
print("PREVIEW saved:", PREVIEW)

# segunda vista en angulo 3/4 para mostrar la profundidad del relieve
cam.location = (-6.2, -6.6, 0.6)
scene.render.filepath = os.path.join(BASE, "Plato_preview_angle.png")
bpy.ops.render.render(write_still=True)
print("PREVIEW angle saved")
cam.location = (-1.4, -8.6, 0.35)   # restaurar vista frontal para el .blend

# ---------------------------------------------------------------------------
# Empaquetar textura y guardar .blend autonomo
# ---------------------------------------------------------------------------
try:
    bpy.ops.file.pack_all()
except Exception as e:
    print("pack warn:", e)
bpy.ops.wm.save_as_mainfile(filepath=BLEND)
print("BLEND saved:", BLEND)
print("DONE")
