import sys

sys.path.append("..")
import os
import shutil

now_dir = os.getcwd()
import soundfile as sf
import librosa
from lib.tools import audioEffects
from assets.i18n.i18n import I18nAuto

i18n = I18nAuto()
import gradio as gr
import tabs.resources as resources
import numpy as np
from scipy.signal import resample

def save_to_wav2(dropbox):
    file_path = dropbox.name
    target_path = os.path.join("assets","audios", os.path.basename(file_path))

    if os.path.exists(target_path):
        os.remove(target_path)
        print("Replacing old dropdown file...")

    shutil.move(file_path, target_path)
    return target_path


audio_root = "assets/audios"
audio_others_root = "assets/audios/audio-others"
sup_audioext = {
    "wav",
    "mp3",
    "flac",
    "ogg",
    "opus",
    "m4a",
    "mp4",
    "aac",
    "alac",
    "wma",
    "aiff",
    "webm",
    "ac3",
}
audio_paths = [
    os.path.join(root, name)
    for root, _, files in os.walk(audio_root, topdown=False)
    for name in files
    if name.endswith(tuple(sup_audioext)) and root == audio_root
]

audio_others_paths = [
    os.path.join(root, name)
    for root, _, files in os.walk(audio_others_root, topdown=False)
    for name in files
    if name.endswith(tuple(sup_audioext)) and root == audio_others_root
]


def change_choices3():
    audio_paths = [
    os.path.join(root, name)
    for root, _, files in os.walk(audio_root, topdown=False)
    for name in files
    if name.endswith(tuple(sup_audioext)) and root == audio_root
    ]
    audio_others_paths = [
    os.path.join(root, name)
    for root, _, files in os.walk(audio_others_root, topdown=False)
    for name in files
    if name.endswith(tuple(sup_audioext)) and root == audio_others_root
    ]

    return (
        {"choices": sorted(audio_others_paths), "__type__": "update"},
        {"choices": sorted(audio_paths), "__type__": "update"},
    )


def generate_output_path(output_folder, base_name, extension):
    index = 1
    while True:
        output_path = os.path.join(output_folder, f"{base_name}_{index}.{extension}")
        if not os.path.exists(output_path):
            return output_path
        index += 1

from pydub import AudioSegment
from pydub.silence import detect_nonsilent
import glob
import re
def combine_and_save_audios(
    audio1_path, audio2_path, output_path, volume_factor_audio1, volume_factor_audio2
):
    audio1 = AudioSegment.from_file(audio1_path)
    audio2 = AudioSegment.from_file(audio2_path)
    
    # Verificar cuál audio tiene mayor longitud
    if len(audio1) > len(audio2):
        # Calcular la diferencia en duración en segundos
        diff_duration_seconds = (len(audio1) - len(audio2)) / 1000.0  # Convertir a segundos
        print(f"diff_duration_seconds: {diff_duration_seconds} seconds")
        # Crear el segmento de silencio en Pydub
        silence = AudioSegment.silent(duration=int(diff_duration_seconds))  # Convertir a milisegundos

        # Agregar el silencio al audio2 para igualar la duración
        audio2 = audio2 + silence
    else:
        # Calcular la diferencia en duración en segundos
        diff_duration_seconds = (len(audio2) - len(audio1)) / 1000.0  # Convertir a segundos
        print(f"diff_duration_seconds: {diff_duration_seconds} seconds")
        # Crear el segmento de silencio en Pydub
        silence = AudioSegment.silent(duration=int(diff_duration_seconds))  # Convertir a milisegundos

        # Agregar el silencio al audio1 para igualar la duración
        audio1 = audio1 + silence

    # Ajustar el volumen de los audios multiplicando por el factor de ganancia
    if volume_factor_audio1 != 1.0:
        audio1 *= volume_factor_audio1
    if volume_factor_audio2 != 1.0:
        audio2 *= volume_factor_audio2

    # Combinar los audios
    combined_audio = audio1.overlay(audio2)

    # Guardar el audio combinado en el archivo de salida
    combined_audio.export(output_path, format="wav")


def audio_combined(
    audio1_path,
    audio2_path,
    volume_factor_audio1=1.0,
    volume_factor_audio2=1.0,
    reverb_enabled=False,
    compressor_enabled=False,
    noise_gate_enabled=False,
):
    output_folder = os.path.join(now_dir,"assets", "audios", "audio-outputs")
    os.makedirs(output_folder, exist_ok=True)

    # Generar nombres únicos para los archivos de salida
    base_name = "combined_audio"
    extension = "wav"
    output_path = generate_output_path(output_folder, base_name, extension)
    print(reverb_enabled)
    print(compressor_enabled)
    print(noise_gate_enabled)

    if reverb_enabled or compressor_enabled or noise_gate_enabled:
        # Procesa el primer audio con los efectos habilitados
        base_name = "effect_audio"
        output_path = generate_output_path(output_folder, base_name, extension)
        processed_audio_path = audioEffects.process_audio(
            audio2_path,
            output_path,
            reverb_enabled,
            compressor_enabled,
            noise_gate_enabled,
        )
        base_name = "combined_audio"
        output_path = generate_output_path(output_folder, base_name, extension)
        # Combina el audio procesado con el segundo audio usando audio_combined
        combine_and_save_audios(
            audio1_path,
            processed_audio_path,
            output_path,
            volume_factor_audio1,
            volume_factor_audio2,
        )

        return i18n("Conversion complete!"), output_path
    else:
        base_name = "combined_audio"
        output_path = generate_output_path(output_folder, base_name, extension)
        # No hay efectos habilitados, combina directamente los audios sin procesar
        combine_and_save_audios(
            audio1_path,
            audio2_path,
            output_path,
            volume_factor_audio1,
            volume_factor_audio2,
        )

        return i18n("Conversion complete!"), output_path

def process_audio(file_path):
    try:
        # load audio file
        song = AudioSegment.from_file(file_path)

        print(f"Ignore the warning if you saw any...")

        # set silence threshold and duration
        silence_thresh = -70  # dB
        min_silence_len = 750  # ms, adjust as needed

        # detect nonsilent parts
        nonsilent_parts = detect_nonsilent(song, min_silence_len=min_silence_len, silence_thresh=silence_thresh)

        # Create a new directory to store chunks
        file_dir = os.path.dirname(file_path)
        file_name = os.path.basename(file_path).split('.')[0]
        new_dir_path = os.path.join(file_dir, file_name)
        os.makedirs(new_dir_path, exist_ok=True)

        # Check if timestamps file exists, if so delete it
        timestamps_file = os.path.join(file_dir, f"{file_name}_timestamps.txt")
        if os.path.isfile(timestamps_file):
            os.remove(timestamps_file)

        # export chunks and save start times
        segment_count = 0
        for i, (start_i, end_i) in enumerate(nonsilent_parts):
            chunk = song[start_i:end_i]
            chunk_file_path = os.path.join(new_dir_path, f"chunk{i}.wav")
            chunk.export(chunk_file_path, format="wav")

            print(f"Segment {i} created!")
            segment_count += 1
            
            # write start times to file
            with open(timestamps_file, "a", encoding="utf-8") as f:
                f.write(f"{chunk_file_path} starts at {start_i} ms\n")
        
        print(f"Total segments created: {segment_count}")
        print(f"Split all chunks for {file_path} successfully!")

        return "Finish", new_dir_path

    except Exception as e:
        print(f"An error occurred: {e}")
        return "Error", None


def merge_audio(timestamps_file):
    try:
        # Extract prefix from the timestamps filename
        prefix = os.path.basename(timestamps_file).replace('_timestamps.txt', '')
        timestamps_dir = os.path.dirname(timestamps_file)
        print(timestamps_dir)
        print(prefix)

        # Open the timestamps file
        with open(timestamps_file, "r", encoding="utf-8") as f:
            lines = f.readlines()

        # Initialize empty list to hold audio segments
        audio_segments = []
        last_end_time = 0

        print(f"Processing file: {timestamps_file}")

        for line in lines:
            # Extract filename and start time from line
            match = re.search(r"(chunk\d+.wav) starts at (\d+) ms", line)
            if match:
                filename, start_time = match.groups()
                start_time = int(start_time)

                # Construct the complete path to the chunk file
                chunk_file = os.path.join(timestamps_dir, prefix, filename)

                # Add silence from last_end_time to start_time
                silence_duration = max(start_time - last_end_time, 0)
                silence = AudioSegment.silent(duration=silence_duration)
                audio_segments.append(silence)

                # Load audio file and append to list
                audio = AudioSegment.from_wav(chunk_file)
                audio_segments.append(audio)

                # Update last_end_time
                last_end_time = start_time + len(audio)

                print(f"Processed chunk: {chunk_file}")

        # Concatenate all audio_segments and export
        merged_filename = f"{prefix}_merged.wav"
        merged_audio = sum(audio_segments)
        merged_audio.export(os.path.join(timestamps_dir, "audio-outputs", merged_filename), format="wav")

        print(f"Exported merged file: {merged_filename}\n")

    except Exception as e:
        print(f"An error occurred: {e}")




def merge_audios():
        gr.Markdown(
            value="## " + i18n("Merge your generated audios with the instrumental")
        )
        with gr.Row():
            with gr.Column():
                dropbox = gr.File(label=i18n("Drag your audio here:"))
                gr.Markdown(value=i18n("### Instrumental settings:"))
                input_audio1 = gr.Dropdown(
                    label=i18n("Choose your instrumental:"),
                    choices=sorted(audio_others_paths),
                    value="",
                    interactive=True,
                )
                input_audio1_scale = gr.Slider(
                    minimum=0,
                    maximum=10,
                    label=i18n("Volume of the instrumental audio:"),
                    value=1.00,
                    interactive=True,
                )
                gr.Markdown(value=i18n("### Audio settings:"))
                input_audio3 = gr.Dropdown(
                    label=i18n("Select the generated audio"),
                    choices=sorted(audio_paths),
                    value="",
                    interactive=True,
                )
                with gr.Row():
                    input_audio3_scale = gr.Slider(
                        minimum=0,
                        maximum=10,
                        label=i18n("Volume of the generated audio:"),
                        value=1.00,
                        interactive=True,
                    )

                gr.Markdown(value=i18n("### Add the effects:"))
                reverb_ = gr.Checkbox(
                    label=i18n("Reverb"),
                    value=False,
                    interactive=True,
                )
                compressor_ = gr.Checkbox(
                    label=i18n("Compressor"),
                    value=False,
                    interactive=True,
                )
                noise_gate_ = gr.Checkbox(
                    label=i18n("Noise Gate"),
                    value=False,
                    interactive=True,
                )
                with gr.Row():
                    butnone = gr.Button(i18n("Merge"), variant="primary").style(
                        full_width=True
                    )
                    refresh_button = gr.Button(
                        i18n("Refresh"), variant="primary"
                    ).style(full_width=True)

                vc_output1 = gr.Textbox(label=i18n("Output information:"))
                vc_output2 = gr.Audio(
                    label=i18n(
                        "Export audio (click on the three dots in the lower right corner to download)"
                    ),
                    type="filepath",
                )

                dropbox.upload(
                    fn=save_to_wav2, inputs=[dropbox], outputs=[input_audio1]
                )

                refresh_button.click(
                    fn=lambda: change_choices3(),
                    inputs=[],
                    outputs=[input_audio1, input_audio3],
                )

                butnone.click(
                    fn=audio_combined,
                    inputs=[
                        input_audio1,
                        input_audio3,
                        input_audio1_scale,
                        input_audio3_scale,
                        reverb_,
                        compressor_,
                        noise_gate_,
                    ],
                    outputs=[vc_output1, vc_output2],
                )