#!/usr/bin/env python

from log import log
# import argparse
# import psutil
import sys
# import threading
# import urllib.parse
# import webbrowser
#
#
# from datetime import datetime, timedelta
# from typing import Callable, Dict, List, Optional, Tuple
#
# from PyQt5.QtCore import QDate, QEvent, QProcess, Qt, QTime, QTimer, QUrl
from PyQt5.QtCore import Qt
from PyQt5.QtGui import QColor
# from PyQt5.QtWebEngineWidgets import QWebEngineView as QWebView
from PyQt5.QtWidgets import (
    QApplication,
    QDialog,
    #     QFileDialog,
    #     QInputDialog,
    QLabel,
    #     QLineEdit,
    QListWidgetItem,
    QMainWindow,
    #     QMessageBox,
)
#
from dbconfig import engine, Session
# import helpers
# import music
#
# from config import Config
from models import (
    Base,
    # Playdates,
    PlaylistRows,
    Playlists,
    Settings,
    Tracks
)
from playlists import PlaylistTab
from sqlalchemy.orm.exc import DetachedInstanceError
# from ui.dlg_search_database_ui import Ui_Dialog
from ui.dlg_SelectPlaylist_ui import Ui_dlgSelectPlaylist
# from ui.downloadcsv_ui import Ui_DateSelect
from config import Config
from ui.main_window_ui import Ui_MainWindow
# from utilities import create_track_from_file, update_db
#
#
# log = logging.getLogger(Config.LOG_NAME)
class TrackData:
    def __init__(self, track):
        self.id = track.id
        self.title = track.title
        self.artist = track.artist
        self.duration = track.duration
        self.start_gap = track.start_gap
        self.fade_at = track.fade_at
        self.silence_at = track.silence_at
        self.path = track.path
        self.mtime = track.mtime


# class InfoTab(QWebView):
#     """Subclass QWebView to show info about tracks"""
# 
#     def __init__(self, parent=None) -> None:
#         super().__init__(parent)
#         self.title = None

class Window(QMainWindow, Ui_MainWindow):
    def __init__(self, parent=None) -> None:
        super().__init__(parent)
        self.setupUi(self)

#         self.timer: QTimer = QTimer()
#         self.even_tick: bool = True
#         self.playing: bool = False
#         self.disable_play_next_controls()
#
#         self.music: music.Music = music.Music()
        self.current_track: Optional[TrackData] = None
        self.current_track_playlist_tab: Optional[PlaylistTab] = None
        self.info_tabs: Optional[Dict[str, QWebView]] = {}
        self.next_track: Optional[TrackData] = None
        self.next_track_playlist_tab: Optional[PlaylistTab] = None
        self.previous_track: Optional[TrackData] = None
        self.previous_track_position: Optional[int] = None

#         self.set_main_window_size()
        self.lblSumPlaytime = QLabel("")
        self.statusbar.addPermanentWidget(self.lblSumPlaytime)
#         self.txtSearch = QLineEdit()
#         self.statusbar.addWidget(self.txtSearch)
#         self.txtSearch.setHidden(True)
#         self.hide_played_tracks = False
#
        self.splitter.setStretchSizes[200,200]
        self.visible_playlist_tab: Callable[[], PlaylistTab] = \
            self.tabPlaylist.currentWidget
#
        self._load_last_playlists()
#         self.enable_play_next_controls()
#         self.check_audacity()
#         self.timer.start(Config.TIMER_MS)
        self.connect_signals_slots()
#
#     def set_main_window_size(self) -> None:
#         """Set size of window from database"""
#
#         with Session() as session:
#             record = Settings.get_int_settings(session, "mainwindow_x")
#             x = record.f_int or 1
#             record = Settings.get_int_settings(session, "mainwindow_y")
#             y = record.f_int or 1
#             record = Settings.get_int_settings(session, "mainwindow_width")
#             width = record.f_int or 1599
#             record = Settings.get_int_settings(session, "mainwindow_height")
#             height = record.f_int or 981
#             self.setGeometry(x, y, width, height)
#         return
#
#     @staticmethod
#     def print_current_database():
#         with Session() as session:
#             db = session.bind.engine.url.database
#             print(f"{db=}")
#
#     @staticmethod
#     def check_audacity() -> None:
#         """Offer to run Audacity if not running"""
#
#         if not Config.CHECK_AUDACITY_AT_STARTUP:
#             return
#
#         if "audacity" in [i.name() for i in psutil.process_iter()]:
#             return
#
#         if helpers.ask_yes_no("Audacity not running", "Start Audacity?"):
#             QProcess.startDetached(Config.AUDACITY_COMMAND, [])

    def clear_selection(self) -> None:
        """ Clear selected row"""

        if self.visible_playlist_tab():
            self.visible_playlist_tab().clear_selection()
#
#     def closeEvent(self, event: QEvent) -> None:
#         """Don't allow window to close when a track is playing"""
#
#         if self.music.playing():
#             log.debug("closeEvent() ignored as music is playing")
#             event.ignore()
#             helpers.show_warning(
#                 "Track playing",
#                 "Can't close application while track is playing")
#         else:
#             log.debug("closeEvent() accepted")
#
#             with Session() as session:
#                 record = Settings.get_int_settings(
#                     session, "mainwindow_height")
#                 if record.f_int != self.height():
#                     record.update(session, {'f_int': self.height()})
#
#                 record = Settings.get_int_settings(session, "mainwindow_width")
#                 if record.f_int != self.width():
#                     record.update(session, {'f_int': self.width()})
#
#                 record = Settings.get_int_settings(session, "mainwindow_x")
#                 if record.f_int != self.x():
#                     record.update(session, {'f_int': self.x()})
#
#                 record = Settings.get_int_settings(session, "mainwindow_y")
#                 if record.f_int != self.y():
#                     record.update(session, {'f_int': self.y()})
#
#                 # Find a playlist tab (as opposed to an info tab) and
#                 # save column widths
#                 if self.current_track_playlist_tab:
#                     self.current_track_playlist_tab.close()
#                 elif self.next_track_playlist_tab:
#                     self.next_track_playlist_tab.close()
#
#             event.accept()

    def connect_signals_slots(self) -> None:
#         self.actionAdd_note.triggered.connect(self.create_note)
        self.action_Clear_selection.triggered.connect(self.clear_selection)
#         self.actionClosePlaylist.triggered.connect(self.close_playlist_tab)
#         self.actionDownload_CSV_of_played_tracks.triggered.connect(
#             self.download_played_tracks)
#         self.actionEnable_controls.triggered.connect(
#             self.enable_play_next_controls)
#         self.actionExport_playlist.triggered.connect(self.export_playlist_tab)
#         self.actionImport.triggered.connect(self.import_track)
#         self.actionFade.triggered.connect(self.fade)
#         self.actionMoveSelected.triggered.connect(self.move_selected)
#         self.actionNewPlaylist.triggered.connect(self.create_playlist)
#         self.actionOpenPlaylist.triggered.connect(self.open_playlist)
#         self.actionPlay_next.triggered.connect(self.play_next)
#         self.actionSearch.triggered.connect(self.search_playlist)
#         self.actionSearch_database.triggered.connect(self.search_database)
#         self.actionSelect_next_track.triggered.connect(self.select_next_row)
#         self.actionSelect_played_tracks.triggered.connect(self.select_played)
#         self.actionSelect_previous_track.triggered.connect(
#             self.select_previous_row)
#         self.actionSelect_unplayed_tracks.triggered.connect(
#             self.select_unplayed)
#         self.actionSetNext.triggered.connect(
#             lambda: self.tabPlaylist.currentWidget().set_selected_as_next())
#         self.actionSkip_next.triggered.connect(self.play_next)
#         self.actionStop.triggered.connect(self.stop)
#         # self.btnAddNote.clicked.connect(self.create_note)
#         # self.btnDatabase.clicked.connect(self.search_database)
#         self.btnDrop3db.clicked.connect(self.drop3db)
#         self.btnHidePlayed.clicked.connect(self.hide_played)
#         self.btnFade.clicked.connect(self.fade)
#         # self.btnPlay.clicked.connect(self.play_next)
#         # self.btnSetNext.clicked.connect(
#         #     lambda: self.tabPlaylist.currentWidget().set_selected_as_next())
#         # self.btnSongInfo.clicked.connect(self.song_info_search)
#         self.btnStop.clicked.connect(self.stop)
#         self.tabPlaylist.tabCloseRequested.connect(self.close_tab)
#         self.txtSearch.returnPressed.connect(self.search_playlist_return)
#         self.txtSearch.textChanged.connect(self.search_playlist_update)
#
#         self.timer.timeout.connect(self.tick)
#
#     def create_playlist(self) -> None:
#         """Create new playlist"""
#
#         dlg = QInputDialog(self)
#         dlg.setInputMode(QInputDialog.TextInput)
#         dlg.setLabelText("Playlist name:")
#         dlg.resize(500, 100)
#         ok = dlg.exec()
#         if ok:
#             with Session() as session:
#                 playlist = Playlists(session, dlg.textValue())
#                 self.create_playlist_tab(session, playlist)
#
#     def close_playlist_tab(self) -> None:
#         """Close active playlist tab"""
#
#         self.close_tab(self.tabPlaylist.currentIndex())
#
#     def close_tab(self, index: int) -> None:
#         """
#         Close tab unless it holds the curren or next track
#         """
#
#         if hasattr(self.tabPlaylist.widget(index), 'playlist_id'):
#             if self.tabPlaylist.widget(index) == (
#                     self.current_track_playlist_tab):
#                 self.statusbar.showMessage(
#                     "Can't close current track playlist", 5000)
#                 return
#             if self.tabPlaylist.widget(index) == self.next_track_playlist_tab:
#                 self.statusbar.showMessage(
#                     "Can't close next track playlist", 5000)
#                 return
#             # It's OK to close this playlist so remove from open playlist list
#             self.tabPlaylist.widget(index).close()
#
#         # Close regardless of tab type
#         self.tabPlaylist.removeTab(index)
#
#     def create_note(self) -> None:
#         """Call playlist to create note"""
#
#         try:
#             self.visible_playlist_tab().create_note()
#         except AttributeError:
#             # Just return if there's no visible playlist tab
#             return

    def create_playlist_tab(self, session: Session,
                            playlist: Playlists) -> None:
        """
        Take the passed playlist database object, create a playlist tab and
        add tab to display.
        """

        playlist_tab: PlaylistTab = PlaylistTab(
            musicmuster=self, session=session, playlist_id=playlist.id)
        idx: int = self.tabPlaylist.addTab(playlist_tab, playlist.name)
        self.tabPlaylist.setCurrentIndex(idx)
#
#     def disable_play_next_controls(self) -> None:
#         """
#         Disable "play next" keyboard controls
#         """
#
#         log.debug("disable_play_next_controls()")
#         self.actionPlay_next.setEnabled(False)
#         self.statusbar.showMessage("Play controls: Disabled", 0)
#
#     def download_played_tracks(self) -> None:
#         """Download a CSV of played tracks"""
#
#         dlg = DownloadCSV(self)
#         if dlg.exec():
#             start_dt = dlg.ui.dateTimeEdit.dateTime().toPyDateTime()
#             # Get output filename
#             pathspec: Tuple[str, str] = QFileDialog.getSaveFileName(
#                 self, 'Save CSV of tracks played',
#                 directory="/tmp/playlist.csv",
#                 filter="CSV files (*.csv)"
#             )
#             if not pathspec:
#                 return
#
#             path: str = pathspec[0]
#             if not path.endswith(".csv"):
#                 path += ".csv"
#
#             with open(path, "w") as f:
#                 with Session() as session:
#                     for playdate in Playdates.played_after(session, start_dt):
#                         f.write(
#                             f"{playdate.track.artist},{playdate.track.title}\n"
#                         )
#
#     def drop3db(self) -> None:
#         """Drop music level by 3db if button checked"""
#
#         if self.btnDrop3db.isChecked():
#             self.music.set_volume(Config.VOLUME_VLC_DROP3db, set_default=False)
#         else:
#             self.music.set_volume(Config.VOLUME_VLC_DEFAULT, set_default=False)
#
#     def enable_play_next_controls(self) -> None:
#         """
#         Enable "play next" keyboard controls
#         """
#
#         log.debug("enable_play_next_controls()")
#         self.actionPlay_next.setEnabled(True)
#         self.statusbar.showMessage("Play controls: Enabled", 0)
#
#     def end_of_track_actions(self) -> None:
#         """
#         Clean up after track played
#
#         Actions required:
#          - Set flag to say we're not playing a track
#          - Reset current track
#          - Tell playlist_tab track has finished
#          - Reset current playlist_tab
#          - Reset clocks
#          - Update headers
#          - Enable controls
#         """
#
#         # Set flag to say we're not playing a track so that tick()
#         # doesn't see player=None and kick off end-of-track actions
#         self.playing = False
#
#         # Reset current track
#         if self.current_track:
#             self.previous_track = self.current_track
#             self.current_track = None
#
#         # Tell playlist_tab track has finished and
#         # reset current playlist_tab
#         if self.current_track_playlist_tab:
#             self.current_track_playlist_tab.play_stopped()
#             self.current_track_playlist_tab = None
#
#         # Reset clocks
#         self.frame_fade.setStyleSheet("")
#         self.frame_silent.setStyleSheet("")
#         self.label_elapsed_timer.setText("00:00")
#         self.label_end_timer.setText("00:00")
#         self.label_fade_length.setText("0:00")
#         self.label_fade_timer.setText("00:00")
#         self.label_silent_timer.setText("00:00")
#         self.label_track_length.setText("0:00")
#         self.label_start_time.setText("00:00:00")
#         self.label_end_time.setText("00:00:00")
#
#         # Update headers
#         self.update_headers()
#
#         # Enable controls
#         self.enable_play_next_controls()
#
#     def export_playlist_tab(self) -> None:
#         """Export the current playlist to an m3u file"""
#
#         if not self.visible_playlist_tab():
#             return
#
#         with Session() as session:
#             playlist = Playlists.get_by_id(
#                 session, self.visible_playlist_tab().playlist_id)
#             # Get output filename
#             pathspec: Tuple[str, str] = QFileDialog.getSaveFileName(
#                 self, 'Save Playlist',
#                 directory=f"{playlist.name}.m3u",
#                 filter="M3U files (*.m3u);;All files (*.*)"
#             )
#             if not pathspec:
#                 return
#
#             path: str = pathspec[0]
#             if not path.endswith(".m3u"):
#                 path += ".m3u"
#
#             with open(path, "w") as f:
#                 # Required directive on first line
#                 f.write("#EXTM3U\n")
#                 for _, track in playlist.tracks.items():
#                     f.write(
#                         "#EXTINF:"
#                         f"{int(track.duration / 1000)},"
#                         f"{track.title} - "
#                         f"{track.artist}"
#                         "\n"
#                         f"{track.path}"
#                         "\n"
#                     )
#
#     def fade(self) -> None:
#         """Fade currently playing track"""
#
#         log.debug("musicmuster:fade()", True)
#
#         self.stop_playing(fade=True)
#
#     def hide_played(self):
#         """Toggle hide played tracks"""
#
#         if self.hide_played_tracks:
#             self.hide_played_tracks = False
#             self.btnHidePlayed.setText("Hide played")
#         else:
#             self.hide_played_tracks = True
#             self.btnHidePlayed.setText("Show played")
#         if self.current_track_playlist_tab:
#             with Session() as session:
#                 self.current_track_playlist_tab.update_display(session)
#
#     def import_track(self) -> None:
#         """Import track file"""
#
#         dlg = QFileDialog()
#         dlg.setFileMode(QFileDialog.ExistingFiles)
#         dlg.setViewMode(QFileDialog.Detail)
#         dlg.setDirectory(Config.IMPORT_DESTINATION)
#         dlg.setNameFilter("Music files (*.flac *.mp3)")
#
#         if dlg.exec_():
#             with Session() as session:
#                 txt: str = ""
#                 new_tracks = []
#                 for fname in dlg.selectedFiles():
#                     tags = helpers.get_tags(fname)
#                     new_tracks.append((fname, tags))
#                     title = tags['title']
#                     artist = tags['artist']
#                     possible_matches = Tracks.search_titles(session, title)
#                     if possible_matches:
#                         txt += 'Similar to new track '
#                         txt += f'"{title}" by "{artist} ({fname})":\n\n'
#                         for track in possible_matches:
#                             txt += f' "{track.title}" by {track.artist}'
#                             txt += f' ({track.path})\n'
#                         txt += "\n"
#                 # Check whether to proceed if there were potential matches
#                 if txt:
#                     txt += "Proceed with import?"
#                     result = QMessageBox.question(self,
#                                                   "Possible duplicates",
#                                                   txt,
#                                                   QMessageBox.Ok,
#                                                   QMessageBox.Cancel
#                                                   )
#                     if result == QMessageBox.Cancel:
#                         return
#
#                 # Import in separate thread
#                 thread = threading.Thread(target=self._import_tracks,
#                                           args=(new_tracks,))
#                 thread.start()
#
#     def _import_tracks(self, tracks: list):
#         """
#         Import passed files. Don't use parent session as that may be invalid
#         by the time we need it.
#         """
#
#         with Session() as session:
#             for (fname, tags) in tracks:
#                 track = create_track_from_file(session, fname, tags=tags)
#                 # Add to playlist on screen
#                 # If we don't specify "repaint=False", playlist will
#                 # also be saved to database
#                 self.visible_playlist_tab().insert_track(session, track)

    def _load_last_playlists(self) -> None:
        """Load the playlists that were open when the last session closed"""

        with Session() as session:
            for playlist in Playlists.get_open(session):
                self.create_playlist_tab(session, playlist)
                playlist.mark_open(session)

    def move_selected(self) -> None:
        """
        Move selected rows to another playlist

        Actions required:
        - identify destination playlist
        - update playlist for the rows in the database
        - remove them from the display
        - update destination playlist display if loaded
        """

        # Identify destination playlist
        with Session() as session:
            visible_tab = self.visible_playlist_tab()
            source_playlist = visible_tab.playlist_id
            playlists = []
            for playlist in Playlists.get_all(session):
                if playlist.id == source_playlist:
                    continue
                else:
                    playlists.append(playlist)

            # Get destination playlist id
            dlg = SelectPlaylistDialog(self, playlists=playlists,
                                       session=session)
            dlg.exec()
            if not dlg.playlist:
                return
            destination_playlist = dlg.playlist

            # Update playlist for the rows in the database
            plr_ids = visible_tab.get_selected_playlistrow_ids()
            PlaylistRows.move_to_playlist(
                session, plr_ids, destination_playlist.id
            )

            # Remove moved rows from display
            visible_tab.remove_selected_rows()

            # Update destination playlist_tab if visible (if not visible, it
            # will be re-populated when it is opened)
            destination_visible_playlist_tab = None
            for tab in range(self.tabPlaylist.count()):
                # Non-playlist tabs won't have a 'playlist_id' attribute
                if not hasattr(self.tabPlaylist.widget(tab), 'playlist_id'):
                    continue
                if self.tabPlaylist.widget(tab).playlist_id == dlg.playlist.id:
                    destination_visible_playlist_tab = (
                        self.tabPlaylist.widget(tab))
                    break
            if destination_visible_playlist_tab:
                destination_visible_playlist_tab.populate(
                    session, dlg.playlist.id)

    def open_info_tabs(self) -> None:
        """
        Ensure we have info tabs for next and current track titles
        """

        title_list: List[str] = []

        if self.previous_track:
            title_list.append(self.previous_track.title)
        if self.current_track:
            title_list.append(self.current_track.title)
        if self.next_track:
            title_list.append(self.next_track.title)

        for title in title_list:
            if title in self.info_tabs.keys():
                # We already have a tab for this track
                continue
            if len(self.info_tabs) >= Config.MAX_log.info_TABS:
                # Find an unneeded info tab
                try:
                    old_title = list(
                        set(self.info_tabs.keys()) - set(title_list)
                    )[0]
                except IndexError:
                    log.debug(
                        f"ensure_info_tabs({title_list}): unable to add "
                        f"{title=}"
                    )
                    return
                # Assign redundant widget a new title
                widget = self.info_tabs[title] = self.info_tabs[old_title]
                idx = self.tabPlaylist.indexOf(widget)
                self.tabPlaylist.setTabText(
                    idx, title[:Config.log.info_TAB_TITLE_LENGTH])
                del self.info_tabs[old_title]
            else:
                # Create a new tab for this title
                widget = self.info_tabs[title] = QWebView()
                widget.setZoomFactor(Config.WEB_ZOOM_FACTOR)
                self.tabPlaylist.addTab(
                    widget, title[:Config.log.info_TAB_TITLE_LENGTH])
            txt = urllib.parse.quote_plus(title)
            url = Config.log.info_TAB_URL % txt
            widget.setUrl(QUrl(url))
#
#     def play_next(self) -> None:
#         """
#         Play next track.
#
#         Actions required:
#         - If there is no next track set, return.
#         - If there's currently a track playing, fade it.
#         - Move next track to current track.
#         - Update record of current track playlist_tab
#         - If current track on different playlist_tab to last, reset
#             last track playlist_tab colour
#         - Set current track playlist_tab colour
#         - Restore volume if -3dB active
#         - Play (new) current track.
#         - Tell database to record it as played
#         - Tell playlist track is now playing
#         - Disable play next controls
#         - Update headers
#         - Update clocks
#         """
#
#         log.debug(
#             "musicmuster.play_next(), "
#             f"next_track={self.next_track.title if self.next_track else None} "
#             "current_track="
#             f"{self.current_track.title if self.current_track else None}",
#             True
#         )
#
#         # If there is no next track set, return.
#         if not self.next_track:
#             log.debug("musicmuster.play_next(): no next track selected", True)
#             return
#
#         with Session() as session:
#             # If there's currently a track playing, fade it.
#             self.stop_playing(fade=True)
#
#             # Move next track to current track.
#             self.current_track = self.next_track
#             self.next_track = None
#
#             # If current track on different playlist_tab to last, reset
#             # last track playlist_tab colour
#             # Set current track playlist_tab colour
#             if self.current_track_playlist_tab != self.next_track_playlist_tab:
#                 self.set_tab_colour(self.current_track_playlist_tab,
#                                     QColor(Config.COLOUR_NORMAL_TAB))
#
#             # Update record of current track playlist_tab
#             self.current_track_playlist_tab = self.next_track_playlist_tab
#             self.next_track_playlist_tab = None
#
#             # Set current track playlist_tab colour
#             self.set_tab_colour(self.current_track_playlist_tab,
#                                 QColor(Config.COLOUR_CURRENT_TAB))
#
#             # Restore volume if -3dB active
#             if self.btnDrop3db.isChecked():
#                 self.btnDrop3db.setChecked(False)
#
#             # Play (new) current track
#             start_at = datetime.now()
#             self.music.play(self.current_track.path)
#
#             # Tell database to record it as played
#             Playdates(session, self.current_track.id)
#
#             # Set last_played date
#             Tracks.update_lastplayed(session, self.current_track.id)
#
#             # Tell playlist track is now playing
#             self.current_track_playlist_tab.play_started(session)
#
#             # Disable play next controls
#             self.disable_play_next_controls()
#
#             # Update headers
#             self.update_headers()
#
#             # Update clocks
#             self.label_track_length.setText(
#                 helpers.ms_to_mmss(self.current_track.duration)
#             )
#             fade_at = self.current_track.fade_at
#             silence_at = self.current_track.silence_at
#             length = self.current_track.duration
#             self.label_fade_length.setText(
#                 helpers.ms_to_mmss(silence_at - fade_at))
#             self.label_start_time.setText(
#                 start_at.strftime(Config.TRACK_TIME_FORMAT))
#             end_at = start_at + timedelta(
#                 milliseconds=self.current_track.duration)
#             self.label_end_time.setText(
#                 end_at.strftime(Config.TRACK_TIME_FORMAT))
#
#     def search_database(self) -> None:
#         """Show dialog box to select and cue track from database"""
#
#         with Session() as session:
#             dlg = DbDialog(self, session)
#             dlg.exec()
#
#     def search_playlist(self):
#         """Show text box to search playlist"""
#
#         self.disable_play_next_controls()
#         self.txtSearch.setHidden(False)
#         self.txtSearch.setFocus()
#
#     def search_playlist_return(self):
#         """Close off search box when return pressed"""
#
#         self.txtSearch.setText("")
#         self.txtSearch.setHidden(True)
#         self.enable_play_next_controls()
#         self.visible_playlist_tab().set_filter("")
#
#     def search_playlist_update(self):
#         """Update search when search string changes"""
#
#         self.visible_playlist_tab().set_filter(self.txtSearch.text())
#
#     def open_playlist(self):
#         with Session() as session:
#             playlists = Playlists.get_closed(session)
#             dlg = SelectPlaylistDialog(self, playlists=playlists,
#                                        session=session)
#             dlg.exec()
#             playlist = dlg.playlist
#             if playlist:
#                 playlist.mark_open(session)
#                 self.create_playlist_tab(session, playlist)
#
#     def select_next_row(self) -> None:
#         """Select next or first row in playlist"""
#
#         self.visible_playlist_tab().select_next_row()
#
#     def select_played(self) -> None:
#         """Select all played tracks in playlist"""
#
#         self.visible_playlist_tab().select_played_tracks()
#
#     def select_previous_row(self) -> None:
#         """Select previous or first row in playlist"""
#
#         self.visible_playlist_tab().select_previous_row()
#
#     def select_unplayed(self) -> None:
#         """Select all unplayed tracks in playlist"""
#
#         self.visible_playlist_tab().select_unplayed_tracks()

    def set_tab_colour(self, widget: PlaylistTab, colour: QColor) -> None:
            """
            Find the tab containing the widget and set the text colour
            """

            idx = self.tabPlaylist.indexOf(widget)
            self.tabPlaylist.tabBar().setTabTextColor(idx, colour)
#
#     def song_info_search(self) -> None:
#         """
#         Open browser tab for Wikipedia, searching for
#         the first that exists of:
#             - selected track
#             - next track
#             - current track
#         """
#
#         title: Optional[str] = self.visible_playlist_tab().get_selected_title()
#         if not title:
#             if self.next_track:
#                 title = self.next_track.title
#         if not title:
#             if self.current_track:
#                 title = self.current_track.title
#         if title:
#             txt = urllib.parse.quote_plus(title)
#             url = Config.log.info_TAB_URL % txt
#             webbrowser.open(url, new=2)
#
#     def stop(self) -> None:
#         """Stop playing immediately"""
#
#         log.debug("musicmuster.stop()")
#
#         self.stop_playing(fade=False)
#
#     def stop_playing(self, fade=True) -> None:
#         """
#         Stop playing current track
#
#         Actions required:
#         - Return if not playing
#         - Stop/fade track
#         - Reset playlist_tab colour
#         - Run end-of-track actions
#         """
#
#         log.debug(f"musicmuster.stop_playing({fade=})", True)
#
#         # Return if not playing
#         if not self.playing:
#             log.debug("musicmuster.stop_playing(): not playing", True)
#             return
#
#         # Stop/fade track
#         self.previous_track_position = self.music.get_position()
#         if fade:
#             log.debug("musicmuster.stop_playing(): fading music", True)
#             self.music.fade()
#         else:
#             log.debug("musicmuster.stop_playing(): stopping music", True)
#             self.music.stop()
#
#         # Reset playlist_tab colour
#         if self.current_track_playlist_tab == self.next_track_playlist_tab:
#             self.set_tab_colour(self.current_track_playlist_tab,
#                                 QColor(Config.COLOUR_NEXT_TAB))
#         else:
#             self.set_tab_colour(self.current_track_playlist_tab,
#                                 QColor(Config.COLOUR_NORMAL_TAB))
#
#         # Run end-of-track actions
#         self.end_of_track_actions()

    def this_is_the_next_track(self, playlist_tab: PlaylistTab,
                               track: Tracks, session) -> None:
        """
        This is notification from a playlist tab that it holds the next
        track to be played.

        Actions required:
         - Clear next track if on other tab
         - Reset tab colour if on other tab
         - Note next playlist tab
         - Set next playlist_tab tab colour
         - Note next track
         - Update headers
         - Populate ‘info’ tabs

        """

        # Clear next track if on another tab
        if self.next_track_playlist_tab != playlist_tab:
            # We need to reset the ex-next-track playlist
            if self.next_track_playlist_tab:
                self.next_track_playlist_tab.clear_next(session)

                # Reset tab colour if on other tab
                if (self.next_track_playlist_tab !=
                        self.current_track_playlist_tab):
                    self.set_tab_colour(
                        self.next_track_playlist_tab,
                        QColor(Config.COLOUR_NORMAL_TAB))

            # Note next playlist tab
            self.next_track_playlist_tab = playlist_tab

        # Set next playlist_tab tab colour if it isn't the
        # currently-playing tab
        if (self.next_track_playlist_tab !=
                self.current_track_playlist_tab):
            self.set_tab_colour(
                self.next_track_playlist_tab,
                QColor(Config.COLOUR_NEXT_TAB))

        # Note next track
        self.next_track = TrackData(track)

        # Update headers
        self.update_headers()

        # Populate 'info' tabs
        self.open_info_tabs()
#
#     def tick(self) -> None:
#         """
#         Carry out clock tick actions.
#
#         The Time of Day clock is updated every tick (500ms).
#
#         All other timers are updated every second. As the timers have a
#         one-second resolution, updating every 500ms can result in some
#         timers updating and then, 500ms later, other timers updating. That
#         looks odd.
#
#         Actions required:
#         - Update TOD clock
#         - If track is playing, update track clocks time and colours
#         - Else: run stop_track
#         """
#
#         # Update TOD clock
#         self.lblTOD.setText(datetime.now().strftime(Config.TOD_TIME_FORMAT))
#
#         self.even_tick = not self.even_tick
#         if not self.even_tick:
#             return
#
#         # If track is playing, update track clocks time and colours
#         if self.music.player and self.music.playing():
#             self.playing = True
#             playtime: int = self.music.get_playtime()
#             time_to_fade: int = (self.current_track.fade_at - playtime)
#             time_to_silence: int = (
#                     self.current_track.silence_at - playtime)
#             time_to_end: int = (self.current_track.duration - playtime)
#
#             # Elapsed time
#             if time_to_end < 500:
#                 self.label_elapsed_timer.setText(
#                     helpers.ms_to_mmss(playtime)
#                 )
#             else:
#                 self.label_elapsed_timer.setText(
#                     helpers.ms_to_mmss(playtime)
#                 )
#
#             # Time to fade
#             self.label_fade_timer.setText(helpers.ms_to_mmss(time_to_fade))
#
#             # If silent in the next 5 seconds, put warning colour on
#             # time to silence box and enable play controls
#             if time_to_silence <= 5500:
#                 self.frame_silent.setStyleSheet(
#                     f"background: {Config.COLOUR_ENDING_TIMER}"
#                 )
#                 self.enable_play_next_controls()
#             # Set warning colour on time to silence box when fade starts
#             elif time_to_fade <= 500:
#                 self.frame_silent.setStyleSheet(
#                     f"background: {Config.COLOUR_WARNING_TIMER}"
#                 )
#             # Five seconds before fade starts, set warning colour on
#             # time to silence box and enable play controls
#             elif time_to_fade <= 5500:
#                 self.frame_fade.setStyleSheet(
#                     f"background: {Config.COLOUR_WARNING_TIMER}"
#                 )
#                 self.enable_play_next_controls()
#             else:
#                 self.frame_silent.setStyleSheet("")
#                 self.frame_fade.setStyleSheet("")
#
#             self.label_silent_timer.setText(
#                 helpers.ms_to_mmss(time_to_silence)
#             )
#
#             # Time to end
#             self.label_end_timer.setText(helpers.ms_to_mmss(time_to_end))
#
#         else:
#             if self.playing:
#                 self.stop_playing()

    def update_headers(self) -> None:
        """
        Update last / current / next track headers
        """

        try:
            self.hdrPreviousTrack.setText(
                f"{self.previous_track.title} - {self.previous_track.artist}"
            )
        except (AttributeError, DetachedInstanceError):
            self.hdrPreviousTrack.setText("")

        try:
            self.hdrCurrentTrack.setText(
                f"{self.current_track.title} - {self.current_track.artist}"
            )
        except (AttributeError, DetachedInstanceError):
            self.hdrCurrentTrack.setText("")

        try:
            self.hdrNextTrack.setText(
                f"{self.next_track.title} - {self.next_track.artist}"
            )
        except (AttributeError, DetachedInstanceError):
            self.hdrNextTrack.setText("")
#
#
# class DbDialog(QDialog):
#     """Select track from database"""
#
#     def __init__(self, parent, session):    # review
#         super().__init__(parent)
#         self.session = session
#         self.ui = Ui_Dialog()
#         self.ui.setupUi(self)
#         self.ui.btnAdd.clicked.connect(self.add_selected)
#         self.ui.btnAddClose.clicked.connect(self.add_selected_and_close)
#         self.ui.btnClose.clicked.connect(self.close)
#         self.ui.matchList.itemDoubleClicked.connect(self.double_click)
#         self.ui.matchList.itemSelectionChanged.connect(self.selection_changed)
#         self.ui.radioTitle.toggled.connect(self.title_artist_toggle)
#         self.ui.searchString.textEdited.connect(self.chars_typed)
#
#         record = Settings.get_int_settings(self.session, "dbdialog_width")
#         width = record.f_int or 800
#         record = Settings.get_int_settings(self.session, "dbdialog_height")
#         height = record.f_int or 600
#         self.resize(width, height)
#
#     def __del__(self):  # review
#         record = Settings.get_int_settings(self.session, "dbdialog_height")
#         if record.f_int != self.height():
#             record.update(self.session, {'f_int': self.height()})
#
#         record = Settings.get_int_settings(self.session, "dbdialog_width")
#         if record.f_int != self.width():
#             record.update(self.session, {'f_int': self.width()})
#
#     def add_selected(self):     # review
#         if not self.ui.matchList.selectedItems():
#             return
#
#         item = self.ui.matchList.currentItem()
#         track = item.data(Qt.UserRole)
#         self.add_track(track)
#
#     def add_selected_and_close(self):   # review
#         self.add_selected()
#         self.close()
#
#     def title_artist_toggle(self):  # review
#         """
#         Handle switching between searching for artists and searching for
#         titles
#         """
#
#         # Logic is handled already in chars_typed(), so just call that.
#         self.chars_typed(self.ui.searchString.text())
#
#     def chars_typed(self, s):   # review
#         if len(s) > 0:
#             if self.ui.radioTitle.isChecked():
#                 matches = Tracks.search_titles(self.session, s)
#             else:
#                 matches = Tracks.search_artists(self.session, s)
#             self.ui.matchList.clear()
#             if matches:
#                 for track in matches:
#                     t = QListWidgetItem()
#                     t.setText(
#                         f"{track.title} - {track.artist} "
#                         f"[{helpers.ms_to_mmss(track.duration)}] "
#                         f"({helpers.get_relative_date(track.lastplayed)})"
#                     )
#                     t.setData(Qt.UserRole, track)
#                     self.ui.matchList.addItem(t)
#
#     def double_click(self, entry):  # review
#         track = entry.data(Qt.UserRole)
#         self.add_track(track)
#         # Select search text to make it easier for next search
#         self.select_searchtext()
#
#     def add_track(self, track):     # review
#         # Add to playlist on screen
#         self.parent().visible_playlist_tab().insert_track(
#             self.session, track)
#         # Commit session to get correct row numbers if more tracks added
#         self.session.commit()
#         # Select search text to make it easier for next search
#         self.select_searchtext()
#
#     def select_searchtext(self):    # review
#         self.ui.searchString.selectAll()
#         self.ui.searchString.setFocus()
#
#     def selection_changed(self):    # review
#         if not self.ui.matchList.selectedItems():
#             return
#
#         item = self.ui.matchList.currentItem()
#         track = item.data(Qt.UserRole)
#         self.ui.dbPath.setText(track.path)
#
#
# class DownloadCSV(QDialog):
#     def __init__(self, parent=None):
#         super().__init__(parent)
#
#         self.ui = Ui_DateSelect()
#         self.ui.setupUi(self)
#         self.ui.dateTimeEdit.setDate(QDate.currentDate())
#         self.ui.dateTimeEdit.setTime(QTime(19, 59, 0))
#         self.ui.buttonBox.accepted.connect(self.accept)
#         self.ui.buttonBox.rejected.connect(self.reject)


class SelectPlaylistDialog(QDialog):
    def __init__(self, parent=None, playlists=None, session=None):
        super().__init__(parent)

        if playlists is None:
            return
        self.ui = Ui_dlgSelectPlaylist()
        self.ui.setupUi(self)
        self.ui.lstPlaylists.itemDoubleClicked.connect(self.list_doubleclick)
        self.ui.buttonBox.accepted.connect(self.open)
        self.ui.buttonBox.rejected.connect(self.close)
        self.session = session
        self.playlist = None
        self.plid = None

        record = Settings.get_int_settings(
            self.session, "select_playlist_dialog_width")
        width = record.f_int or 800
        record = Settings.get_int_settings(
            self.session, "select_playlist_dialog_height")
        height = record.f_int or 600
        self.resize(width, height)

        for playlist in playlists:
            p = QListWidgetItem()
            p.setText(playlist.name)
            p.setData(Qt.UserRole, playlist)
            self.ui.lstPlaylists.addItem(p)

    def __del__(self):  # review
        record = Settings.get_int_settings(
            self.session, "select_playlist_dialog_height")
        if record.f_int != self.height():
            record.update(self.session, {'f_int': self.height()})

        record = Settings.get_int_settings(
            self.session, "select_playlist_dialog_width")
        if record.f_int != self.width():
            record.update(self.session, {'f_int': self.width()})

    def list_doubleclick(self, entry):  # review
        self.playlist = entry.data(Qt.UserRole)
        self.accept()

    def open(self):     # review
        if self.ui.lstPlaylists.selectedItems():
            item = self.ui.lstPlaylists.currentItem()
            self.playlist = item.data(Qt.UserRole)
            self.accept()


if __name__ == "__main__":
    #     p = argparse.ArgumentParser()
    #     # Only allow at most one option to be specified
    #     group = p.add_mutually_exclusive_group()
    #     group.add_argument('-u', '--update',
    #                        action="store_true", dest="update",
    #                        default=False, help="Update database")
    #     # group.add_argument('-f', '--full-update',
    #     #                    action="store_true", dest="full_update",
    #     #                    default=False, help="Update database")
    #     # group.add_argument('-i', '--import', dest="fname", help="Input file")
    #     args = p.parse_args()
    #
    #     # Run as required
    #     if args.update:
    #         log.debug("Updating database")
    #         with Session() as session:
    #             update_db(session)
    #     # elif args.full_update:
    #     #     log.debug("Full update of database")
    #     #     with Session() as session:
    #     #         full_update_db(session)
    #     else:
    #         # Normal run
    try:
        Base.metadata.create_all(engine)
        app = QApplication(sys.argv)
        win = Window()
        win.show()
        sys.exit(app.exec())
    except Exception:
        msg = "Unhandled Exception caught by musicmuster.main()"
        log.exception(msg, exc_info=True, stack_info=True)
