# -*- coding: utf-8 -*-
"""
Module d'analyse des fichiers de log minwii.

$Id$
$URL$
"""

from minwii.logfilereader import LogFileReader
from pprint import pprint
from minwii.musicxml import musicXml2Song
from minwii.globals import PLAYING_MODES
from statlib import stats
from datetime import timedelta
from xml.etree import ElementTree
import os.path

PLAYING_MODES = dict(PLAYING_MODES)

DEFAULT_STATS = (#'geometricmean',
                 ('harmonicmean', 'Moyenne harmonique'),
                 ('mean', 'Moyenne  '),
                 ('median', 'Médiane'),
                 #'medianscore',
                 #'mode',
                 #'moment',
                 ('variation', 'Variation'),
                 #'skew',
                 ('kurtosis', 'Kurtosis'),
                 #'itemfreq',
                 #'histogram',
                 #'cumfreq',
                 #'relfreq',
                 )

def statsresults(m) :
    def computeList(self):
        l = m(self)
        results = []
        for name, label in DEFAULT_STATS :
            results.append('%s : %s' % (label, getattr(stats, name)(l)))
        return '\n'.join(results)
    computeList.__name__ = m.__name__
    computeList.__doc__ = m.__doc__
    return computeList

def timebased(m) :
    m.timebased = True
    return m

class LogFileAnalyser(LogFileReader) :

    POSSIBLE_ANALYSES = {'BEGINNER' : ('songDuration',
                                       'playingDuration',
                                       'noteEndNoteOnLatency',
                                       'realisationRate')
                                       
                        ,'EASY'     : ('songDuration',
                                       'playingDuration',
                                       'noteEndNoteOnLatency',
                                       'realisationRate',
                                       'missCount',
                                       'getMissPerTimeFrame')
                                       
                        ,'NORMAL'   : ('songDuration',
                                       'playingDuration',
                                       'realisationRate',
                                       'missCount',
                                       'getMissPerTimeFrame')
                                       
                        ,'ADVANCED' : ('songDuration',
                                       'playingDuration',
                                       'realisationRate',
                                       'missCount',
                                       'getMissPerTimeFrame')
                                       
                        ,'EXPERT'   : ('songDuration',
                                       'playingDuration',
                                       'realisationRate',
                                       'missCount',
                                       'getMissPerTimeFrame')
                        }
    
    def analyse(self) :
        results = []
        
        try :
            self.mode = mode = self.getMode()
            results.append(('Mode de jeu', PLAYING_MODES.get(mode, mode), False))

            self.songTitle = LogFileAnalyser.getSongTitle(self.getSongFile())
            results.append(('Chanson', self.songTitle, False))

            for name in self.POSSIBLE_ANALYSES[mode] :
                meth = getattr(self, name)
                results.append( (meth.__doc__, meth(), getattr(meth, 'timebased', False)) )
        except :
            raise
        
        return results
    
    @staticmethod
    def getSongTitle(file) :
        if os.path.exists(file) :
            it = ElementTree.iterparse(file, ['start', 'end'])
            creditFound = False

            for evt, el in it :
                if el.tag == 'credit' :
                    creditFound = True
                if el.tag == 'credit-words' and creditFound:
                    return el.text
                if el.tag == 'part-list' :
                    # plus de chance de trouver un titre
                    return os.path.basename(file)
        else :
            return os.path.basename(file)

    def _toTimeDelta(self, milliseconds) :
        duration = milliseconds / 1000.
        duration = int(round(duration, 0))
        return str(timedelta(seconds=duration))
    
    def playingDuration(self) :
        'Temps de jeu'
        #retourne la durée écoulée entre le premier et de dernier message
        #de type événement : correspond à la durée d'interprétation.
        
        last = self.getLastEventTicks()
        first = self.getFirstEventTicks()
        return self._toTimeDelta(last - first)
        
    
    def songDuration(self) :
        'Durée de référence de la chanson'
        #retourne la durée de référence de la chanson
        #en prenant en compte le tempo présent dans la transcription
        #et en effectuant toutes les répétitions des couplets / refrains.
        
        songFile = self.getSongFile()
        song = musicXml2Song(songFile)
        duration = song.duration
        return self._toTimeDelta(duration)
    
    @statsresults
    def noteEndNoteOnLatency(self) :
        'Réactivité'
        eIter = self.getEventsIterator()
        latencies = []
        lastnoteEndT = 0
        
        for ticks, eventName, message in eIter :
            if eventName == 'NOTEEND':
                lastnoteEndT = ticks
            if eventName == 'NOTEON' and lastnoteEndT :
                latencies.append(ticks - lastnoteEndT)
        
        return latencies
    
    def noteOnCount(self) :
        "retourne le nombre d'événements NOTEON"
        
        eIter = self.getEventsIterator()
        cpt = 0        

        for ticks, eventName, message in eIter :
            if eventName == 'NOTEON' :
                cpt = cpt + 1
        
        return cpt
    
    def realisationRate(self) :
        'Taux de réalisation'
        #taux de réalisation en nombre de note
        #peut être supérieur à 100 % car la chanson
        #boucle à l'infini.
        
        songFile = self.getSongFile()
        song = musicXml2Song(songFile)
        songNoteCpt = 0
        for note, verseIndex in song.iterNotes() :
            songNoteCpt = songNoteCpt + 1
        
        return round(self.noteOnCount() / float(songNoteCpt) * 100, 1)
    
    def missCount(self) :
        "Nombre d'erreurs"
        eIter = self.getEventsIterator()
        miss = 0
        if self.mode in ('EASY', 'NORMAL') :
            catchColUp = False
            for ticks, eventName, message in eIter :
                if eventName == 'COLDOWN' :
                    colState = message.split(None, 2)[1]
                    colState = colState == 'True'
                    if colState :
                        catchColUp = False
                        continue
                    else :
                        catchColUp = True
                elif eventName == 'NOTEON' :
                    catchColUp = False
                elif eventName == 'COLUP' and catchColUp :
                    miss = miss + 1
        else :
            for ticks, eventName, message in eIter :
                if eventName == 'COLDOWN' :
                    colState = message.split(None, 2)[1]
                    colState = colState == 'True'
                    if not colState :
                        miss = miss + 1
        
        return miss
    
    @timebased
    def getMissPerTimeFrame(self, timeFrame=10000) :
        "Nombre d'erreurs en fonction du temps"
        eIter = self.getEventsIterator()
        firstTicks = self.getFirstEventTicks()
        frames = [0]

        if self.mode in ('EASY', 'NORMAL') :
            catchColUp = False
            for ticks, eventName, message in eIter :
                if ticks - firstTicks > timeFrame :
                    firstTicks = ticks
                    frames.append(0)
                    
                if eventName == 'COLDOWN' :
                    colState = message.split(None, 2)[1]
                    colState = colState == 'True'
                    if colState :
                        catchColUp = False
                        continue
                    else :
                        catchColUp = True
                elif eventName == 'NOTEON' :
                    catchColUp = False
                elif eventName == 'COLUP' and catchColUp :
                    frames[-1] = frames[-1] + 1
        else :
            for ticks, eventName, message in eIter :
                if ticks - firstTicks > timeFrame :
                    firstTicks = ticks
                    frames.append(0)
                
                if eventName == 'COLDOWN' :
                    colState = message.split(None, 2)[1]
                    colState = colState == 'True'
                    if not colState :
                        frames[-1] = frames[-1] + 1
        
        return frames
                
        
        
        

def main() :
    from optparse import OptionParser
    usage = "%prog logfile"
    op = OptionParser(usage)
    options, args = op.parse_args()
    if len(args) != 1 :
        op.error("incorrect number of arguments")


    lfa = LogFileAnalyser(args[0])
    pprint(lfa.analyse())

if __name__ == "__main__" :
    from os.path import realpath, sep
    import sys
    minwiipath = realpath(__file__).split(sep)
    minwiipath = minwiipath[:-2]
    minwiipath = sep.join(minwiipath)
    sys.path.insert(1, minwiipath)
    main()
