import os
import numpy as np
from PIL import Image, ImageDraw, ImageFont
import matplotlib.pyplot as plt
import matplotlib.animation as animation
import logging
import argparse
import random

# Configure logging
logging.basicConfig(
    filename='process.log',
    level=logging.DEBUG,
    format='%(asctime)s - %(levelname)s - %(message)s'
)

def log_and_print(message, level='info'):
    """Log and print messages."""
    if level == 'info':
        logging.info(message)
    elif level == 'debug':
        logging.debug(message)
    elif level == 'error':
        logging.error(message)
    print(message)

def resize_image(image_path, new_size=(1024, 1024)):
    """Resize the image to a specified size."""
    try:
        img = Image.open(image_path)
        # Handle PIL versions for resampling
        try:
            resample_method = Image.Resampling.LANCZOS
        except AttributeError:
            resample_method = Image.LANCZOS  # For older PIL versions
        img = img.resize(new_size, resample=resample_method)
        resized_dir = 'resized_images'
        os.makedirs(resized_dir, exist_ok=True)
        base_name = os.path.splitext(os.path.basename(image_path))[0]
        resized_path = os.path.join(resized_dir, f"{base_name}_{new_size[0]}x{new_size[1]}.png")
        img.save(resized_path)
        log_and_print(f"Image resized and saved to {resized_path}")
        return resized_path
    except Exception as e:
        log_and_print(f"Error resizing image: {e}", 'error')
        return None

def read_text_from_file(file_path, image_size):
    """Read text message from a file and render it as an image."""
    try:
        with open(file_path, 'r') as file:
            text_message = file.read().strip()
        log_and_print(f"Text message read from {file_path}: {text_message}")

        # Create a blank image with white background
        img_text = Image.new('RGB', image_size, color=(255, 255, 255))
        draw = ImageDraw.Draw(img_text)

        # Choose a font size based on image size
        font_size = min(image_size) // 15
        try:
            font = ImageFont.truetype("arial.ttf", font_size)  # Adjust to available font path
        except IOError:
            font = ImageFont.load_default()
            log_and_print("Default font loaded. Truetype font not found.", 'error')

        # Get text size using textbbox method
        bbox = draw.textbbox((0, 0), text_message, font=font)
        text_width = bbox[2] - bbox[0]
        text_height = bbox[3] - bbox[1]

        # Calculate position to center the text
        position = ((image_size[0]-text_width)//2, (image_size[1]-text_height)//2)

        # Draw the text
        draw.text(position, text_message, fill=(0, 0, 0), font=font)

        log_and_print("Text image created successfully.")
        return np.array(img_text)
    except Exception as e:
        log_and_print(f"Error reading or rendering text: {e}", 'error')
        return None

def create_particles(img, step=1):
    """Create particles from an image."""
    height, width = img.shape[:2]
    particles = []
    for y in range(0, height, step):
        for x in range(0, width, step):
            color = img[y, x]
            particles.append({'start_pos': (x, y), 'color': color})
    return particles

def get_text_positions(text_img, step=1):
    """Get positions of text pixels in the text image."""
    positions = []
    height, width = text_img.shape[:2]
    for y in range(0, height, step):
        for x in range(0, width, step):
            if np.all(text_img[y, x] < 128):  # Assuming text is darker than background
                positions.append((x, y))
    return positions

def assign_target_positions(particles, target_positions):
    """Assign target positions to particles."""
    num_particles = len(particles)
    num_targets = len(target_positions)

    if num_particles > num_targets:
        # Repeat target positions to match the number of particles
        repeats = (num_particles // num_targets) + 1
        target_positions_extended = target_positions * repeats
        target_positions_assigned = target_positions_extended[:num_particles]
    else:
        # Randomly select target positions without replacement
        target_positions_assigned = random.sample(target_positions, num_particles)

    # Shuffle the target positions to add randomness to the movement
    random.shuffle(target_positions_assigned)

    for i, particle in enumerate(particles):
        particle['end_pos'] = target_positions_assigned[i]
    return particles

def animate_particles(particles, hold_duration=6, morph_duration=4, fps=30):
    """Animate particles moving from start positions to end positions."""
    try:
        fig, ax = plt.subplots()
        plt.axis('off')
        ax.set_aspect('equal')
        max_x = max(p['start_pos'][0] for p in particles + [{'start_pos': (0, 0)}]) + 10
        max_y = max(p['start_pos'][1] for p in particles + [{'start_pos': (0, 0)}]) + 10
        ax.set_xlim(0, max_x)
        ax.set_ylim(0, max_y)
        ax.invert_yaxis()  # Invert y-axis to match image coordinate system

        # Number of frames
        morph_frames = int(morph_duration * fps)
        hold_frames = int(hold_duration * fps)
        total_frames = morph_frames + hold_frames

        # Initialize scatter plot
        scatter = ax.scatter(
            [p['start_pos'][0] for p in particles],
            [p['start_pos'][1] for p in particles],
            c=[p['color']/255.0 for p in particles],
            s=2,  # Smaller particle size
            linewidths=0,
            marker='o'
        )

        # Precompute particle paths with easing function (optional)
        for p in particles:
            p['x_path'] = np.linspace(p['start_pos'][0], p['end_pos'][0], morph_frames)
            p['y_path'] = np.linspace(p['start_pos'][1], p['end_pos'][1], morph_frames)

        def update(frame_num):
            if frame_num < morph_frames:
                x = [p['x_path'][frame_num] for p in particles]
                y = [p['y_path'][frame_num] for p in particles]
            else:
                x = [p['end_pos'][0] for p in particles]
                y = [p['end_pos'][1] for p in particles]
            scatter.set_offsets(np.column_stack((x, y)))
            return scatter,

        anim = animation.FuncAnimation(
            fig,
            update,
            frames=total_frames,
            interval=1000 / fps,
            blit=True,
            repeat=False  # Do not repeat the animation
        )
        log_and_print("Animation created successfully.")
        return anim
    except Exception as e:
        log_and_print(f"Error creating animation: {e}", 'error')
        return None

def save_animation(anim, gif_path, mp4_path, fps):
    """Save the animation in both GIF and MP4 formats."""
    try:
        anim.save(gif_path, writer='pillow', fps=fps)
        log_and_print(f"GIF saved successfully to {gif_path}")
    except Exception as e:
        log_and_print(f"Error saving GIF: {e}", 'error')

    try:
        anim.save(mp4_path, writer='ffmpeg', fps=fps)
        log_and_print(f"MP4 saved successfully to {mp4_path}")
    except Exception as e:
        log_and_print(f"Error saving MP4: {e}", 'error')

def main():
    # Argument parsing for flexibility
    parser = argparse.ArgumentParser(description='Image to Text Morphing Animation Script.')
    parser.add_argument('image_path', type=str, help='Path to the input image.')
    parser.add_argument('--text_file', type=str, default='text_message.txt',
                        help='Path to the text file containing the desired message.')
    parser.add_argument('--hold_duration', type=int, default=6,
                        help='Seconds to hold the transformed text.')
    parser.add_argument('--morph_duration', type=int, default=4,
                        help='Seconds to perform the morphing animation.')
    parser.add_argument('--fps', type=int, default=30,
                        help='Frames per second for the animation.')
    args = parser.parse_args()

    image_path = args.image_path
    text_file_path = args.text_file
    hold_duration = args.hold_duration
    morph_duration = args.morph_duration
    fps = args.fps

    # Ensure the image exists
    if not os.path.isfile(image_path):
        log_and_print(f"Image file does not exist: {image_path}", 'error')
        return

    # Resize the image to a manageable size
    resized_image_path = resize_image(image_path)
    if not resized_image_path:
        log_and_print("Failed to resize image.", 'error')
        return

    # Read and process text
    image_size = (1024, 1024)  # Output image size
    text_image = read_text_from_file(text_file_path, image_size)
    if text_image is None:
        log_and_print("Failed to read or render text image.", 'error')
        return

    # Create particles from the resized image
    img = np.array(Image.open(resized_image_path))
    particles = create_particles(img, step=2)  # Step size for particle creation
    text_positions = get_text_positions(text_image, step=2)
    particles = assign_target_positions(particles, text_positions)

    # Animate particles
    anim = animate_particles(particles, hold_duration=hold_duration, morph_duration=morph_duration, fps=fps)
    if anim is None:
        log_and_print("Failed to create animation.", 'error')
        return

    # Save the animation in GIF and MP4 formats
    output_dir = 'output'
    os.makedirs(output_dir, exist_ok=True)
    gif_path = os.path.join(output_dir, 'transition_animation.gif')
    mp4_path = os.path.join(output_dir, 'transition_animation.mp4')

    # Save to the conda environment path
    save_animation(anim, gif_path, mp4_path, fps)

    # Save to /sdcard/Download path
    sdcard_path = '/sdcard/Download'
    os.makedirs(sdcard_path, exist_ok=True)
    sdcard_gif_path = os.path.join(sdcard_path, 'transition_animation.gif')
    sdcard_mp4_path = os.path.join(sdcard_path, 'transition_animation.mp4')

    # Copy to /sdcard/Download
    try:
        from shutil import copyfile
        copyfile(gif_path, sdcard_gif_path)
        copyfile(mp4_path, sdcard_mp4_path)
        log_and_print(f"Files saved to {sdcard_path}")
    except Exception as e:
        log_and_print(f"Error saving files to {sdcard_path}: {e}", 'error')

if __name__ == '__main__':
    main()
This worked but it didn't just make the particles small it made the gif very small, could barely see it, I need it saved as a 1024x1024.