#!/usr/bin/env python3
# -*- coding: utf-8 -*-

'''
Name:           youplay.py
Description:    search, play and save audio files from Youtube
Usage:          start it with: './youplay.py --help'
Author:         Ralf Hersel - ralf.hersel@gmx.net
Licence:        GPL3
GUI-Library:	GTK3
Repository:     https://codeberg.org/ralfhersel/youplay
More infos:     see constants and readme.md
'''

# === Todo =====================================================================

'''
- delete downloaded songs
- graphical buttons for media controls
'''

# === Libraries ================================================================
import os
from youplay import *

# Third party imports                                                           # for the music play in GUI mode
import gi                                                                       # for GTK Gui
gi.require_version('Gtk', '3.0')
from gi.repository import Gtk, Gdk, GLib

# === GUI ======================================================================

class Gui:
    def __init__(self):
        # === Variables ========================================================
        import locale
        locale.setlocale(locale.LC_NUMERIC, "C")
        from mpv import MPV
        self.player = mpv.MPV(ytdl=True, input_default_bindings=True, input_vo_keyboard=True) # initialize mpv internal player
        @self.player.property_observer('time-pos')

        def time_observer(_name, value):										# get playback timer
            if value is not None:                                               # avoid Type Error when started
                sec = int(value)                                                # get seconds from flaot
                sec_time_object = time.gmtime(sec)                              # convert to time object
                hms = time.strftime("%H:%M:%S",sec_time_object)                 # convert seconds to hour:minute:second
                title_time_text = self.status_text.strip() + '  -  ' + hms		# song titel - play time
                self.show_status_text(title_time_text)      					# update status bar

        # === Widgets ==========================================================

        # Window
            # Box (vertical)
                # Entry
                # ScrolledWindow
                    # TreeView
                        # TreeViewColumn
                # Box (horizontal)
                    # Buttons
                # StatusBar
                # ProgressBar
        
        self.window = Gtk.Window()                                              # Window
        self.window.set_title(PROG_NAME + ' it from Youtube')
        self.window.set_default_icon_from_file(os.path.dirname(__file__) + '/youplay.svg')
        self.window.set_default_size(WIN_WIDTH, WIN_HEIGHT)                     # width, height
        self.window.set_position(Gtk.WindowPosition.CENTER)						# center window

        self.window.connect('delete_event', self.on_window_delete)              # Window red-cross clicked
        
        self.main_box = Gtk.Box(orientation=Gtk.Orientation.VERTICAL)           # Vertical Box
        self.window.add(self.main_box)

        self.entry = Gtk.Entry()                                                # Entry
        self.entry.connect('activate', self.on_entry_return)                    # Return pressed
        icon_name = "system-search-symbolic"
        self.entry.set_icon_from_icon_name(Gtk.EntryIconPosition.PRIMARY, icon_name)
        self.main_box.pack_start(self.entry, False, False, 0)
        
        self.liststore = Gtk.ListStore(str, str, str)                           # Treeview and Liststore
        self.treeview = Gtk.TreeView(model=self.liststore)
        self.treeview.connect('button-press-event', self.on_treeview_clicked)	# Treeview item clicked
        for i, column_title in enumerate(['Nr', 'Title', 'Duration']):
            renderer = Gtk.CellRendererText()
            column = Gtk.TreeViewColumn(column_title, renderer, text=i)
            if i == 0:                                                          # Nr
                column.set_alignment(0.5)										# center Nr heading
                renderer.set_property('xalign', 0.5)                            # center Nr content
            if i == 2:                                                          # Duration
                column.set_alignment(1)										    # right align column heading
                renderer.set_property('xalign', 1)                              # right align column content
            self.treeview.append_column(column)

        self.scrollable_treelist = Gtk.ScrolledWindow()
        self.scrollable_treelist.add(self.treeview)
        self.main_box.pack_start(self.scrollable_treelist, True, True, 0)
        
        self.button_box = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL)       # Horizontal Button Box
        self.button_box.set_property('spacing', 5)
        self.button_box.set_property('homogeneous', True)
        
        self.button_play = Gtk.Button(label='Play')								# PLAY button
        self.button_play.connect('clicked', self.on_button_play_clicked)
        self.button_box.pack_start(self.button_play, True, True, 0)

        self.button_pause = Gtk.Button(label='Pause')							# PAUSE button
        self.button_pause.connect('clicked', self.on_button_pause_clicked)
        self.button_box.pack_start(self.button_pause, True, True, 0)

        self.button_back = Gtk.Button(label='Back')								# BACK button
        self.button_back.connect('clicked', self.on_button_back_clicked)
        self.button_box.pack_start(self.button_back, True, True, 0)

        self.button_forward = Gtk.Button(label='Forward')						# FORWARD button
        self.button_forward.connect('clicked', self.on_button_forward_clicked)
        self.button_box.pack_start(self.button_forward, True, True, 0)

        self.button_songs = Gtk.Button(label='Songs')							# SONGS button
        self.button_songs.connect('clicked', self.on_button_songs_clicked)
        self.button_box.pack_start(self.button_songs, True, True, 0)
        
        self.button_about = Gtk.Button(label='About')							# ABOUT button
        self.button_about.connect('clicked', self.on_button_about_clicked)
        self.button_box.pack_start(self.button_about, True, True, 0)
        
        self.button_quit = Gtk.Button(label='Quit')								# QUIT button
        self.button_quit.connect('clicked', self.on_button_quit_clicked)
        self.button_box.pack_start(self.button_quit, True, True, 0)

        self.main_box.add(self.button_box)
        
        self.status_bar = Gtk.Statusbar()
        self.main_box.add(self.status_bar)

        self.progressbar = Gtk.ProgressBar()
        self.progressbar.set_pulse_step(0.2)
        self.timeout_id = GLib.timeout_add(200, self.on_timeout, None)          # progress bar speed
        self.main_box.add(self.progressbar)
        
        self.window.show_all()                                                  # show GUI
        self.progressbar.hide()


# === Event Handler ============================================================

    def on_window_delete(self, widget, event, data=None):                       # close the window by red cross
        self.player.stop()
        del self.player
        Gtk.main_quit()


    def on_entry_return(self, widget):                                          # Return pressed in entry field
        label = self.button_play.get_label()                                    # Enable new search when song is playing
        if label == 'Stop':
            self.player.stop()
            self.show_status_text('Stopped playing')
            
        self.progressbar.hide()
        self.button_play.set_label('Play')
        self.progressbar.show()
        self.show_status_text('Retrieving song list from Youtube ... please wait')
        song = widget.get_text()
        song_dict = get_songlist(song)
        song_list = song_dict.items()                                           # convert dict to list
        if len(song_list) == 0:                                                 # nothing found
            self.show_status_text('Nothing found on Youtube, try another search.')
        else:
            self.liststore.clear()
            for key, item in song_dict.items():
                number = key
                title = item[0]
                duration = item[1]
                self.liststore.append([number, title, duration])
            self.show_status_text('Select a song and click Play')
        self.progressbar.hide()


    def on_treeview_clicked(self, widget, event):                               # click on treeview item
        if event.type == Gdk.EventType._2BUTTON_PRESS:                          # but only doubleclick
            self.player.stop()                                                  # stop already playing song
            selected = self.get_tree_selection(self.treeview)
            self.do_play(selected)


    def on_button_play_clicked(self, widget):                                   # PLAY/STOP clicked
        label = widget.get_label()
        if label == 'Play':
            selected = self.get_tree_selection(self.treeview)
            self.do_play(selected)
        if label == 'Stop':
            widget.set_label('Play')
            self.player.stop()
            self.show_status_text('Stopped playing')


    def on_button_pause_clicked(self, widget):									# PAUSE/CONTINUE clicked
        label = widget.get_label()
        if label == 'Pause':
            widget.set_label('Continue')
            self.player.command('cycle', 'pause')
        if label == 'Continue':
            widget.set_label('Pause')
            self.player.command('cycle', 'pause')


    def on_button_back_clicked(self, widget):									# BACK clicked
        self.player.command('seek', '-5')


    def on_button_forward_clicked(self, widget):								# FORWARD clicked
        self.player.command('seek', '5')


    def on_button_songs_clicked(self, widget):                                  # show list of downloaded songs
        music_path = get_music_dir()
        files=glob.glob(music_path + '*.mp3')
        try: filename = files[0]
        except IndexError:
            self.show_status_text('No songs in music folder: ' + music_path)
            return

        self.liststore.clear()
        number = 0
        for filepath in files:
            title = os.path.basename(filepath)                                  # get filename from path
            title = title[:-4]                                                  # cut '.mp3' from filename
            title = title[:MAX_TITLE_LENGHT].ljust(MAX_TITLE_LENGHT, ' ')    	# cut and fit title length
            number += 1
            duration = get_mp3_duration(filepath)
            self.liststore.append([str(number), title, duration])
        self.show_status_text('Select a song and click Play')
        

    def on_button_about_clicked(self, widget):                                  # ABOUT clicked
        self.show_info_dialog(PROG_NAME, ABOUT)


    def on_button_quit_clicked(self, widget):                                   # QUIT clicked
        self.player.stop()
        del self.player
        Gtk.main_quit()


    def on_timeout(self, user_data):                                            # update value on progress bar
        self.progressbar.pulse()
        return True


# === Methods ==================================================================

    def do_play(self, selected):                                                # play song from GUI
        if selected == None:
            self.show_status_text('Nothing selected')
        else:
            self.progressbar.show()
            self.show_status_text('Downloading song from Youtube ... please wait')
            song = selected[1]
            audio_path = get_song(song)
            self.progressbar.hide()
            self.status_text = selected[1]                        				# status bar will be updated by mpv time_observer
            self.button_play.set_label('Stop')
            self.player.play(audio_path)


    def get_tree_selection(self, tree):                                         # get selected tree item
        selection = tree.get_selection()
        model, treeiter = selection.get_selected()
        if treeiter != None:
            result = model[treeiter]
        else:
            result = None
        return result
        
    
    def show_status_text(self, text):                                           # show status text
        self.status_bar.push(0, text)
        while Gtk.events_pending():
            Gtk.main_iteration_do(True)                                         # wait until statusbar is refreshed


    def show_info_dialog(self, title, text):                                    # Info dialog
        dialog = Gtk.MessageDialog(message_type=Gtk.MessageType.INFO,
            buttons=Gtk.ButtonsType.OK, text=text)
        dialog.set_title(title)
        dialog.run()
        dialog.destroy()


# === Main =====================================================================
def main():
    gui = Gui()
    Gtk.main()

# === Main =====================================================================
if __name__ == '__main__':    
    main()
