"""

    [Module documentaion here]

    @author:[author], [year]
"""
from PyQt5 import QtWidgets
from path import Path

from PyQt5.QtGui import QIcon
from PyQt5.QtWidgets import QMainWindow, QListWidgetItem, QMessageBox

from core.logging_ import Logger
from core.models import TrackTag, Track
from core.repositories import TrackRepository, SessionTrackRepository, SessionRepository, \
    TrackTagRepository, TagRepository
from ui.qt.dlg_meta_editor import DlgMetaEditor
from ui.qt.dlg_playlist import DlgPlaylist
from ui.qt.dlg_select_playlist import DlgSelectPlaylist
from ui.qt.main_ui import Ui_mainWindow
from ui.qt.widgets.explorertable import ExplorerTable
from ui.qt.widgets.playlist_table import PlaylistTable

logger = Logger.get()


class MainWindow(QMainWindow):
    def __init__(self, settings=None):
        super().__init__()

        self.settings = settings or {}

        self.selected_playlist = None
        self.playing_queue = []
        self.selected_track = None  # /!\ the selected track is not necessarily the one being played!

        self.createWidgets()

    def createWidgets(self):
        self.ui = Ui_mainWindow()
        self.ui.setupUi(self)

        # Stack and menus
        self.ui.stack.setCurrentIndex(0)

        menu_items = {
            0: (':/img/rsc/dance.png', 'Ma séance'),
            1: (':/img/rsc/map.png', 'Explorer'),
            # 2: (':/img/rsc/calendar.png', 'Agenda'),
            3: (':/img/rsc/settings.png', 'Paramètres'),
        }

        for i, item in menu_items.items():
            icon, lbl = item
            item = QListWidgetItem(QIcon(icon), lbl)
            item.index = i
            self.ui.menu.addItem(item)

        QtWidgets.qApp.focusChanged.connect(self.focus_changed)

        # Menu item clicked
        self.ui.menu.itemClicked.connect(self.menu_item_selected)

        # Page 1 - Homepage
        self.ui.sessionPlaylist.trackDoubleClicked.connect(self.play_playlist)
        self.ui.sessionBtnChange.clicked.connect(self.selectPlaylist)

        self.ui.btnSessionStart.clicked.connect(self.play_playlist)

        # Page 3 - explorer
        self.ui.explorerTable.populate()
        self.ui.explorerTable.trackSelected.connect(self.newTrackSelected)
        self.ui.explorerTable.trackDoubleClicked.connect(self.play_track)
        self.ui.btnExplorerRefresh.clicked.connect(self.refresh_explorer_tree)
        self.ui.explorerLineSearch.editingFinished.connect(self.explorerFiltersChanged)
        self.ui.explorerLineSearch.textChanged.connect(self.explorerFiltersChanged)  # when search bar is cleared
        self.ui.explorerBtnSearch.clicked.connect(self.explorerFiltersChanged)

        self.populate_explorer_filter_tags()

        self.ui.explorerTrackEdit.clicked.connect(self.showMetaEditor)

        self.ui.lineSearchTags.textChanged.connect(self.ui.explorerTrackTagsTable.filter)
        self.ui.explorerTrackTagsTable.tagChecked.connect(self.addTrackTags)
        self.ui.explorerTrackTagsTable.tagUnchecked.connect(self.removeTrackTags)

        self.ui.explorerTrackMetaStack.setCurrentIndex(0)
        self.ui.btnPlaySelectedTrack.clicked.connect(self.playSelectedTrack)

        self.ui.btnSelectPlaylist.clicked.connect(self.selectPlaylist)

        self.ui.explorerAddToPlaylist.clicked.connect(self.add_to_playlist)
        self.ui.explorerPlaylist.droppedEvent.connect(self.add_to_playlist)

        self.ui.explorerPlaylist.trackSelected.connect(self.newTrackSelected)
        self.ui.explorerPlaylist.trackDoubleClicked.connect(self.play_playlist)

        self.ui.btnPlaylistMoveToTop.clicked.connect(self.ui.explorerPlaylist.move_selected_to_top)
        self.ui.btnPlaylistMoveUp.clicked.connect(self.ui.explorerPlaylist.move_selected_up)
        self.ui.btnPlaylistMoveDown.clicked.connect(self.ui.explorerPlaylist.move_selected_down)
        self.ui.btnPlaylistMoveToBottom.clicked.connect(self.ui.explorerPlaylist.move_selected_to_bottom)

        self.ui.btnPlaylistMoveToTop.clicked.connect(self.ui.sessionPlaylist.update)
        self.ui.btnPlaylistMoveUp.clicked.connect(self.ui.sessionPlaylist.update)
        self.ui.btnPlaylistMoveDown.clicked.connect(self.ui.sessionPlaylist.update)
        self.ui.btnPlaylistMoveToBottom.clicked.connect(self.ui.sessionPlaylist.update)

        self.ui.btnPlaylistRemove.clicked.connect(self.remove_from_playlist)

        # Page 5 - settings
        self.ui.musicFoldersAddButton.clicked.connect(self.ui.settingsMusicFoldersTable.add_music_folder)
        self.ui.musicFoldersRemoveButton.clicked.connect(self.ui.settingsMusicFoldersTable.remove_music_folder)
        self.ui.settingsMusicFoldersTable.populate()

        self.ui.tagsTableAddButton.clicked.connect(self.addTag)
        self.ui.tableTagsRemoveButton.clicked.connect(self.removeTag)
        self.ui.settingsTagsTableWidget.selectable = False
        self.ui.settingsTagsTableWidget.populate()
        self.ui.settingsTagsTableWidget.contentChanged.connect(self.ui.explorerTrackTagsTable.populate)
        self.ui.settingsTagsTableWidget.contentChanged.connect(self.populate_explorer_filter_tags)

        # vlc frame
        self.ui.vlcFrame.trackStarted.connect(self.vlcTrackStarted)
        self.ui.vlcFrame.playlistEnded.connect(self.vlcPlaylistEnded)

        # Apply settings
        if self.settings:
            self.ui.vlcFrame.set_volume(int(self.settings['volume']))
            self.ui.vlcFrame.set_muted(self.settings['muted'])

            playlist_id = self.settings['playlist']
            if playlist_id is not None and playlist_id > 0:
                playlist_repo = SessionRepository()
                playlist = playlist_repo.get_by_id(playlist_id)
                self.loadPlaylist(playlist)

    def focus_changed(self, old, new):
        if old is self.ui.explorerTrackNotepad:
            self.saveTrackNotes()

    def menu_item_selected(self, e):
        self.ui.stack.setCurrentIndex(e.index)

    def indexation_started(self):
        self.ui.statusbar.setStatusTip("Indexation en cours...")

    def indexation_ended(self):
        self.ui.statusbar.setStatusTip("Indexation terminée.")

    def refresh_explorer_tree(self, tracks=None):
        if tracks:
            self.ui.explorerTable.update_tracks(tracks)
        else:
            self.ui.explorerTable.populate()
        self.ui.explorerLineSearch.clear()
        self.ui.explorerFilterTags.setCurrentIndex(0)

    def newTrackSelected(self, track=None):
        sender = self.sender()
        self.selected_track = None
        self.ui.explorerTrackMetaStack.setCurrentIndex(0)
        self.ui.btnPlaySelectedTrack.setEnabled(False)
        self.ui.explorerAddToPlaylist.setEnabled(False)
        self.ui.btnPlaylistRemove.setEnabled(False)
        self.ui.explorerTrackNotepad.setText("")
        self.ui.frameMissingFileWarning.setVisible(False)

        if not track:
            return

        self.selected_track = track

        # track infos
        self.update_meta()

        self.ui.explorerTrackNotepad.setHtml(track.note)
        self.ui.explorerTrackTagsTable.populate(track)
        self.ui.explorerTrackMetaStack.setCurrentIndex(1)

        # Commands
        if self.selected_track.status == Track.STATUS_UNAVAILABLE:
            self.ui.frameMissingFileWarning.setVisible(True)
        else:
            self.ui.btnPlaySelectedTrack.setEnabled(True)

            if type(sender) is ExplorerTable:
                self.ui.explorerAddToPlaylist.setEnabled(True)

            elif type(sender) is PlaylistTable:
                self.ui.btnPlaylistRemove.setEnabled(True)
            else:
                raise RuntimeError("Unknown sender")

        self.ui.explorerTable.viewport().repaint()
        self.ui.explorerPlaylist.viewport().repaint()
        self.ui.sessionPlaylist.viewport().repaint()

    def update_meta(self):
        if self.selected_track:
            title, artist, album, track_num = self.selected_track.title, \
                                              self.selected_track.artist, \
                                              self.selected_track.album, \
                                              self.selected_track.track_num
        else:
            title, artist, album, track_num = "", "", "", ""

        self.ui.explorerLblTrackTitle.setText(title)
        self.ui.explorerLblTrackArtist.setText(artist)
        self.ui.explorerLblTrackAlbum.setText(album)
        self.ui.explorerLblTrackNumber.setText(
            str(track_num if track_num is not None else "")
        )

        if self.selected_track:
            # refresh tables
            self.refresh_explorer_tree([self.selected_track])
            if self.selected_playlist:
                self.ui.explorerPlaylist.update()
                self.ui.sessionPlaylist.update()

    def populate_explorer_filter_tags(self):
        tag_repo = TagRepository()
        try:
            self.ui.explorerFilterTags.currentIndexChanged.disconnect(self.explorerFiltersChanged)
        except TypeError:
            pass
        self.ui.explorerFilterTags.clear()

        self.ui.explorerFilterTags.addItem("Toutes les étiquettes", 0)
        self.ui.explorerFilterTags.setCurrentIndex(0)
        for tag in tag_repo.get_all():
            self.ui.explorerFilterTags.addItem(tag.label, tag.id)
        self.ui.explorerFilterTags.currentIndexChanged.connect(self.explorerFiltersChanged)

    def addTag(self):
        self.ui.settingsTagsTableWidget.add()

    def removeTag(self):
        if QMessageBox.question(self, "Confirmer", "Êtes-vous sûr(e) de vouloir supprimer cette étiquette?") != QMessageBox.Yes:
            return
        self.ui.settingsTagsTableWidget.removeSelected()

    def playSelectedTrack(self):
        if self.selected_track:
            self.play_track(self.selected_track)

    def play_track(self, track):
        if not Path(track.path).exists():
            return
        logger.info("Start playing: %s" % track)
        self.ui.vlcFrame.load_track(track)
        self.ui.vlcFrame.play()

    def showMetaEditor(self):
        if not self.selected_track:
            return
        r = DlgMetaEditor.edit(self, self.selected_track)
        if r:
            self.update_meta()

    def createOrEditPlaylist(self):
        r = DlgPlaylist.edit(self)

    def selectPlaylist(self):
        playlist = DlgSelectPlaylist.select(self)
        if playlist:
            self.loadPlaylist(playlist)

    def loadPlaylist(self, playlist):

        # home page
        self.ui.sessionPlaylist.setEnabled(True)
        self.ui.frameNotes.setEnabled(True)
        self.ui.sessionLblTitle.setText(playlist.name)
        self.ui.sessionPlaylist.populate(playlist)

        self.ui.frameNotes.set_playlist(playlist)

        # explorer page
        self.ui.explorerPlaylist.setEnabled(True)
        self.ui.explorerLblPlaylistTitle.setText(playlist.name)
        self.ui.explorerPlaylist.populate(playlist)

        self.selected_playlist = playlist

    def playlistItemDropped(self, event):
        if event.source() == ExplorerTable:
            self.add_to_playlist()

    def add_to_playlist(self):
        track = self.ui.explorerTable.selected_track()
        if track is None:
            return
        playlist = self.ui.explorerPlaylist.playlist
        if playlist is None:
            return
        track_repo = TrackRepository()
        track_repo.add_to_session(
            track.id,
            playlist.id
        )
        self.ui.explorerPlaylist.update()
        self.ui.sessionPlaylist.update()

    def remove_from_playlist(self):
        session_track = self.ui.explorerPlaylist.selected_session_track()
        if session_track is None:
            return
        session_track_repo = SessionTrackRepository()
        session_track_repo.delete(session_track, True)
        self.ui.explorerPlaylist.update()
        self.ui.sessionPlaylist.update()

    def play_playlist(self, track=None):

        if track.status == Track.STATUS_UNAVAILABLE:
            track = None

        playlist = self.selected_playlist
        track_repo = TrackRepository()
        tracks = track_repo.get_by_session_id(playlist.id)
        tracks = [t for t in tracks if t.status != Track.STATUS_UNAVAILABLE]
        if not tracks:
            return

        start_at = tracks.index(track) if track else 0

        logger.info("Start playing playlist: %s from index %s" % (playlist, start_at))
        self.ui.vlcFrame.load_playlist(playlist, tracks, start_at)
        self.ui.vlcFrame.play()

    def vlcTrackStarted(self, track):
        if self.selected_playlist:
            self.ui.explorerPlaylist.set_is_playing(track)
            self.ui.sessionPlaylist.set_is_playing(track)
        else:
            self.ui.explorerPlaylist.set_is_playing(None)
            self.ui.sessionPlaylist.set_is_playing(None)

    def vlcPlaylistEnded(self):
        self.ui.explorerPlaylist.set_is_playing(None)
        self.ui.sessionPlaylist.set_is_playing(None)

    def addTrackTags(self, tag_id):
        track_id = self.selected_track.id
        track_tag_repo = TrackTagRepository()
        tt = TrackTag(track_id=track_id, tag_id=tag_id)
        track_tag_repo.create(tt)
        track_tag_repo.commit()

    def removeTrackTags(self, tag_id):
        track_id = self.selected_track.id
        track_tag_repo = TrackTagRepository()
        track_tag_repo.query().filter(TrackTag.track_id == track_id).filter(TrackTag.tag_id == tag_id).delete()
        track_tag_repo.commit()

    def saveTrackNotes(self):
        if not self.selected_track:
            return
        note = self.ui.explorerTrackNotepad.toHtml()
        track_repo = TrackRepository()
        self.selected_track.note = note
        track_repo.commit()

    def explorerFiltersChanged(self):
        searchText = self.ui.explorerLineSearch.text()
        tag_id = int(self.ui.explorerFilterTags.currentData())
        self.ui.explorerTable.filterBy(tag_id, searchText)

    def filesIndexed(self, tracks):
        self.statusBar().showMessage(f"{len(tracks)} fichiers indexés", 3000)
        self.refresh_explorer_tree(tracks)

    def musicFolderStatusChanged(self, music_folder_id):
        self.ui.settingsMusicFoldersTable.populate()

    def currentSettings(self):
        volume = self.ui.vlcFrame.volume
        playlist_id = self.selected_playlist.id if self.selected_playlist else None
        muted = self.ui.vlcFrame.is_muted

        return {
            "playlist": playlist_id,
            "volume": volume,
            "muted": muted,
        }

    def __del__(self):
        if self.qdb.isOpen():
            self.qdb.close()
