# -*- coding: utf-8 -*-
"""
Boîte de dialogue pour sélection des chansons.

$Id$
$URL$
"""

import pygame
from pygame.locals import K_RETURN
from pgu.gui import FileDialog
import pgu.gui.basic as basic
import pgu.gui.input as input
import pgu.gui.button as button
import pgu.gui.pguglobals as pguglobals
import pgu.gui.table as table
import pgu.gui.area as area
from pgu.gui.const import *
from pgu.gui.dialog import Dialog

import os
import tempfile
from xml.etree import ElementTree
from minwii.musicxml import musicXml2Song

INDEX_TXT = 'index.txt'

class FileOpenDialog(FileDialog):
    
    
    
    def __init__(self, path):
        cls1 = 'filedialog'
        if not path: self.curdir = os.getcwd()
        else: self.curdir = path
        self.dir_img = basic.Image(
            pguglobals.app.theme.get(cls1+'.folder', '', 'image'))
        td_style = {'padding_left': 4,
                    'padding_right': 4,
                    'padding_top': 2,
                    'padding_bottom': 2}
        self.title = basic.Label("Ouvrir un chanson", cls="dialog.title.label")
        self.body = table.Table()
        self.list = area.List(width=700, height=250)
        self.input_dir = input.Input()
        self.input_file = input.Input()
        self._current_sort = 'alpha'
        self._list_dir_()
        self.button_ok = button.Button("Ouvrir")
        self.button_sort_alpha = button.Button("A-Z")
        self.button_sort_alpha.connect(CLICK, self._set_current_sort_, 'alpha')
        self.button_sort_num = button.Button("0-9")
        self.button_sort_num.connect(CLICK, self._set_current_sort_, 'num')
        self.body.tr()
        self.body.td(basic.Label("Dossier"), style=td_style, align=-1)
        self.body.td(self.input_dir, style=td_style)
        self.body.td(self.button_sort_alpha)
        self.body.td(self.button_sort_num)
        self.body.tr()
        self.body.td(self.list, colspan=4, style=td_style)
        self.list.connect(CHANGE, self._item_select_changed_, None)
        #self.list.connect(CLICK, self._check_dbl_click_, None)
        self._last_time_click = pygame.time.get_ticks()
        self.button_ok.connect(CLICK, self._button_okay_clicked_, None)
        self.body.tr()
        self.body.td(basic.Label("Fichier"), style=td_style, align=-1)
        self.body.td(self.input_file, style=td_style)
        self.body.td(self.button_ok, style=td_style, colspan=2)
        self.value = None
        Dialog.__init__(self, self.title, self.body)
        
#        FileDialog.__init__(self, 
#                            title_txt="Ouvrir une chanson",
#                            button_txt="Ouvrir",
#                            path=path,
#                            )
#        self.list.style.width = 700
#        self.list.style.height = 250
    
    def _list_dir_(self):
        self.input_dir.value = self.curdir
        self.input_dir.pos = len(self.curdir)
        self.input_dir.vpos = 0
        dirs = []
        files = []
        try:
            for i in os.listdir(self.curdir):
                if i.startswith('.') : continue
                if os.path.isdir(os.path.join(self.curdir, i)): dirs.append(i)
                else: files.append(i)
        except:
            self.input_file.value = "Dossier innacessible !"

        dirs.sort()
        dirs.insert(0, '..')

        files.sort()
        for i in dirs:
            self.list.add(i, image=self.dir_img, value=i)
        
        xmlFiles = []
        for i in files:
            if not i.endswith('.xml') :
                continue
            filepath = os.path.join(self.curdir, i)
            xmlFiles.append(filepath)
            # self.list.add(FileOpenDialog.getSongTitle(filepath), value=i)
        
        if xmlFiles :
            printableLines = self.getPrintableLines(xmlFiles)
            for l in printableLines :
                self.list.add(l[0], value = l[1])
 
        self.list.set_vertical_scroll(0)
    
    def getPrintableLines(self, xmlFiles) :
        index = self.getUpdatedIndex(xmlFiles)

        printableLines = []
        for l in index :
            l = l.strip()
            l = l.split('\t')
            printableLines.append(('%s - %s / %s' % (l[2], l[3], l[4]), l[0]))
        
        return printableLines
    
    
    @staticmethod
    def getSongTitle(file) :
        it = ElementTree.iterparse(file, ['start', 'end'])
        creditFound = False
        title = os.path.basename(file)
        
        for evt, el in it :
            if el.tag == 'credit' :
                creditFound = True
            if el.tag == 'credit-words' and creditFound:
                title = el.text
                break
            if el.tag == 'part-list' :
                # au delà de ce tag : aucune chance de trouver un titre
                break
        return title
    
    @staticmethod
    def getSongMetadata(file) :
        metadata = {}
        metadata['title'] = FileOpenDialog.getSongTitle(file).encode('iso-8859-1')
        metadata['mtime'] = str(os.stat(file).st_mtime)
        metadata['file'] = os.path.basename(file)
        song = musicXml2Song(file)
        metadata['distinctNotes'] = len(song.distinctNotes)
        
        histo = song.intervalsHistogram
        coeffInter = reduce(lambda a, b : a + b,
                            [abs(k) * v for k, v in histo.items()])

        totInter = reduce(lambda a, b: a+b, histo.values())
        totInter = totInter - histo.get(0, 0)
        difficulty = int(round(float(coeffInter) / totInter, 0))
        metadata['difficulty'] = difficulty

        return metadata
    
    def getUpdatedIndex(self, xmlFiles) :
        indexTxtPath = os.path.join(self.curdir, INDEX_TXT)
        index = []
        
        if not os.path.exists(indexTxtPath) :
            musicXmlFound = False
            tmp = tempfile.TemporaryFile(mode='r+')
            for file in xmlFiles :
                try :
                    metadata = FileOpenDialog.getSongMetadata(file)
                    musicXmlFound = True
                except ValueError, e :
                    print e
                    if e.args and e.args[0] == 'not a musicxml file' :
                        continue
                
                line = '%(file)s\t%(mtime)s\t%(title)s\t%(distinctNotes)d\t%(difficulty)d\n' % metadata
                index.append(line)
                tmp.write(line)
            
            if musicXmlFound :
                tmp.seek(0)
                indexFile = open(indexTxtPath, 'w')
                indexFile.write(tmp.read())
                indexFile.close()
            tmp.close()
        else :
            indexedFiles = {}
            indexTxt = open(indexTxtPath, 'r')

            # check if index is up to date, and update entries if so.
            for l in filter(None, indexTxt.readlines()) :
                parts = l.split('\t')
                fileBaseName, modificationTime = parts[0], parts[1]
                filePath = os.path.join(self.curdir, fileBaseName)

                if not os.path.exists(filePath) :
                    continue

                indexedFiles[fileBaseName] = l
                currentMtime = str(os.stat(filePath).st_mtime)
                
                # check modification time missmatch
                if currentMtime != modificationTime :
                    try :
                        metadata = FileOpenDialog.getSongMetadata(filePath)
                        musicXmlFound = True
                    except ValueError, e :
                        print e
                        if e.args and e.args[0] == 'not a musicxml file' :
                            continue
                    
                    metadata = FileOpenDialog.getSongMetadata(filePath)
                    line = '%(file)s\t%(mtime)s\t%(title)s\t%(distinctNotes)d\t%(difficulty)d\n' % metadata
                    indexedFiles[fileBaseName] = line
            
            # check for new files.
            for file in xmlFiles :
                fileBaseName = os.path.basename(file)
                if not indexedFiles.has_key(fileBaseName) :
                    try :
                        metadata = FileOpenDialog.getSongMetadata(filePath)
                        musicXmlFound = True
                    except ValueError, e :
                        print e
                        if e.args and e.args[0] == 'not a musicxml file' :
                            continue
                
                    metadata = FileOpenDialog.getSongMetadata(file)
                    line = '%(file)s\t%(mtime)s\t%(title)s\t%(distinctNotes)d\t%(difficulty)d\n' % metadata
                    indexedFiles[fileBaseName] = line
            
            # ok, the index is up to date !
            
            index = indexedFiles.values()
            

        if self._current_sort == 'alpha' :
            def s(a, b) :
                da = desacc(a.split('\t')[2]).lower()
                db = desacc(b.split('\t')[2]).lower()
                return cmp(da, db)
                
        elif self._current_sort == 'num' :
            def s(a, b) :
                da = int(a.split('\t')[3])
                db = int(b.split('\t')[3])
                return cmp(da, db)
        else :
            s = cmp
        
        index.sort(s)
        return index
    
    def _set_current_sort_(self, arg) :
        self._current_sort = arg
        self.list.clear()
        self._list_dir_()
    
    def _check_dbl_click_(self, arg) :
        if pygame.time.get_ticks() - self._last_time_click < 300 :
            self._button_okay_clicked_(None)
        else :
            self._last_time_click = pygame.time.get_ticks()
    
    def event(self, e) :
        FileDialog.event(self, e)
        
        if e.type == CLICK and \
           e.button == 1 and \
           self.list.rect.collidepoint(e.pos) :
            self._check_dbl_click_(e)
        
        if e.type == KEYDOWN and e.key == K_RETURN :
            self._button_okay_clicked_(None)
            

# utils
from unicodedata import decomposition
from string import printable
_printable = dict([(c, True) for c in printable])
isPrintable = _printable.has_key

def _recurseDecomposition(uc):
    deco = decomposition(uc).split()
    fullDeco = []
    if deco :
        while (deco) :
            code = deco.pop()
            if code.startswith('<') :
                continue
            c = unichr(int(code, 16))
            subDeco = decomposition(c).split()
            if subDeco :
                deco.extend(subDeco)
            else :
                fullDeco.append(c)
        fullDeco.reverse()
    else :
        fullDeco.append(uc)
    
    fullDeco = u''.join(filter(lambda c : isPrintable(c), fullDeco))
    return fullDeco

def desacc(s, encoding='iso-8859-1') :
    us = s.decode(encoding, 'ignore')
    ret = []
    for uc in us :
        ret.append(_recurseDecomposition(uc))
    return u''.join(ret)