#!/usr/bin/env python
# -*- coding: utf-8 -*-
from datetime import datetime
import os
import sys
import tempfile

import click
import pyperclip
import pysrt
from moviepy.editor import AudioFileClip
import speech_recognition as sr
import urwid
import vlc


PALETTE = [
    ('normal', 'black', 'light gray'),
    ('complete', 'black', 'dark red'),
]


class TimeSlider(urwid.ProgressBar):

    def get_text(self):
        return format_dt(self.current)


@click.command()
@click.option('--anki-media', envvar='ANKI_MEDIA')
@click.option('--srt', is_flag=True)
@click.option('--srtpath', type=click.Path(exists=True))
@click.argument('srcpath', type=click.Path(exists=True))
def main(anki_media, srt, srtpath, srcpath):

    if srtpath is None:
        srtpath = os.path.splitext(srcpath)[0] + '.srt'

    subs = init_subs(srt, srtpath)

    # Disable subtitles because they slow down the player for unknown reason
    instance = vlc.Instance('--no-sub-autodetect-file')
    main_player = instance.media_player_new(srcpath)
    chop_player = instance.media_player_new()

    audio = AudioFileClip(srcpath)
    marks = [0, 0]

    main_display = urwid.Text(srcpath)
    divider1 = urwid.Divider()
    time_slider = TimeSlider('normal', 'complete', current=0, done=audio.duration*1000)
    divider2 = urwid.Divider()
    mark_text = urwid.Text('')
    divider3 = urwid.Divider()
    name_text = urwid.Text('', align='right')
    detail_text = urwid.Text('')
    command_display = urwid.Columns([(9, name_text), detail_text], 3)

    pile = urwid.Pile([
        main_display,
        divider1,
        time_slider,
        divider2,
        mark_text,
        divider3,
        command_display
    ])

    filler = urwid.Filler(urwid.Padding(pile, width=76, left=4), 'top', top=2)

    ctx = {
        'main_player': main_player,
        'chop_player': chop_player,

        'anki_media': anki_media,
        'srcpath': srcpath,
        'audio':audio,
        'subs': subs,
        'marks': marks,

        'main_display': main_display,
        'time_slider': time_slider,
        'mark_text': mark_text,
        'name_text': name_text,
        'detail_text': detail_text,

        # can be set after input_handler
        'loop': None,
        'last_chop': '',
        'last_lr': '',
    }

    handler = input_handler(ctx)
    loop = urwid.MainLoop(filler, PALETTE, unhandled_input=handler)
    ctx['loop'] = loop
    main_player.play()

    main_player.get_instance().log_unset()
    loop.set_alarm_in(0.5, tick, ctx)
    loop.run()


def init_subs(srt, srtpath):
    if not srt:
        return None

    if not os.path.exists(srtpath):
        sys.exit("'%s' does not exist" % srtpath)

    return pysrt.open(srtpath)


def tick(loop, ctx):
    marks = ctx['marks']
    main_player = ctx['main_player']
    time_slider = ctx['time_slider']
    mark_text = ctx['mark_text']

    # Update slider
    t = max(main_player.get_time(), 0)
    time_slider.current = t
    mark_text.set_text('%s ~ %s' % tuple(format_dt(x) for x in marks))

    # To suppress errors generated by VLC
    loop.screen.clear()

    # Reschedule
    loop.set_alarm_in(0.333, tick, ctx)


def input_handler(ctx):
    main_player = ctx['main_player']
    chop_player = ctx['chop_player']

    anki_media = ctx['anki_media']
    srcpath = ctx['srcpath']

    audio = ctx['audio']
    subs = ctx['subs']
    marks = ctx['marks']

    time_slider = ctx['time_slider']
    name_text = ctx['name_text']
    detail_text = ctx['detail_text']

    def toggle():
        if main_player.is_playing():
            pause()
        else:
            if chop_player.is_playing():
                chop_player.stop()
            play()

    def play():
        r = main_player.play()
        show_command('play')

    def reload_and_play():
        t = main_player.get_time()
        main_player.set_mrl(srcpath)
        main_player.play()
        main_player.set_time(t)
        show_command('reload')

    def pause():
        main_player.set_pause(1)
        show_command('pause')

    def seek(s):
        t = offset(main_player.get_time(), (s * 1000))
        main_player.set_time(t)
        show_command('seek', '%+ds' % s)

    def mark_left():
        marks[0] = (main_player.get_time())
        show_command('markL', format_dt(marks[0]))

    def mark_right():
        marks[1] = (main_player.get_time())
        show_command('markR', format_dt(marks[1]))

    def adjust_left(ms):
        l = marks[0]
        marks[0] = (offset(l, ms))
        show_command('adjustL', '%+dms' % ms)

    def adjust_right(ms):
        r = marks[1]
        marks[1] = (offset(r, ms))
        show_command('adjustR', '%+dms' % ms)

    def offset(t, os):
        return min(max(t + os, 0), audio.duration*1000)

    def chop():
        l, r = marks
        if l >= r:
            return
        pause()
        audio_slice = audio.subclip(l/1000., r/1000.)
        now = datetime.now().strftime('%Y%m%d-%H%M%S')
        filename = now + '.mp3'
        filepath = os.path.join(anki_media, filename)
        audio_slice.write_audiofile(filepath, verbose=False, progress_bar=False)
        ctx['last_chop'] = filepath
        ctx['last_lr'] = (l, r)
        show_command('chop', filename)
        chop_player.set_mrl(filepath)
        chop_player.play()
        # Seek to the end of the chop
        main_player.set_time(r)

    def replay_chop():
        last_chop = ctx['last_chop']
        if not last_chop:
            return
        pause()
        chop_player.set_mrl(last_chop)
        chop_player.play()
        show_command('replay', os.path.relpath(last_chop, anki_media))

    def dump(do_transcript):
        if not ctx['last_chop']:
            return

        loop = ctx['loop']
        last_chop = ctx['last_chop']
        last_l, last_r = ctx['last_lr']

        filename = os.path.relpath(last_chop, anki_media)
        sound_str = '[sound:%s]' % filename

        if do_transcript:
            show_command('dump', '%s (recognizing)' % sound_str)
            loop.draw_screen()
            transcript = (slice_subs(subs, last_l, last_r) if subs else
                          recognize(last_chop)).strip()
            text = '%s\n%s' % (sound_str, transcript)
            pyperclip.copy(text)
            show_command('dump', text)
        else:
            pyperclip.copy(sound_str)
            show_command('dump', sound_str)

    def yank_title():
        pyperclip.copy(srcpath)
        show_command('clip', srcpath)

    def yank_position():
        pos = format_dt(main_player.get_time())
        # Remove ms part
        pos, _ = pos.split('.')
        pyperclip.copy(pos)
        show_command('clip', pos)

    def show_command(cmd, msg=''):
        name_text.set_text(cmd)
        detail_text.set_text(msg)

    def handle(key):
        if key is 'q':
            raise urwid.ExitMainLoop()
        if key is ' ':
            toggle()
        if key is 'j':
            seek(-2)
        if key is 'J':
            seek(-30)
        if key is 'k':
            mark_left()
        if key is 'K':
            mark_right()
            chop()
        if key is 'l':
            seek(2)
        if key is 'L':
            seek(30)
        if key is 'a':
            adjust_left(+100)
        if key is 'z':
            adjust_left(-100)
        if key is 's':
            adjust_right(+100)
        if key is 'x':
            adjust_right(-100)
        if key is 'c':
            chop()
        if key is 'r':
            replay_chop()
        if key is 'R':
            reload_and_play()
        if key is 'd':
            dump(do_transcript=False)
        if key is 'D':
            dump(do_transcript=True)
        if key is 'y':
            yank_position()
        if key is 'Y':
            yank_title()

    return handle


def slice_subs(subs, l, r, padding=1500):
    ss = subs.slice(starts_after=l-padding, ends_before=r+padding)
    return ' '.join(s.text for s in ss)


def recognize(filepath):
    mp3 = AudioFileClip(filepath)
    _, wav_filepath = tempfile.mkstemp('.wav')
    mp3.write_audiofile(wav_filepath, verbose=False, progress_bar=False)
    r = sr.Recognizer()
    with sr.AudioFile(wav_filepath) as source:
        audio = r.record(source)
        try:
            return r.recognize_google_cloud(audio)
        except (sr.RequestError, sr.UnknownValueError) as exc:
            return str(exc)


def format_dt(ms1):
    s1, ms = divmod(ms1, 1000)
    m1, s = divmod(s1, 60)
    h, m = divmod(m1, 60)
    return '%d:%02d:%02d.%d' % (h, m, s, ms // 100)



if __name__ == "__main__":
    main()
