import os
import subprocess
import numpy as np
import uuid
from moviepy.editor import VideoFileClip
from scipy.io import wavfile
import random

video_extensions = ['.mp4', '.mkv', '.wmv', '.avi']
output_folder = 'Output'

def calculate_loudness(audio_data):
    if audio_data.ndim == 1:
        volume = audio_data ** 2
    else:
        volume = np.mean(audio_data ** 2, axis=1)
    return np.sqrt(volume)

def find_loudest_moments(audio_filename, num_moments, segment_duration, video_duration, starting_offset, ending_offset):
    rate, audio_data = wavfile.read(audio_filename)
    if audio_data.ndim == 2:
        audio_data = np.mean(audio_data, axis=1)
    volume = calculate_loudness(audio_data.astype('float32'))
    
    start_index = int(starting_offset * rate)
    end_index = int((video_duration - ending_offset) * rate)
    moments = []
    
    while len(moments) < num_moments:
        index = np.argmax(volume[start_index:end_index])
        moment = (start_index + index) / rate
        
        # Skip moments too close to the start or end
        if moment - segment_duration / 2 < starting_offset or moment + segment_duration / 2 > video_duration - ending_offset:
            volume[start_index + index] = 0
            continue

        moments.append(moment)
        
        # Clear out the volume data around the found moment to avoid overlap
        clear_margin = int(segment_duration * rate / 2)
        clear_start = max(start_index, start_index + index - clear_margin)
        clear_end = min(end_index, start_index + index + clear_margin)
        volume[clear_start:clear_end] = 0
    
    return sorted(moments)

def ask_peak_position():
    print("Choisissez le positionnement du pic sonore dans le segment vidéo :")
    print("1 - En début de vidéo (à un tiers)")
    print("2 - Au milieu de la vidéo")
    print("3 - En fin de vidéo (aux deux tiers)")
    print("4 - Aléatoire (à un tiers, au milieu, ou aux deux tiers)")
    choice = input("Entrez le numéro de votre choix : ")
    
    while choice not in ('1', '2', '3', '4'):
        print("Entrée invalide. Veuillez choisir une option valide.")
        choice = input("Entrez le numéro de votre choix : ")
    
    return int(choice)

def extract_segments(video_path, moments, segment_duration, peak_position_choice, video_duration):
    if not os.path.exists(output_folder):
        os.makedirs(output_folder)
    base_name = os.path.splitext(os.path.basename(video_path))[0]

    for i, moment in enumerate(moments):
        if peak_position_choice == 1: # Start of video
            start_time = max(moment - segment_duration / 3, 0)
        elif peak_position_choice == 2: # Middle of video
            start_time = max(moment - segment_duration / 2, 0)
        elif peak_position_choice == 3: # End of video
            start_time = max(moment - 2 * segment_duration / 3, 0)
        elif peak_position_choice == 4: # Random
            start_time = max(moment - segment_duration * random.choice([1/3, 1/2, 2/3]), 0)
        else:
            raise ValueError("Invalid peak position choice")

        end_time = min(start_time + segment_duration, video_duration)
        output_filename = f"{base_name}_moment{i + 1}.mp4"
        output_path = os.path.join(output_folder, output_filename)

        command = [
            "ffmpeg",
            "-y",
            "-ss", str(start_time),
            "-i", video_path,
            "-t", str(min(segment_duration, video_duration - start_time)),
            "-c:v", "libx264",
            "-preset", "medium",
            "-crf", "23",
            "-c:a", "aac",
            "-strict", "-2",
            "-b:a", "192k",
            output_path
        ]

        subprocess.run(command, check=True, stderr=subprocess.PIPE)
        print(f"Extracted and re-encoded {output_filename}")

def process_video_files(starting_offset_seconds, ending_offset_seconds, num_moments, segment_duration, peak_position_choice):
    temporary_audio_files = []

    for root, _, files in os.walk('.'):
        for file in files:
            if file.lower().endswith(tuple(video_extensions)):
                video_path = os.path.join(root, file)
                unique_id = str(uuid.uuid4())
                audio_path = f'temp_audio{unique_id}.wav'
                temporary_audio_files.append(audio_path)

                try:
                    video_clip = VideoFileClip(video_path)
                    video_duration = video_clip.duration
                    video_clip.audio.write_audiofile(audio_path, codec='pcm_s16le', fps=44100, nbytes=2, buffersize=2000)
                    video_clip.close()
                    moments = find_loudest_moments(audio_path, num_moments, segment_duration, video_duration, starting_offset_seconds, ending_offset_seconds)
                    extract_segments(video_path, moments, segment_duration, peak_position_choice, video_duration)
                finally:
                    if os.path.exists(audio_path):
                        os.remove(audio_path)
                print(f"Finished processing video {video_path}")

    # Cleanup temporary audio files created during processing
    for audio_file in temporary_audio_files:
        if os.path.exists(audio_file):
            os.remove(audio_file)

if __name__ == "__main__":
    starting_offset_seconds = float(input("Combien de secondes à ignorer au début pour l'analyse ? "))
    ending_offset_seconds = float(input("Combien de secondes à ignorer à la fin pour l'analyse ? "))
    num_moments = int(input("Combien de moments forts souhaitez-vous extraire de chaque vidéo ? "))
    segment_duration = float(input("Quelle est la durée (en secondes) de chaque segment vidéo à extraire ? "))
    peak_position_choice = ask_peak_position()
    
    process_video_files(starting_offset_seconds, ending_offset_seconds, num_moments, segment_duration, peak_position_choice)

    print("All temporary audio files have been cleaned up.")
    print("All videos have been processed.")