#!/usr/bin/env python3

import os
import shutil
import cv2
import numpy as np
from moviepy.editor import ImageSequenceClip
from datetime import datetime

class ParallaxLayer:
    """Class to represent a single parallax layer."""
    def __init__(self, image, factor):
        self.surface = image
        self.factor = factor
        self.scroll = 0

class ParallaxEffect:
    """Class to manage the parallax effect with multiple layers."""
    def __init__(self, size, orientation='horizontal'):
        self.size = size
        self.orientation = orientation
        self.levels = []
        self.scroller = 0

    def add_layer(self, image, factor):
        """Add a layer to the parallax effect."""
        layer = ParallaxLayer(image, factor)
        self.levels.append(layer)

    def draw(self, surface):
        """Draw all parallax levels onto the surface."""
        s_width, s_height = self.size
        for lvl in self.levels:
            if self.orientation == 'vertical':
                surface.blit(lvl.surface, (0, 0),
                             (0, -lvl.scroll, s_width, s_height))
                surface.blit(lvl.surface,
                             (0, lvl.scroll - lvl.surface.shape[0]))
            else:
                surface.blit(lvl.surface, (0, 0),
                             (lvl.scroll, 0, s_width, s_height))
                surface.blit(lvl.surface,
                             (lvl.surface.shape[1] - lvl.scroll, 0),
                             (0, 0, lvl.scroll, s_height))

    def scroll(self, offset, orientation=None):
        """Scroll the parallax layers by a certain offset."""
        if orientation is not None:
            self.orientation = orientation

        self.scroller = (self.scroller + offset)
        for lvl in self.levels:
            if self.orientation == 'vertical':
                lvl.scroll = (self.scroller / lvl.factor) % lvl.surface.shape[0]
            else:
                lvl.scroll = (self.scroller / lvl.factor) % lvl.surface.shape[1]

def clear_processing_folder(folder_path):
    """Clears the processing folder."""
    if os.path.exists(folder_path):
        shutil.rmtree(folder_path)
    os.makedirs(folder_path)

def create_parallax_effect(image_path, output_path, duration=10, fps=30):
    """
    Creates an animated parallax effect from a single image.

    :param image_path: Path to the input image.
    :param output_path: Path to save the output video.
    :param duration: Duration of the output video in seconds.
    :param fps: Frames per second for the output video.
    """

    # Load the image with alpha channel if present
    image = cv2.imread(image_path, cv2.IMREAD_UNCHANGED)

    if image is None:
        raise FileNotFoundError(f"Image not found at path: {image_path}")

    height, width = image.shape[:2]

    # Create layers with different factors
    foreground = cv2.resize(image, None, fx=1.1, fy=1.1, interpolation=cv2.INTER_LINEAR)
    background = cv2.resize(image, None, fx=1.0, fy=1.0, interpolation=cv2.INTER_LINEAR)

    # Initialize the ParallaxEffect object
    parallax = ParallaxEffect(size=(width, height), orientation='horizontal')
    parallax.add_layer(background, factor=1.0)
    parallax.add_layer(foreground, factor=0.5)

    # Calculate the number of frames
    num_frames = int(duration * fps)

    # Initialize a list to store frames
    frames = []

    for i in range(num_frames):
        # Calculate scroll offset
        offset = i / num_frames * width * 0.1

        # Scroll the parallax layers
        parallax.scroll(offset)

        # Create empty canvas for each frame
        frame = np.zeros((height, width, 3), dtype=np.uint8)

        # Draw the parallax effect
        parallax.draw(frame)

        # Convert BGR to RGB for MoviePy
        frame_rgb = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
        frames.append(frame_rgb)

    # Create the video clip
    clip = ImageSequenceClip(frames, fps=fps)
    clip.write_videofile(output_path, codec='libx264')

def main():
    # Paths
    input_image = "/root/parallax/new.jpeg"
    output_folder = "/root/parallax/image"
    processing_folder = "/root/parallax/processing"
    shared_folder = "/sdcard/Shared"  # If you need to copy the output here

    # Generate timestamp
    timestamp = datetime.now().strftime("%Y%m%d%H%M%S")

    # Prepare filenames
    input_filename = f"new_{timestamp}.jpeg"
    output_filename = f"new_{timestamp}.mp4"

    # Ensure output and processing folders exist
    os.makedirs(output_folder, exist_ok=True)
    clear_processing_folder(processing_folder)

    # Copy and rename the input image to the processing folder
    processing_image_path = os.path.join(processing_folder, input_filename)
    shutil.copy2(input_image, processing_image_path)

    # Prepare the output video path
    output_video_path = os.path.join(output_folder, output_filename)

    # Create the parallax effect
    create_parallax_effect(processing_image_path, output_video_path)

    # Optionally copy the output to the shared folder
    if os.path.isdir(shared_folder):
        shutil.copy2(output_video_path, shared_folder)

    # Clear the processing folder after completion
    clear_processing_folder(processing_folder)

    print(f"Parallax effect video created successfully at {output_video_path}")

if __name__ == "__main__":
    main()