#!/usr/bin/python3

from datetime import datetime, timedelta
from threading import Timer
from pydub import AudioSegment
from time import sleep
from timeloop import Timeloop
import vlc


class RepeatedTimer(object):
    def __init__(self, interval, function, *args, **kwargs):
        self._timer = None
        self.interval = interval
        self.function = function
        self.args = args
        self.kwargs = kwargs
        self.is_running = False
        self.start()

    def _run(self):
        self.is_running = False
        self.start()
        self.function(*self.args, **self.kwargs)

    def start(self):
        if not self.is_running:
            self._timer = Timer(self.interval, self._run)
            self._timer.start()
            self.is_running = True

    def stop(self):
        self._timer.cancel()
        self.is_running = False


tl = Timeloop()


def leading_silence(audio_segment, silence_threshold=-50.0, chunk_size=10):
    """
    Returns the millisecond/index that the leading silence ends.
    audio_segment - the segment to find silence in
    silence_threshold - the upper bound for how quiet is silent in dFBS
    chunk_size - chunk size for interating over the segment in ms

    https://github.com/jiaaro/pydub/blob/master/pydub/silence.py
    """

    trim_ms = 0  # ms
    assert chunk_size > 0  # to avoid infinite loop
    while (
        audio_segment[trim_ms:trim_ms + chunk_size].dBFS < silence_threshold
            and trim_ms < len(audio_segment)):
        trim_ms += chunk_size

    # if there is no end it should return the length of the segment
    return min(trim_ms, len(audio_segment))


def significant_fade(audio_segment, fade_threshold=-20.0, chunk_size=10):
    """
    Returns the millisecond/index of the point where the fade is down to
    fade_threshold and doesn't get louder again.
    audio_segment - the segment to find silence in
    fade_threshold - the upper bound for how quiet is silent in dFBS
    chunk_size - chunk size for interating over the segment in ms
    """

    assert chunk_size > 0  # to avoid infinite loop

    segment_length = audio_segment.duration_seconds * 1000  # ms
    trim_ms = segment_length - chunk_size
    while (
        audio_segment[trim_ms:trim_ms + chunk_size].dBFS < fade_threshold
            and trim_ms > 0):
        trim_ms -= chunk_size

    # if there is no trailing silence, return lenght of track (it's less
    # the chunk_size, but for chunk_size = 10ms, this may be ignored)
    return trim_ms


def trailing_silence(audio_segment, silence_threshold=-50.0, chunk_size=10):
    """
    Returns the millisecond/index that the trailing silence starts.
    audio_segment - the segment to find silence in
    silence_threshold - the upper bound for how quiet is silent in dFBS
    chunk_size - chunk size for interating over the segment in ms
    """

    assert chunk_size > 0  # to avoid infinite loop

    segment_length = audio_segment.duration_seconds * 1000  # ms
    trim_ms = segment_length - chunk_size
    while (
        audio_segment[trim_ms:trim_ms + chunk_size].dBFS < silence_threshold
            and trim_ms > 0):
        trim_ms -= chunk_size

    # if there is no trailing silence, return lenght of track (it's less
    # the chunk_size, but for chunk_size = 10ms, this may be ignored)
    return trim_ms


def ms_to_mmss(ms, decimals=0):
    if not ms:
        return "-"
    if ms < 0:
        sign = "-"
    else:
        sign = ""

    minutes, remainder = divmod(ms, 60 * 1000)
    seconds = remainder / 1000

    return f"{sign}{minutes:.0f}:{seconds:02.{decimals}f}"


# @tl.job(interval=timedelta(seconds=1))
def update_progress(player, talk_at, silent_at):
    elapsed_time = player.get_time()
    total_time = player.get_length()
    remaining_time = total_time - elapsed_time
    talk_time = remaining_time - (total_time - talk_at)
    silent_time = remaining_time - (total_time - silent_at)
    end_time = (datetime.now() + timedelta(
        milliseconds=remaining_time)).strftime("%H:%M:%S")
    print(
        f"\t{ms_to_mmss(elapsed_time)}/"
        f"{ms_to_mmss(total_time)}\t\t"
        f"Talk in: {ms_to_mmss(talk_time)} "
        f"Silent in: {ms_to_mmss(silent_time)} "
        f"Ends at: {end_time} [{ms_to_mmss(remaining_time)}]"
        , end="\r")


# Print name of current song, print name of next song. Play current when
# return pressed, Pri--current-song-output-lengthnt remaining time every
# second. When it ends, print name of new current and next song.


def test():
    track = "wibg.mp3"
    segment = AudioSegment.from_mp3(track)
    print(f"Track: {track}")
    print(f"Leading silence: {ms_to_mmss(leading_silence(segment), decimals=1)}")
    talk_at = significant_fade(segment)
    silent_at = trailing_silence(segment)
    print(f"Talkover fade: {ms_to_mmss(talk_at)}")
    print(f"Track silent from: {ms_to_mmss(silent_at)}")
    p = vlc.MediaPlayer("wibg.mp3")
    _ = input("")
    p.play()
    print()
    rt = RepeatedTimer(0.5, update_progress, p, talk_at, silent_at)
    sleep(1)
    while p.is_playing():
        sleep(1)
    rt.stop()  # better in a try/finally block to make sure the program ends!
    print("End")


test()
# next_song = get_next_song
# 
# def play_track():
#     r = run_aud_cmd("--current-song-length
# 
# 
# 
# def play():
#     play_track()
#     songtimer_start()
# 
# 
# print("Start playing in 3 seconds")
# 
# sleep(3)
# 
# play()


# print("starting...")
# This auto-starts, no need of rt.start()
# rt = RepeatedTimer(0.3, hello, "World")
#
# count = 5
# while count > 0:
#     print("main job here")
#     sleep(2)
# rt.stop()  # better in a try/finally block to make sure the program ends!


# #!/usr/bin/python3
#
# import time
# from timeloop import Timeloop
# from datetime import timedelta
#
# tl = Timeloop()
#
# @tl.job(interval=timedelta(seconds=2))
# def sample_job_every_2s():
#     print(f"2s job current time: {time.ctime()}")
#
# @tl.job(interval=timedelta(seconds=5))
# def sample_job_every_5s():
#     print(f"5s job current time: {time.ctime()}")
#
# @tl.job(interval=timedelta(seconds=10))
# def sample_job_every_10s():
#     print(f"10s job current time: {time.ctime()}")
#
# @tl.job(interval=timedelta(seconds=0.2))
# def sample_job_every_10s():
#     print(f"200ms job current time: {time.ctime()}")
#
# if __name__ == "__main__":
#     tl.start(block=True)
#
