import subprocess
import os
import sys
import tempfile
import argparse
import json
import textwrap  # Added to handle indentation in the multi-line string

def load_config(config_path):
    """Load the configuration from the JSON file."""
    with open(config_path, 'r') as f:
        config = json.load(f)
    return config

def create_blender_script(blender_executable, logo_path, output_dir, duration, fps, rotation_deg, resolution, render_engine, render_device):
    """
    Creates a Blender Python script to animate the logo.
    Returns the path to the temporary Blender script.
    """
    blender_script = f'''
import bpy
import sys
import os

# Parameters
LOGO_PATH = r"{logo_path}"
OUTPUT_DIR = r"{output_dir}"
DURATION = {duration}
FPS = {fps}
ROTATION_SPEED = {rotation_deg}
RESOLUTION = {resolution}

def clear_scene():
    """Remove all objects from the current Blender scene."""
    bpy.ops.wm.read_factory_settings(use_empty=True)

def import_logo(logopath):
    """Import the logo image as a plane with transparency."""
    # Enable the Import Images as Planes addon
    bpy.ops.wm.addon_enable(module="io_import_images_as_planes")
    
    # Import the image as a plane
    bpy.ops.import_image.to_plane(files=[{{"name": os.path.basename(logopath)}}], directory=os.path.dirname(logopath))
    
    # Get a reference to the imported plane
    logo = bpy.context.selected_objects[0]
    
    # Scale the logo to fit the scene
    logo.scale = (1, 1, 1)  # Adjust as necessary
    
    return logo

def setup_material(logo_obj):
    """Set up the material of the logo for better visual effects."""
    mat = bpy.data.materials.new(name="LogoMaterial")
    mat.use_nodes = True
    nodes = mat.node_tree.nodes
    links = mat.node_tree.links
    
    # Clear existing nodes
    nodes.clear()
    
    # Create nodes
    output_node = nodes.new(type='ShaderNodeOutputMaterial')
    principled_node = nodes.new(type='ShaderNodeBsdfPrincipled')
    
    # Emission for glow effect
    emission_node = nodes.new(type='ShaderNodeEmission')
    
    # Transparent BSDF
    transparent_node = nodes.new(type='ShaderNodeBsdfTransparent')
    
    # Mix Shader
    mix_shader = nodes.new(type='ShaderNodeMixShader')
    
    # Connect nodes
    links.new(principled_node.outputs['BSDF'], mix_shader.inputs[1])
    links.new(emission_node.outputs['Emission'], mix_shader.inputs[2])
    links.new(transparent_node.outputs['BSDF'], mix_shader.inputs['Fac'])
    links.new(mix_shader.outputs['Shader'], output_node.inputs['Surface'])
    
    # Set material properties
    mix_shader.inputs['Fac'].default_value = 0.0  # Fully Principled BSDF
    emission_node.inputs['Strength'].default_value = 0.5  # Adjust for glow strength
    
    # Assign material to the logo object
    logo_obj.data.materials.append(mat)

def setup_lighting():
    """Set up basic lighting in the scene."""
    # Add an area light
    bpy.ops.object.light_add(type='AREA', location=(5, -5, 5))
    area_light = bpy.context.active_object
    area_light.data.energy = 500
    area_light.data.size = 10

    # Add a point light for additional illumination
    bpy.ops.object.light_add(type='POINT', location=(-5, 5, 5))
    point_light = bpy.context.active_object
    point_light.data.energy = 100

def setup_camera():
    """Set up the camera to focus on the logo."""
    # Add a camera
    bpy.ops.object.camera_add(location=(0, -10, 5), rotation=(1.0470, 0, 0))
    camera = bpy.context.active_object
    bpy.context.scene.camera = camera

def animate_logo(logo_obj, duration, fps, rotation_deg):
    """Animate the logo to rotate smoothly over the specified duration."""
    total_frames = duration * fps

    # Insert initial keyframe at frame 1
    logo_obj.rotation_euler = (0, 0, 0)
    logo_obj.keyframe_insert(data_path="rotation_euler", frame=1)

    # Insert final keyframe at total_frames
    logo_obj.rotation_euler = (0, 0, rotation_deg * (3.1415926535 / 180))  # Convert degrees to radians
    logo_obj.keyframe_insert(data_path="rotation_euler", frame=total_frames)

    # Set interpolation to linear for constant rotation speed
    for fcurve in logo_obj.animation_data.action.fcurves:
        for keyframe in fcurve.keyframe_points:
            keyframe.interpolation = 'LINEAR'

def setup_render(output_dir, resolution, fps, render_engine, render_device):
    """Configure render settings for image sequence with alpha transparency."""
    scene = bpy.context.scene
    scene.render.engine = '{render_engine}'  # Options: 'CYCLES', 'BLENDER_EEVEE'
    if '{render_engine}'.upper() == 'CYCLES':
        scene.cycles.device = '{render_device}'  # Options: 'CPU', 'GPU' if available

    scene.render.resolution_x = {resolution}
    scene.render.resolution_y = {resolution}
    scene.render.film_transparent = True  # For transparent backgrounds

    # Render as PNG image sequence
    scene.render.image_settings.file_format = 'PNG'
    scene.render.image_settings.color_mode = 'RGBA'  # Ensure alpha channel is included

    scene.render.resolution_percentage = 100

    scene.frame_start = 1
    scene.frame_end = int(DURATION * FPS)

    scene.render.fps = FPS

    # Set output path and make a separate folder for frames
    frames_output_dir = os.path.join(OUTPUT_DIR, "frames")
    if not os.path.exists(frames_output_dir):
        os.makedirs(frames_output_dir)
    scene.render.filepath = os.path.join(frames_output_dir, "frame_")

def main():
    """Main function to orchestrate the animated logo creation."""
    clear_scene()
    logo = import_logo(LOGO_PATH)
    setup_material(logo)
    setup_lighting()
    setup_camera()
    animate_logo(logo, DURATION, FPS, ROTATION_SPEED)
    setup_render(OUTPUT_DIR, RESOLUTION, FPS, "{render_engine}", "{render_device}")
    bpy.ops.render.render(animation=True)

if __name__ == "__main__":
    main()
'''
    # Remove any indentation from the multi-line string
    blender_script = textwrap.dedent(blender_script)

    # Create a temporary file to store the Blender script
    temp_dir = tempfile.gettempdir()
    blender_script_path = os.path.join(temp_dir, "temp_blender_script.py")
    with open(blender_script_path, "w") as f:
        f.write(blender_script)
    
    return blender_script_path

def convert_frames_to_webm(frames_dir, output_webm_path, fps):
    """
    Converts rendered PNG frames to a WebM video with transparency using FFmpeg.
    """
    # Check if FFmpeg is installed and accessible
    result = subprocess.run(['ffmpeg', '-version'], stdout=subprocess.PIPE, stderr=subprocess.PIPE)
    if result.returncode != 0:
        print("FFmpeg is not installed or not found in PATH. Skipping WebM conversion.")
        return False

    ffmpeg_command = [
        'ffmpeg',
        '-framerate', str(fps),
        '-i', os.path.join(frames_dir, 'frame_%04d.png'),
        '-c:v', 'libvpx-vp9',
        '-pix_fmt', 'yuva420p',
        '-auto-alt-ref', '0',  # Necessary for VP9 alpha
        output_webm_path
    ]
    
    print("Converting frames to WebM...")
    try:
        subprocess.run(ffmpeg_command, check=True)
        return True
    except subprocess.CalledProcessError as e:
        print(f"FFmpeg conversion failed: {e}")
        return False

def copy_to_downloads(source_path, destination_path):
    """
    Copies the final video to the Windows Downloads folder.
    """
    # Ensure the destination directory exists
    if not os.path.exists(destination_path):
        os.makedirs(destination_path)
    
    # Use WSL's cp command
    try:
        subprocess.run(['cp', source_path, destination_path], check=True)
        print(f"Copied {source_path} to {destination_path}")
        return True
    except subprocess.CalledProcessError as e:
        print(f"Failed to copy to Downloads: {e}")
        return False

def main_automation():
    """
    Main function to automate Blender logo animation, render, convert, and copy output.
    """
    # Path to the config file (located in the same directory as the script)
    script_dir = os.path.dirname(os.path.realpath(__file__))
    config_path = os.path.join(script_dir, "3d_config.json")
    
    # Load configuration
    if not os.path.isfile(config_path):
        print(f"Configuration file not found at: {config_path}")
        sys.exit(1)
    
    with open(config_path, 'r') as f:
        config = json.load(f)
    
    # Extract configurations
    blender_executable = config.get("blender_executable", "/mnt/c/Program Files/Blender/blender.exe")
    logo_path = config.get("logo_path", "/mnt/c/Users/Jeff/logo/logo.png")
    output_dir = config.get("output_dir", "/mnt/c/Users/Jeff/logo/output")
    output_video_filename = config.get("output_video_filename", "rotating_logo.mp4")
    duration_seconds = config.get("animation", {}).get("duration_seconds", 5)
    fps = config.get("animation", {}).get("fps", 24)
    rotation_deg = config.get("animation", {}).get("rotation_degrees", 360)
    resolution = config.get("render_settings", {}).get("resolution", 1024)
    render_engine = config.get("render_settings", {}).get("render_engine", "CYCLES")
    render_device = config.get("render_settings", {}).get("render_device", "CPU")
    convert_webm = config.get("video_conversion", {}).get("convert_to_webm", True)
    webm_filename = config.get("video_conversion", {}).get("webm_filename", "rotating_logo.webm")
    copy_output_enable = config.get("copy_output", {}).get("enable_copy", True)
    copy_output_destination = config.get("copy_output", {}).get("destination_path", "/mnt/c/Users/Jeff/Downloads/")
    
    # Validate logo path
    if not os.path.isfile(logo_path):
        print(f"Logo file not found at: {logo_path}")
        sys.exit(1)
    
    # Create the Blender Python script
    blender_script_path = create_blender_script(
        blender_executable=blender_executable,
        logo_path=logo_path,
        output_dir=output_dir,
        duration=duration_seconds,
        fps=fps,
        rotation_deg=rotation_deg,
        resolution=resolution,
        render_engine=render_engine,
        render_device=render_device
    )
    
    # Run Blender with the generated script
    blender_command = [
        blender_executable,
        '--background',  # Run in background mode
        '--python', blender_script_path
    ]
    
    print("Starting Blender to render the animation...")
    try:
        subprocess.run(blender_command, check=True)
        print("Blender rendering complete.")
    except subprocess.CalledProcessError as e:
        print(f"Blender encountered an error: {e}")
        sys.exit(1)
    
    # Define the frames directory
    frames_dir = os.path.join(output_dir, "frames")
    
    # Check if frames were rendered
    if not os.path.isdir(frames_dir):
        print("No frames were rendered. Check the Blender script for errors.")
        sys.exit(1)
    
    # Convert frames to WebM if enabled
    if convert_webm:
        output_webm_path = os.path.join(output_dir, webm_filename)
        success = convert_frames_to_webm(frames_dir, output_webm_path, fps)
        if success:
            print(f"WebM video successfully created at: {output_webm_path}")
        else:
            print("WebM conversion failed.")
            sys.exit(1)
    
    # Define the path to the final output (WebM or MP4)
    final_output_path = output_webm_path if convert_webm else os.path.join(output_dir, output_video_filename)
    
    # Copy the final output to Windows Downloads if enabled
    if copy_output_enable:
        copied = copy_to_downloads(final_output_path, copy_output_destination)
        if not copied:
            print("Failed to copy the final output to Downloads.")
            sys.exit(1)
    
    # Clean up the temporary Blender script
    os.remove(blender_script_path)
    
    print("Automation process completed successfully.")

if __name__ == "__main__":
    main_automation()