class SelectAudioListWidget(QListWidget):
    """
    Class that creates a customiced QListWidget used in the SelectAudioMenu.
    """
    def __init__(self, controller, parent=None):
        """
        Constructor of the SelectAudioListWidget class.

        :param controller: GUIController object.
        """

        super(SelectAudioListWidget, self).__init__(parent)

        self.db = RAM_DB()
        self.controller = controller

        self.itemClicked.connect(self.item_click)
        self.setMinimumSize(QSize(600, 300))

        # Those lines are for the kinetic scrolling in the list
        QScroller.grabGesture(self, QScroller.LeftMouseButtonGesture)

        self.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
        self.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
        self.setVerticalScrollMode(QAbstractItemView.ScrollPerPixel)
        self.setHorizontalScrollMode(QAbstractItemView.ScrollPerPixel)
        self.setAutoScroll(False)

        self.setStyleSheet("background-color: rgb(255,255,255)")

        self.setSelectionMode(QAbstractItemView.NoSelection)
        (fileName, pathFiles, self.metaDataList) = self.db.getAudioDB()
        self.itemsDict = {}

        for i in range(0, len(fileName)):

            customListItemWidget = CustomListItemWidget()
            customListItemWidget.setTextUp(self.metaDataList[i][0])
            customListItemWidget.setTextDown(
                getBandName(self.metaDataList[i][1]))
            customListItemWidget.setIcon(getArtworkPath(self.metaDataList[i]))

            customListItemWidget.setPath(pathFiles[i])

            item = QListWidgetItem()
            item.setSizeHint(customListItemWidget.sizeHint())

            self.addItem(item)
            self.setItemWidget(item, customListItemWidget)
            self.itemsDict[str(item)] = customListItemWidget

    def item_click(self, item):
        """
        Changes to the Play Audio Menu when a item is clicked.

        :param item: The clicked item.
        """

        #Set the track selected for playing it
        self.db.setSelection(
            self.db.getIndexByPath(self.itemsDict[str(item)].getPath()))
        #Switch to PlayAudioMenu
        self.controller.changeToMenu("PlayAudioMenu")
        #Call to audioController for load the new audio file...
        audioController = AudioController()
        audioController.loadAudio()
    class __AudioController:
        def __init__(self):
            """
            Constructor of the AudioController class.
            """

            self.db = RAM_DB()
            (self.fileName, self.pathFiles,
             self.metaDataList) = self.db.getAudioDB()
            self.path = None
            self.audioObject = AudioFile(self.notifyController)
            self.currentFMStation = None
            self.SI4703 = SI4703()
            self.playingRadio = False
            self.observers = []
            self.GUICoolDown = False
            self.threadController = ThreadController()

        ###############################################################################
        #OBSERVER PATTERN
        ###############################################################################
        def register(self, observer):
            """
            Register method of the observer pattern.

            :param observer: Object to subscribe.
            """

            if not observer in self.observers:
                self.observers.append(observer)

        def unregister(self, observer):
            """
            Untegister method of the observer pattern.

            :param observer: Object to unsubscribe.
            """

            if observer in self.observers:
                self.observers.remove(observer)

        def unregister_all(self):
            """
            Unsubscribe all the subscribers.
            """

            if self.observers:
                del self.observers[:]

        def update_observers(self, *args, **kwargs):
            """
            Notify to all the observers/subscribers.

            :param args: args
            :param kwargs: kwargs
            """

            for observer in self.observers:
                observer.update(*args, **kwargs)

        ###############################################################################

        def getAudioObject(self):
            """
            Returns the audio object, instance of the AudioFile class.
            :return: Current Audio File instance.
            """

            return self.audioObject

        def loadAudio(self):
            """
            Loads an audio file. (Plays it).
            """

            if (self.playingRadio == True):
                self.stopRadio()

            if (self.audioObject.getStatus() == AudioStatus.NOFILE):
                self.audioObject.playAudio()
            else:
                self.audioObject.stopAudio()
                self.audioObject.playAudio()
            self.update_observers(
                "NewMetaData",
                arg1=self.path,
                arg2=self.metaDataList[self.db.getSelection()])

        def startUpdateStatusThread(self):
            """
            Starts a thread that updates the reproduction status by polling to the vlc lib.
            """

            self.audioObject.startUpdateStatusThread()
            self.update_observers(
                "NewMetaData",
                arg1=self.path,
                arg2=self.metaDataList[self.db.getSelection()])

        def nextTrack(self):
            """
            Switch to the next track of the list.
            """

            self.audioObject.stopAudio()
            if (self.db.getSelection() + 1 < len(self.pathFiles)):
                self.db.setSelection(self.db.getSelection() + 1)
            else:
                self.db.setSelection(0)

            self.loadAudio()

        def previousTrack(self):
            """
            Switch to the previous track of the list.
            """

            self.audioObject.stopAudio()
            if (self.db.getSelection() - 1 >= 0):
                self.db.setSelection(self.db.getSelection() - 1)
            else:
                self.db.setSelection(len(self.pathFiles) - 1)
            self.loadAudio()

        def nextTrackEvent(self):
            """
            Manages the automatic change to the next track notifying to the GUI.
            """

            if (self.db.getSelection() + 1 < len(self.pathFiles)):
                self.db.setSelection(self.db.getSelection() + 1)
            else:
                self.db.setSelection(0)
            self.path = self.pathFiles[self.db.getSelection()]
            self.update_observers(
                "NewMetaData",
                arg1=self.path,
                arg2=self.metaDataList[self.db.getSelection()])

        def pause(self):
            """
            Pauses the audio.
            """

            self.audioObject.pauseAudio()
            self.update_observers("AudioPaused", arg1=None, arg2=None)

        def resume(self):
            """
            Resumes a paused audio.
            """

            self.audioObject.resumeAudio()
            self.update_observers("AudioResumed", arg1=None, arg2=None)

        def changeAudioSecond(self, second):
            """
            Changes the current reproduction second to another second.

            :param second: New current second.
            """

            self.audioObject.changeAudioSecond(second)

        def updateReproductionSecondEvent(self, second):
            """
            Send an event to the observers when the current reproduction second changes.
            """

            self.update_observers("UpdateReproductionSecond",
                                  arg1=second,
                                  arg2=None)

        def initRadio(self):
            """
            Initialize the radio module if it's stopped.
            """

            if self.playingRadio == False:
                #STOP the reproduction of Audio
                if self.audioObject.getStatus() != AudioStatus.NOFILE:
                    self.audioObject.stopAudio()

                #Init the radio
                if (self.currentFMStation == None):
                    self.currentFMStation = 92.3
                self.update_observers("UpdateCurrentFMFrequency",
                                      arg1=self.currentFMStation,
                                      arg2=None)
                self.startGUICoolDown(3)
                self.SI4703.initRadio()
                self.SI4703.setVolume(15)
                self.SI4703.setChannel(self.currentFMStation)
                self.playingRadio = True

        def stopRadio(self):
            """
            Stops the radio module if it's working.
            """

            if self.playingRadio == True:
                self.SI4703.stopRadio()
                self.playingRadio = False

        def setCurrentFMFrequency(self, frequency):
            """
            Changes the tuned frequency of the radio module.

            :param frequency: New frequency to tune.
            """

            if (frequency >= 87.5 and frequency <= 108):
                self.currentFMStation = frequency
                self.update_observers("UpdateCurrentFMFrequency",
                                      arg1=self.currentFMStation,
                                      arg2=None)
                self.SI4703.setChannel(self.currentFMStation)

        def getCurrentFMFrequency(self):
            """
            Returns the current tuned frequency.

            :return currentFMStation: Current tuned frequency.
            """

            return self.currentFMStation

        def getCurrentFMStationName(self):
            """
            Returns the name of the FM station (actually the frequency as string)

            :return currentFMStation: Current tuned frequency as string.
            """

            return str(self.currentFMStation)

        def updateRadioObservers(self):
            """
            Update all the observers related to the radio module.
            """

            self.update_observers("UpdateCurrentFMFrequency",
                                  arg1=self.currentFMStation,
                                  arg2=None)
            self.update_observers("UpdateRadioChannelData",
                                  arg1=None,
                                  arg2=None)

        def nextFrequency(self):
            """
            Changes to the next frequency.
            """

            if (self.currentFMStation >= 87.5 and self.currentFMStation < 108):
                self.currentFMStation = round(self.currentFMStation + 0.1, 2)
                self.update_observers("UpdateCurrentFMFrequency",
                                      arg1=self.currentFMStation,
                                      arg2=None)

        def previousFrequency(self):
            """
            Changes to the previous frequency.
            """

            if (self.currentFMStation > 87.5 and self.currentFMStation <= 108):
                self.currentFMStation = round(self.currentFMStation - 0.1, 2)
                self.update_observers("UpdateCurrentFMFrequency",
                                      arg1=self.currentFMStation,
                                      arg2=None)

        def startChangeFrequencyThread(self):
            """
            Starts the thread launched when we change between frequencies manually.
            """

            if (self.threadController.getChangeFrequencyThread() != None):
                self.threadController.getChangeFrequencyThread().stop()
                self.threadController.setChangeFrequencyThread(None)

            self.changeFrequencyThread = ChangeFrequencyThread(
                0.5, self.currentFMStation, self.setCurrentFMFrequency,
                self.startGUICoolDown)
            self.threadController.setChangeFrequencyThread(
                self.changeFrequencyThread)
            self.threadController.getChangeFrequencyThread().start()

        def seekUp(self):
            """
            Seek up a new FM station.
            """

            self.SI4703.seekUp(self.notifyController)

        def seekDown(self):
            """
            Seek down a new FM station.
            """

            self.SI4703.seekDown(self.notifyController)

        def setGUICoolDown(self, state):
            """
            Sets the status of the cooldown of the GUI.

            :param state: State of the cooldown.
            """

            if (state == False):
                self.update_observers("CoolDownEnded", arg1=None, arg2=None)
            self.GUICoolDown = state

        def getGUICoolDown(self):
            """
            Returns the state of the cooldown of the GUI.

            :return GUICoolDown: Status of the cooldown of the GUI.
            """

            return self.GUICoolDown

        def startGUICoolDown(self, ms=0.5):
            """
            Starts the cooldown of the GUI when the radio module is busy.

            :param ms:  Duration of the cooldown.
            """

            self.update_observers("CoolDownStarted", arg1=None, arg2=None)
            buttonCoolDownThread = ButtonCoolDownThread(
                ms, self.setGUICoolDown)
            buttonCoolDownThread.start()

        def getPlayingRadio(self):
            """
            Returns if the radio is being played or not.

            :return playingRadio: Status of the radio.
            """

            return self.playingRadio

        def getStatus(self):
            """
            Returns the current status of the reproduction. See AudioStatus class.

            :return: Current status.
            """

            return self.audioObject.getStatus()

        def notifyController(self, notify, var=0):
            """
            Method that other classes uses to notifie to the AudioController of the changes.

            :param notify: Kind of notification.
            :param var: Attached var.
            """

            if (notify == "nextTrack"):
                self.nextTrackEvent()
            elif (notify == "updateReproductionSecond"):
                self.updateReproductionSecondEvent(var)
            elif (notify == "endOfList"):
                self.nextTrack()
            elif (notify == "seekFrequencyChanged"):
                self.currentFMStation = var
                self.update_observers("UpdateCurrentFMFrequency",
                                      arg1=self.currentFMStation,
                                      arg2=None)

        def __str__(self):
            return repr(self) + self.val