import subprocess
import sys
import os
import imageio
import numpy as np
from pathlib import Path
import textwrap

def to_windows_path(wsl_path):
    """
    Converts a WSL path to a Windows path using wslpath.
    """
    result = subprocess.run(['wslpath', '-w', wsl_path], capture_output=True, text=True)
    if result.returncode != 0:
        print(f"Error converting WSL path to Windows path: {result.stderr}")
        sys.exit(1)
    return result.stdout.strip()

def create_blender_script(frames_dir_win, logo_path_win, total_frames, initial_fall_frames, bounce_frames, rest_frames, fall_through_frames, loop_reset_frames):
    """
    Creates the Blender Python script for animating the logo.
    """
    blender_script = f"""
import bpy
import math

# ----- Configuration -----

# Output directory for rendered frames (Windows path)
frames_dir = r'{frames_dir_win}'

# Logo image path (Windows path)
logo_path = r'{logo_path_win}'

# Animation settings
frame_rate = 30
total_frames = {total_frames}
frame_size = (1024, 1024)

# Frame ranges
initial_fall_frames = {initial_fall_frames}
bounce_frames = {bounce_frames}
rest_frames = {rest_frames}
fall_through_frames = {fall_through_frames}
loop_reset_frames = {loop_reset_frames}

# ----- Render Settings -----

# Use EEVEE for faster rendering
bpy.context.scene.render.engine = 'BLENDER_EEVEE'

# Set resolution
bpy.context.scene.render.resolution_x = frame_size[0]
bpy.context.scene.render.resolution_y = frame_size[1]
bpy.context.scene.render.resolution_percentage = 100

# Set frame rate
bpy.context.scene.render.fps = frame_rate

# Set output settings
bpy.context.scene.render.image_settings.file_format = 'PNG'
bpy.context.scene.render.image_settings.color_mode = 'RGBA'
bpy.context.scene.render.film_transparent = True  # Transparent background

# Set output path prefix
bpy.context.scene.render.filepath = frames_dir + "/frame_"

# ----- Delete Default Objects -----

bpy.ops.object.select_all(action='SELECT')
bpy.ops.object.delete(use_global=False)

# ----- Import Logo as Plane -----

# Ensure the 'Import Images as Planes' add-on is enabled
if not bpy.ops.import_image.to_plane.poll():
    bpy.ops.preferences.addon_enable(module='io_import_images_as_planes')

# Import the logo
bpy.ops.import_image.to_plane(
    files=[{{'name': '{Path(logo_path_win).name}', 'name': '{Path(logo_path_win).name}'}}],
    directory=r'{Path(logo_path_win).parent.as_posix()}/',
    relative=True,
    align_axis='Z+',
    rotation=0
)

logo = bpy.context.active_object

# Set origin to center of mass
bpy.ops.object.origin_set(type='ORIGIN_CENTER_OF_MASS', center='BOUNDS')

# ----- Animate Fall and Bounces -----

# Initial Position: Above the frame
logo.location = (0, 0, 10)  # (X, Y, Z)
logo.keyframe_insert(data_path="location", frame=1)

# Insert keyframe at frame=initial_fall_frames (fall to ground)
logo.location = (0, 0, 0)
logo.keyframe_insert(data_path="location", frame=initial_fall_frames)

# Define bounce parameters
number_of_bounces = {5}
bounce_decay = {0.6}  # Each bounce height is 60% of the previous
initial_bounce_height = {4.0}  # Starting bounce height

current_frame = initial_fall_frames
current_height = initial_bounce_height

for i in range(number_of_bounces):
    # Time allocated per bounce (up and down)
    frames_per_bounce = int(bounce_frames / number_of_bounces)

    # Bounce Up
    current_frame += frames_per_bounce // 2
    logo.location = (0, 0, current_height)
    logo.keyframe_insert(data_path="location", frame=current_frame)

    # Bounce Down
    current_frame += frames_per_bounce // 2
    logo.location = (0, 0, 0)
    logo.keyframe_insert(data_path="location", frame=current_frame)

    # Decrease bounce height for next bounce
    current_height *= bounce_decay

# ----- Animate Rest Period -----

# Frames remaining for rest
current_frame += rest_frames
logo.location = (0, 0, 0)
logo.keyframe_insert(data_path="location", frame=current_frame)

# ----- Animate Fall Through Bottom -----

# Start falling through
logo.location = (0, 0, 0)
logo.keyframe_insert(data_path="location", frame=current_frame)

current_frame += fall_through_frames
logo.location = (0, 0, -10)  # Falls through bottom
logo.keyframe_insert(data_path="location", frame=current_frame)

# ----- Animate Loop Reset -----

# Jump back to initial position instantly (for looping)
logo.location = (0, 0, 10)
logo.keyframe_insert(data_path="location", frame=current_frame + loop_reset_frames)

# ----- Set Interpolation Mode to 'BEZIER' -----

for fcurve in logo.animation_data.action.fcurves:
    for keyframe_point in fcurve.keyframe_points:
        keyframe_point.interpolation = 'BEZIER'

# ----- Set Up Camera -----

# Add a camera
bpy.ops.object.camera_add(location=(0, -15, 5))
camera = bpy.context.active_object

# Point the camera at the logo
direction = logo.location - camera.location
rot_quat = direction.to_track_quat('-Z', 'Y')
camera.rotation_euler = rot_quat.to_euler()

# Set camera as active
bpy.context.scene.camera = camera

# ----- Render Animation -----

bpy.ops.render.render(animation=True)
    """
    blender_script = textwrap.dedent(blender_script)
    blender_script_path = Path("/mnt/c/Users/Jeff/logo/render/blender_script.py")
    
    if not blender_script_path.is_file():
        with open(blender_script_path, 'w') as f:
            f.write(blender_script)
        print(f"Blender script created at {blender_script_path}")
    else:
        print(f"Blender script already exists at {blender_script_path}")
    
    return blender_script_path

def main():
    """
    Automates Blender to create a "bounce" animated GIF of a logo:
    - Falls from top.
    - Bounces 5 times with decreasing heights.
    - Comes to rest for 7 seconds.
    - Falls through the bottom.
    - Loops seamlessly.
    """
    # ----- Configuration -----
    
    # Path to your logo image in WSL
    LOGO_PATH_WSL = "/mnt/c/Users/Jeff/logo/logo.png"  # Change if necessary
    
    # Path to save the final GIF in WSL
    OUTPUT_GIF_WSL = "/mnt/c/Users/Jeff/logo/logo_bounce.gif"  # Change if necessary
    
    # Blender executable path in WSL (access Windows executables via /mnt/c/)
    BLENDER_EXECUTABLE_WSL = "/mnt/c/Program Files/Blender/blender.exe"  # Adjust if Blender is installed elsewhere
    
    # Animation settings
    FRAME_SIZE = (1024, 1024)       # Width, Height in pixels
    FRAME_RATE = 30                 # Frames per second
    TOTAL_FRAMES = 660              # Total number of frames for full loop (~22 seconds)
    DURATION_PER_FRAME = 1 / FRAME_RATE  # Duration per frame in seconds
    
    # Define frame ranges for each animation phase
    # Total Frames: 660 (22 seconds at 30 FPS)
    INITIAL_FALL_FRAMES = 30        # 1 second
    BOUNCE_FRAMES = 200             # ~6.666 seconds (approx 5 bounces)
    REST_FRAMES = 210               # 7 seconds
    FALL_THROUGH_FRAMES = 70        # ~2.333 seconds
    LOOP_RESET_FRAMES = 50          # ~1.666 seconds
    
    # ----- Validate Paths -----
    
    logo_path = Path(LOGO_PATH_WSL)
    if not logo_path.is_file():
        print(f"Error: Logo file does not exist at path: {LOGO_PATH_WSL}")
        sys.exit(1)
    
    # ----- Convert WSL Paths to Windows Paths -----
    
    frames_dir_wsl = "/mnt/c/Users/Jeff/logo/render"
    frames_dir_win = to_windows_path(frames_dir_wsl)
    frames_dir = Path(frames_dir_wsl)
    frames_dir.mkdir(parents=True, exist_ok=True)
    
    logo_path_win = to_windows_path(str(logo_path))
    
    output_gif_win = to_windows_path(OUTPUT_GIF_WSL)
    
    # ----- Create Blender Python Script If Not Exists -----
    
    blender_script_path = Path("/mnt/c/Users/Jeff/logo/render/blender_script.py")
    if not blender_script_path.is_file():
        blender_script_code = f"""
    import bpy
    import math

    # ----- Configuration -----

    # Output directory for rendered frames (Windows path)
    frames_dir = r'{frames_dir_win}'

    # Logo image path (Windows path)
    logo_path = r'{logo_path_win}'

    # Animation settings
    frame_rate = {FRAME_RATE}
    total_frames = {TOTAL_FRAMES}
    frame_size = (1024, 1024)

    # Frame ranges
    initial_fall_frames = {INITIAL_FALL_FRAMES}
    bounce_frames = {BOUNCE_FRAMES}
    rest_frames = {REST_FRAMES}
    fall_through_frames = {FALL_THROUGH_FRAMES}
    loop_reset_frames = {LOOP_RESET_FRAMES}

    # ----- Render Settings -----

    # Use EEVEE for faster rendering
    bpy.context.scene.render.engine = 'BLENDER_EEVEE'

    # Set resolution
    bpy.context.scene.render.resolution_x = frame_size[0]
    bpy.context.scene.render.resolution_y = frame_size[1]
    bpy.context.scene.render.resolution_percentage = 100

    # Set frame rate
    bpy.context.scene.render.fps = frame_rate

    # Set output settings
    bpy.context.scene.render.image_settings.file_format = 'PNG'
    bpy.context.scene.render.image_settings.color_mode = 'RGBA'
    bpy.context.scene.render.film_transparent = True  # Transparent background

    # Set output path prefix
    bpy.context.scene.render.filepath = frames_dir + "/frame_"

    # ----- Delete Default Objects -----

    bpy.ops.object.select_all(action='SELECT')
    bpy.ops.object.delete(use_global=False)

    # ----- Import Logo as Plane -----

    # Ensure the 'Import Images as Planes' add-on is enabled
    if not bpy.ops.import_image.to_plane.poll():
        bpy.ops.preferences.addon_enable(module='io_import_images_as_planes')

    # Import the logo
    bpy.ops.import_image.to_plane(
        files=[{{'name': '{Path(logo_path_win).name}', 'name': '{Path(logo_path_win).name}'}}],
        directory=r'{Path(logo_path_win).parent.as_posix()}/',
        relative=True,
        align_axis='Z+',
        rotation=0
    )

    logo = bpy.context.active_object

    # Set origin to center of mass
    bpy.ops.object.origin_set(type='ORIGIN_CENTER_OF_MASS', center='BOUNDS')

    # ----- Animate Fall and Bounces -----

    # Initial Position: Above the frame
    logo.location = (0, 0, 10)  # (X, Y, Z)
    logo.keyframe_insert(data_path="location", frame=1)

    # Insert keyframe at frame=initial_fall_frames (fall to ground)
    logo.location = (0, 0, 0)
    logo.keyframe_insert(data_path="location", frame=initial_fall_frames)

    # Define bounce parameters
    number_of_bounces = {5}
    bounce_decay = {0.6}  # Each bounce height is 60% of the previous
    initial_bounce_height = {4.0}  # Starting bounce height

    current_frame = initial_fall_frames
    current_height = initial_bounce_height

    for i in range(number_of_bounces):
        # Time allocated per bounce (up and down)
        frames_per_bounce = int(bounce_frames / number_of_bounces)

        # Bounce Up
        current_frame += frames_per_bounce // 2
        logo.location = (0, 0, current_height)
        logo.keyframe_insert(data_path="location", frame=current_frame)

        # Bounce Down
        current_frame += frames_per_bounce // 2
        logo.location = (0, 0, 0)
        logo.keyframe_insert(data_path="location", frame=current_frame)

        # Decrease bounce height for next bounce
        current_height *= bounce_decay

    # ----- Animate Rest Period -----

    # Frames remaining for rest
    current_frame += rest_frames
    logo.location = (0, 0, 0)
    logo.keyframe_insert(data_path="location", frame=current_frame)

    # ----- Animate Fall Through Bottom -----

    # Start falling through
    logo.location = (0, 0, 0)
    logo.keyframe_insert(data_path="location", frame=current_frame)

    current_frame += fall_through_frames
    logo.location = (0, 0, -10)  # Falls through bottom
    logo.keyframe_insert(data_path="location", frame=current_frame)

    # ----- Animate Loop Reset -----

    # Jump back to initial position instantly (for looping)
    logo.location = (0, 0, 10)
    logo.keyframe_insert(data_path="location", frame=current_frame + loop_reset_frames)

    # ----- Set Interpolation Mode to 'BEZIER' -----

    for fcurve in logo.animation_data.action.fcurves:
        for keyframe_point in fcurve.keyframe_points:
            keyframe_point.interpolation = 'BEZIER'

    # ----- Set Up Camera -----

    # Add a camera
    bpy.ops.object.camera_add(location=(0, -15, 5))
    camera = bpy.context.active_object

    # Point the camera at the logo
    direction = logo.location - camera.location
    rot_quat = direction.to_track_quat('-Z', 'Y')
    camera.rotation_euler = rot_quat.to_euler()

    # Set camera as active
    bpy.context.scene.camera = camera

    # ----- Render Animation -----

    bpy.ops.render.render(animation=True)
        """
        blender_script = textwrap.dedent(blender_script)
        blender_script_path = Path(frames_dir_win) / "blender_script.py"
        blender_script_path_parent = blender_script_path.parent
        blender_script_path_parent.mkdir(parents=True, exist_ok=True)
        if not blender_script_path.is_file():
            with open(blender_script_path, 'w') as f:
                f.write(blender_script)
            print(f"Blender script created at {blender_script_path}")
        else:
            print(f"Blender script already exists at {blender_script_path}")
        
        return blender_script_path

def main():
    """
    Automates Blender to create a "bounce" animated GIF of a logo:
    - Falls from top.
    - Bounces 5 times with decreasing heights.
    - Comes to rest for 7 seconds.
    - Falls through the bottom.
    - Loops seamlessly.
    """
    # ----- Configuration -----
    
    # Path to your logo image in WSL
    LOGO_PATH_WSL = "/mnt/c/Users/Jeff/logo/logo.png"  # Change if necessary
    
    # Path to save the final GIF in WSL
    OUTPUT_GIF_WSL = "/mnt/c/Users/Jeff/logo/logo_bounce.gif"  # Change if necessary
    
    # Blender executable path in WSL (access Windows executables via /mnt/c/)
    BLENDER_EXECUTABLE_WSL = "/mnt/c/Program Files/Blender/blender.exe"  # Adjust if Blender is installed elsewhere
    
    # Animation settings
    FRAME_SIZE = (1024, 1024)       # Width, Height in pixels
    FRAME_RATE = 30                 # Frames per second
    TOTAL_FRAMES = 660              # Total number of frames for full loop (~22 seconds)
    DURATION_PER_FRAME = 1 / FRAME_RATE  # Duration per frame in seconds
    
    # Define frame ranges for each animation phase
    # Total Frames: 660 (22 seconds at 30 FPS)
    INITIAL_FALL_FRAMES = 30        # 1 second
    BOUNCE_FRAMES = 200             # ~6.666 seconds (approx 5 bounces)
    REST_FRAMES = 210               # 7 seconds
    FALL_THROUGH_FRAMES = 70        # ~2.333 seconds
    LOOP_RESET_FRAMES = 50          # ~1.666 seconds
    
    # ----- Validate Paths -----
    
    logo_path = Path(LOGO_PATH_WSL)
    if not logo_path.is_file():
        print(f"Error: Logo file does not exist at path: {LOGO_PATH_WSL}")
        sys.exit(1)
    
    # ----- Convert WSL Paths to Windows Paths -----
    
    frames_dir_wsl = "/mnt/c/Users/Jeff/logo/render"
    frames_dir_win = to_windows_path(frames_dir_wsl)
    frames_dir = Path(frames_dir_wsl)
    frames_dir.mkdir(parents=True, exist_ok=True)
    
    logo_path_win = to_windows_path(str(logo_path))
    
    output_gif_win = to_windows_path(OUTPUT_GIF_WSL)
    
    # ----- Create Blender Python Script If Not Exists -----
    
    blender_script_path = create_blender_script(
        frames_dir_win=frames_dir_win,
        logo_path_win=logo_path_win,
        total_frames=TOTAL_FRAMES,
        initial_fall_frames=INITIAL_FALL_FRAMES,
        bounce_frames=BOUNCE_FRAMES,
        rest_frames=REST_FRAMES,
        fall_through_frames=FALL_THROUGH_FRAMES,
        loop_reset_frames=LOOP_RESET_FRAMES
    )
    
    # ----- Run Blender with the Script -----
    print("Starting Blender to render animation...")
    
    blender_command = [
        BLENDER_EXECUTABLE_WSL,
        '--background',  # Runs Blender in background (no UI)
        '--python', str(blender_script_path)
    ]
    
    try:
        subprocess.run(blender_command, check=True)
        print("Blender rendering completed successfully.")
    except subprocess.CalledProcessError as e:
        print(f"Error: Blender failed with exit code {e.returncode}")
        sys.exit(1)
    
    # ----- Collect Rendered Frames -----
    print("Collecting rendered frames...")
    rendered_frames = sorted(frames_dir.glob("frame_*.png"))
    
    if not rendered_frames:
        print("Error: No rendered frames found.")
        sys.exit(1)
    
    # ----- Create GIF from Frames -----
    print("Creating GIF from frames...")
    frames_list = []
    for frame_file in rendered_frames:
        frame = imageio.imread(frame_file)
        frames_list.append(frame)
    
    # Use imageio to save as GIF
    try:
        imageio.mimsave(
            output_gif_win,
            frames_list,
            fps=FRAME_RATE,
            loop=0,
            palettesize=256
        )
        print(f"GIF saved successfully at {OUTPUT_GIF_WSL}")
    except Exception as e:
        print(f"Error saving GIF with imageio: {e}")
        sys.exit(1)

if __name__ == "__main__":
    main()  Blender script already exists at /mnt/c/Users/Jeff/logo/render/blender_script.py
Starting Blender to render animation...
Blender 4.2.3 LTS (hash 0e22e4fcea03 built 2024-10-15 01:37:33)
Reloading external rigs...
Reloading external metarigs...
OSError: Python file "Blender script already exists at /mnt/c/Users/Jeff/logo/render/blender_script.py
Starting Blender to render animation...
Blender 4.2.3 LTS (hash 0e22e4fcea03 built 2024-10-15 01:37:33)
Reloading external rigs...
Reloading external metarigs...
OSError: Python file "C:\Users\Jeff\logo\mnt\c\Users\Jeff\logo\render\blender_script.py" could not be opened: No such fi
le or directory

Blender quit
Blender rendering completed successfully.
Collecting rendered frames...
Error: No rendered frames found. could not be opened: No such fi
le or directory

Blender quit
Blender rendering completed successfully.
Collecting rendered frames...
Error: No rendered frames found.  isnt this wrong? Blender script already exists at /mnt/c/Users/Jeff/logo/render/blender_script.py
Starting Blender to render animation...
Blender 4.2.3 LTS (hash 0e22e4fcea03 built 2024-10-15 01:37:33)
Reloading external rigs...
Reloading external metarigs...
OSError: Python file "C:\Users\Jeff\logo\mnt\c\Users\Jeff\logo\render\blender_script.py" could not be opened: No such fi
le or directory

Blender quit
Blender rendering completed successfully.
Collecting rendered frames...
Error: No rendered frames found.   isnt this wrong? Blender script already exists at /mnt/c/Users/Jeff/logo/render/blender_script.py
Starting Blender to render animation...
Blender 4.2.3 LTS (hash 0e22e4fcea03 built 2024-10-15 01:37:33)
Reloading external rigs...
Reloading external metarigs...
OSError: Python file "C:\Users\Jeff\logo\mnt\c\Users\Jeff\logo\render\blender_script.py" could not be opened: No such fi
le or directory

Blender quit
Blender rendering completed successfully.Blender script already exists at /mnt/c/Users/Jeff/logo/render/blender_script.py
Starting Blender to render animation...
Blender 4.2.3 LTS (hash 0e22e4fcea03 built 2024-10-15 01:37:33)
Reloading external rigs...
Reloading external metarigs...
OSError: Python file "C:\Users\Jeff\logo\mnt\c\Users\Jeff\logo\render\blender_script.py" could not be opened: No such fi
le or directory

Blender quit
Blender rendering completed successfully. 
Collecting rendered frames...
Error: No rendered frames found. Blender script already exists at /mnt/c/Users/Jeff/logo/render/blender_script.py
Starting Blender to render animation...
Blender 4.2.3 LTS (hash 0e22e4fcea03 built 2024-10-15 01:37:33)
Reloading external rigs...
Reloading external metarigs...
OSError: Python file "C:\Users\Jeff\logo\mnt\c\Users\Jeff\logo\render\blender_script.py" could not be opened: No such fi
le or directory

Blender quit
Blender rendering completed successfully.
Collecting rendered frames...
Error: No rendered frames found.
Collecting rendered frames...
Error: No rendered frames found.