#!/usr/bin/python
# -*- coding: utf8 -*-
import os

import pdb
from os import walk
from re import compile
from threading import Thread

from tkinter import *
from tkinter.constants import *
from tkinter import filedialog
DEFAULT_BUFSIZE = 8*1024


#
# Thread de recherche
#
class SearchThread(Thread):
    """ Thread de recherche de texte
    """
    def __init__(self, path, text, percent, callback):
        Thread.__init__(self)
        self.path = path
        self.text = text
        self.percent = percent
        self.callback = callback
        self.buffer = DEFAULT_BUFSIZE
        self.running = False
        self.exts = ('.txt', '.py','.php')

    def stop(self):
        """Arrête le thread."""
        self.callback("Arreter la recherche !")
        if self.running:
            self.running = False



    def run(self):
        """Méthode lancée par start()."""
        found = 0
        self.running = True
        if os.path.isdir(self.path):
            for root, reps, files in walk(self.path):
                if not self.running:
                    break
                for index, file_ in enumerate(files):
                    ext = os.path.splitext(file_)[-1]
                    if ext not in self.exts:
                        continue
                    if not self.running:
                        break
                    self.percent('Recherche %d/%d: %s/%s' \
                                   % (index, len(files), root, file_))
                    fullname = os.path.join(root, file_)
                    if self.text_in_file(fullname, self.text):
                        self.callback('%s' % fullname)
                        found += 1
        else:
             self.callback("%s n'est pas un dossier" % self.path)
        if found == 0:
            self.callback('Aucun fichier')
        self.percent('%d fichiers(s) trouvé(s)' % found)

    def text_in_file(self, file_, text):
        """Renvoie vrai si le file_ contient le text.

        Lis le text par morceaux pour limiter la taille
        mémoire."""
        ctext = compile('[%s]' % text)
        try:
            f = open(file_, 'r', buffering=self.buffer)
        except IOError:     # en cas de pb d'accès (droits, etc.)
            return False

        with f:
            line = None
            while line != '':
                if not self.running:
                    return False
                line = f.readline(self.buffer)
                if ctext.match(line) is not None:
                    return True
        return False

#
# Frames
#
class FramePath(Frame):
    def __init__(self, root):
        Frame.__init__(self)
        label = Label(self, text="Chemin de recherche")
        label.pack(fill=X, expand=1)
        self.path = Entry(self, name="path")
        self.path.pack(fill=BOTH)
        self.path.insert(0, os.path.expanduser('~'))
        self.path.select_range(0, END)

class FrameText(Frame):
    def __init__(self, root):
        Frame.__init__(self)
        self.label = Label(self, text="Texte à rechercher")
        self.label.pack(fill=X, expand=1)
        self.text = Entry(self, name="text")
        self.text.pack(fill=BOTH)
        self.text.insert(0, '')
        self.text.select_range(0, END)

class FrameButton(Frame):
    def __init__(self, root):
        Frame.__init__(self)

    def create_elements(self, stop_command, search_command,
                              close_command,ask_directory):
        self.button = Button(self,text="Stop",
                             command=stop_command)
        self.button.pack(side=RIGHT, padx=5, pady=5)
        self.button = Button(self,text="Rechercher",
                             command=search_command)
        self.button.pack(side=RIGHT, padx=5, pady=5)
        self.button3 = Button(self,text="Fermer",
                              command=close_command)
        self.button3.pack(side=RIGHT)
        self.button4 = Button(self,text="Dossier ?",
                              command=ask_directory)
        self.button4.pack(side=RIGHT)

class FrameResult(Frame):
    def __init__(self, root):
        Frame.__init__(self)
        self.results_window = Listbox(self)
        self.resultats_ascenseur = Scrollbar(self, orient=VERTICAL,
                               command=self.results_window.yview)
        self.results_window.config(yscrollcommand=\
                                      self.resultats_ascenseur.set)

        self.results_window.pack(side=LEFT, expand=1, fill=BOTH)
        self.resultats_ascenseur.pack(side=RIGHT, fill=Y)



# Application
#
class Application(object):
    """ Frame contenant l'interface de recherche
    et d'affichage des résultats
    """
    def __init__(self):
        self._tk = Tk()

        # création des 4 frames
        options = {'expand': 1, 'fill': BOTH}
        self.add_frame('frm_path', FramePath, **options)
        self.add_frame('frm_text', FrameText, **options)

        self.add_frame('frm_bouton', FrameButton, **options)
        self.frm_bouton.create_elements(self.stop_search,
                                        self.search,
                                        self.close,self.askDirectory)

        self.add_frame('frm_result', FrameResult, **options)
        #, 'relief': RIDGE
        self.searcher = None

        # titre fenêtre application
        self._tk.wm_title('Recherche')
        self.dir_opt = options = {}
        options['initialdir'] = os.path.expanduser('~')
        options['mustexist'] = False
        options['parent'] = self._tk
        options['title'] = 'This is a title'

    def mainloop(self):
        self._tk.mainloop()

    def add_frame(self, name, class_, **pack_options):
        instance = class_(self._tk)
        setattr(self, name, instance)
        instance.pack(**pack_options)

    def _callback(self, msg):
        """Appelée par le thread."""
        self.frm_result.results_window.insert(END, msg)

    def _percent(self, msg):
        """ appelée par le thread """
        #self._tk.wm_title(msg)

    def askDirectory(self):
        """
        :return:
        """
        dirName = filedialog.askdirectory(**self.dir_opt)
        self.frm_path.path.delete(0, END)
        self.frm_path.path.insert(0,dirName)

    def search(self):
        """Lance une recherche."""
        self.stop_search()
        self.frm_result.results_window.delete(0, END)
        self.frm_result.results_window.insert(END, 'Lancement recherche :')
        path = self.frm_path.path.get()
        text = self.frm_text.text.get()

        # lance le thread de recherche
        self.searcher = SearchThread(path, text, self._percent,
                                     self._callback)
        self.searcher.start()

    def stop_search(self):
        """Stoppe une éventuelle recherche en cours."""
        if self.searcher is not None:
            self.searcher.stop()

    def close(self):
        """Demande de fermeture, arrêt d'une éventuelle recherche."""
        self.stop_search()
        self._tk.destroy()

if __name__ == '__main__':
    app = Application()
    app.mainloop()

