#!/usr/bin/python
# -*- coding: utf-8*-

import regex, traceback, time
from threading import Thread, RLock
from collections import OrderedDict
from calibre.gui2 import error_dialog, info_dialog
from calibre.ebooks.oeb.polish.container import OEB_DOCS, OEB_STYLES
from calibre.ebooks.oeb.base import NCX_MIME

from calibre_plugins.typex.utils import log, anon


class SearchFunctions(object):

    def __init__(self, parent):
        self.parent = parent
        self.obj = parent.obj
        self.json_func = parent.json_func
        self.searches = []
        self.key_file_map = {}
        self.key_search_map = OrderedDict()

    def searches_splitter(self):
        json_reg = regex.compile(r'(^\[#?\w*?\])')
        i = -1
        for d in self.obj['searches']:
            if d['name'].startswith('<=='):
                # section start
                self.searches.append([])
                i += 1
                self.searches[i].append(d)
            elif d['name'].startswith('___'):
                continue
            elif json_reg.match(d['name']):
                # regex in the section
                self.searches[i].append(d)
                self.key_search_map[json_reg.match(d['name']).group()] = d
            else :
                # bad json
                info_dialog(None, _('Erreur'),
                    _('La recherche rencontrée est mal nommée, elle sera ignorée. Cliquez sur "Afficher les détails" pour plus d\'info'),
                    det_msg=d['name'] + ' : ' + d['find'], show=True)
        return self.searches, self.key_search_map

    def qualify_key(self, key):
        search = self.key_search_map[key]
        kmode = search['mode']
        kflags = regex.VERSION1 | regex.WORD | regex.FULLCASE | regex.MULTILINE | regex.UNICODE
        if not search['case_sensitive']:
            kflags |= regex.IGNORECASE
        if search['dot_all']:
            kflags |= regex.DOTALL
        kfind = str(search['find'])
        file_list = self.get_file_list(search)
        repl = search['replace']
        kname = search['name']
        return kmode, kflags, kfind, file_list, repl, kname

    def get_file_list(self, reg):
        container = self.parent.current_container
        nav = self.parent.tb.nav
        if '[CSS]' in reg['name']:
            return [name for name, mime in container.mime_map.items() if mime in OEB_STYLES]
        if '[NCX]' in reg['name']:
            fl = [name for name, mime in container.mime_map.items() if mime in NCX_MIME]
            if self.parent.tb.version == 3 :
                fl.append(nav)
            return fl
        fl = [name for name, mime in container.mime_map.items() if mime in OEB_DOCS]
        if self.parent.tb.version == 3 :
            fl.remove(nav)
        return fl

    def regexcounter(self, key, lock):
        start = time.time()
        count_time = 0
        self.count_cache = {}
        kmode, kflags, kfind, file_list, repl, kname = self.qualify_key(key)
        matchedfiles = set()
        total = 0
        reg = r'{}'.format(kfind)
        pat = regex.compile(kfind, flags=kflags)

        def function_replace(match, file):
            if kmode == 'function':
                f = self.parent.fdict[repl]
                f.init_env()
                f.context_name = file
                return f(match)
            else :
                return match.expand(repl)

        for file in file_list :
            num = pos = 0
            with lock:
                if self.parent.cache.get(file, False):
                    data = self.parent.cache[file]
                else:
                    data = self.parent.current_container.raw_data(file, decode=True)
                self.parent.cache[file] = data

            match = pat.search(data, pos=pos)

            while True:
                if match :
                    pos = match.end()
                    rep = function_replace(match, file)
                    if rep != match.group() or '[C]' in kname :
                        num += 1
                        # log('remplacement de {} par {} dans {}, position {}'.format(match.group(), match.expand(repl), file, match.start()))
                        matchedfiles.add(file)
                    match.detach_string()
                    match = pat.search(data, pos=pos)
                    # log('newmatch {}'.format(match))
                else:
                    break
            total += num
            end = time.time()
            count_time = (end - start) * 1000
        # log(key, count_time)
        return kname, total, count_time, key, sorted(list(matchedfiles))


class CountWorker(Thread):

    def __init__(self, obj, keys_queue, result_queue):
        super(CountWorker, self).__init__()  # obj, keys_queue, result_queue)
        self.keys_queue = keys_queue
        self.result_queue = result_queue
        self.obj = obj
        self.obj.count_cache = {}
        self.lock = RLock()

    def run(self):
        while True:
            key = self.keys_queue.get()
            if key is None:
                break
            try :
                res = self.obj.regexcounter(key, self.lock)
            except :
                log('erreur {}'.format(traceback.print_exc()))
                raise
            self.result_queue.put(res)
            self.keys_queue.task_done()

