import numpy as np
import os
import time
from PIL import Image
from moviepy.editor import VideoClip

# Paths
image_one_path = '/home/daddy/morph/one.jpeg'
image_two_path = '/home/daddy/morph/two.png'
output_path = '/home/daddy/morph/output'
output_filename = f'morph{int(time.time())}.mp4'

# Function to create each frame of the video
def make_frame(t):
    if t < duration1:
        # Display the first image
        frame_array = img1_array.copy()
    elif t < duration1 + duration2:
        # Morphing effect with particle explosion and implosion
        progress = (t - duration1) / duration2  # Normalize progress to [0, 1]

        # Create an empty frame with transparency
        frame_array = np.zeros_like(img1_array)
        frame_array[:, :, 3] = 0  # Set alpha channel to 0 (fully transparent)

        for idx, (x, y) in enumerate(particle_positions):
            # Extract the patch from the first image
            patch = img1_array[y:y+particle_size, x:x+particle_size].copy()

            # Explosion offsets
            offset_x, offset_y = explosion_offsets[idx]

            if progress < 0.5:
                # Explosion phase
                explosion_progress = progress * 2  # Scale progress to [0, 1]
                current_x = x + explosion_progress * offset_x
                current_y = y + explosion_progress * offset_y
            else:
                # Implosion phase
                implosion_progress = (progress - 0.5) * 2  # Scale progress to [0, 1]

                # Target positions in the second image
                target_x, target_y = particle_to_target_position.get((x, y), (x, y))

                # Current position moves from exploded position to target position
                current_x = (x + offset_x) + implosion_progress * (target_x - (x + offset_x))
                current_y = (y + offset_y) + implosion_progress * (target_y - (y + offset_y))

            # Convert positions to integers and ensure they stay within frame boundaries
            current_x = int(np.clip(current_x, 0, width - particle_size))
            current_y = int(np.clip(current_y, 0, height - particle_size))

            # Place the current patch onto the frame
            frame_array[current_y:current_y+particle_size, current_x:current_x+particle_size] = patch

    else:
        # Display the second image
        frame_array = img2_array.copy()

    # Convert the NumPy array back to an image
    frame_image = Image.fromarray(frame_array, 'RGBA')
    # Return frame as an RGB image (MoviePy expects RGB format)
    return np.array(frame_image.convert('RGB'))

# Main program
if name == "main":
    # Input parameters with default values
    duration1 = int(input("Enter duration for first image display (seconds, default 5): ") or 5)
    duration2 = int(input("Enter duration for morphing effect (seconds, default 5): ") or 5)
    duration3 = int(input("Enter duration for second image display (seconds, default 5): ") or 5)
    particle_size = int(input("Enter particle size (pixels, default 4): ") or 4)

    # Load images and ensure they are in RGBA format
    img1 = Image.open(image_one_path).convert('RGBA').resize((1024, 1024))
    img2 = Image.open(image_two_path).convert('RGBA').resize((1024, 1024))

    # Convert images to NumPy arrays
    img1_array = np.array(img1)
    img2_array = np.array(img2)

    # Get image dimensions
    width, height = img1.size

    # Calculate the number of particles in the x and y directions
    particles_in_row = width // particle_size
    particles_in_col = height // particle_size

    # Prepare lists to store particle positions and explosion offsets
    particle_positions = []
    explosion_offsets = []

    # Generate particle positions and explosion offsets
    for i in range(particles_in_col):
        for j in range(particles_in_row):
            x = j * particle_size
            y = i * particle_size
            particle_positions.append((x, y))

            # Randomize explosion offsets for more dynamic effect
            offset_x = np.random.uniform(-width, width)
            offset_y = np.random.uniform(-height, height)
            explosion_offsets.append((offset_x, offset_y))

    # Detect target pixels in the second image (e.g., black pixels)
    # We'll use pixels where the sum of RGB values is less than a threshold (i.e., dark pixels)
    target_threshold = int(input("Enter threshold for target pixel detection (0-765, default 100): ") or 100)

    # Sum the RGB channels
    img2_rgb_sum = img2_array[:, :, :3].sum(axis=2)

    # Find positions where the pixel is darker than the threshold
    target_pixels = np.where(img2_rgb_sum < target_threshold)

    # Zip the coordinates
    target_pixel_positions = list(zip(target_pixels[1], target_pixels[0]))  # (x, y)

    # Handle cases where there are more particles than target pixels or vice versa
    num_particles = len(particle_positions)
    num_targets = len(target_pixel_positions)

    # Map particles to target pixels
    particle_to_target_position = {}

    if num_particles <= num_targets:
        # Assign each particle to a target pixel
        selected_targets = np.random.choice(num_targets, num_particles, replace=False)
        for idx, particle_pos in enumerate(particle_positions):
            target_idx = selected_targets[idx]
            target_pos = target_pixel_positions[target_idx]
            particle_to_target_position[particle_pos] = target_pos
    else:
        # There are more particles than target pixels
        # Some particles will fade out; map particles to targets randomly
        selected_targets = np.random.choice(num_targets, num_particles)
        for idx, particle_pos in enumerate(particle_positions):
            target_idx = selected_targets[idx]
            target_pos = target_pixel_positions[target_idx]
            particle_to_target_position[particle_pos] = target_pos

    total_duration = duration1 + duration2 + duration3

    # Create and write the video
    animation = VideoClip(make_frame, duration=total_duration)
    animation.write_videofile(os.path.join( what is missing