import re
import string
from multiprocessing import Pipe, Process
import fasttext
import unidecode
import pycountry

UNKNOWN_THRESHOLD = 0.8
ORIG_OVERRIDE_CONFIDENCE = 0.3
MODEL_PATH = "/nlp/projekty/officebot/trac-git/langid/train/fastText/lang.bin"

URL_FILTER = re.compile('<[^>]*>')
EMOJI_FILTER = re.compile(':[a-z\u00C0-\u017F_\+\-]*:')
BLANK_LINE_FILTER = re.compile('^\\s*$')
JOIN_FILTER = re.compile('(<.*>)* joined the channel')
NUMERIC_FILTER = re.compile("[0-9]+")
PLUS_FILTER = re.compile('\+')

class Preprocessor:
    def __init__(self):
        self.reglist = [ URL_FILTER, EMOJI_FILTER, JOIN_FILTER, BLANK_LINE_FILTER,
                NUMERIC_FILTER, PLUS_FILTER]
        self.truncate_short_text = True
        self.underline_for_space = False

    def preprocess(self, text, clear_punctuation=False, clear_emojis=True):
        for regex in self.reglist:
            if not clear_emojis and regex == EMOJI_FILTER:
                continue
            while True:
                newText = regex.sub(" ", text)
                if newText == text:
                    text = newText
                    break;
                text = newText
        if self.underline_for_space:
            text = text.replace('_', " ")
        if clear_punctuation:
            text = text.translate(text.maketrans(' ', ' ', string.punctuation))
        text = text.strip()
        if len(text) <= 5 and self.truncate_short_text:
            return ""
        return text.lower() if [i for i in text if i.isalnum()] else ""

class LangIdentCore:
    def __init__(self, debug=False):
        self.debug = debug
        self.preprocessor = Preprocessor()
        self.model = fasttext.load_model(MODEL_PATH)

    def classify_from_channel(self, channel, debug=False):
        result = dict()
        for message in channel:
            lang, confidence = self.classify_single(message)
            if lang not in result:
                result[lang] = 1
            result[lang] += 1
        top_lang = max(result, key=result.get)
        return result, top_lang


    def classify_single(self, message, debug=False):
        if isinstance(message, dict):
            message = message.get("text")
        preprocessed = self.preprocessor.preprocess(message.replace('\n', ' '), True)
        #preprocessed = message
        if not preprocessed:
            return "???", 0
        lang = self.model.predict(preprocessed)
        if "diacritics" in str(lang):
            message = unidecode.unidecode(message)
        prediction, confidence = lang
        prediction = prediction[0][9:]
        if not debug and confidence < UNKNOWN_THRESHOLD:
            prediction = "unknown"
        exceptions = {"czechwithoutdiacritics": "csd", "slovakwithoutdiacritics": "skd",
                      "greek": "gr", "unknown": "???"}
        if prediction in exceptions:
            iso_code = exceptions[prediction]
        else:
            iso_code = pycountry.languages.get(name=prediction).alpha_2
        return iso_code, confidence


def lang_loop(conn, debug=False):
    langident = LangIdentCore(debug)
    conn.send(True)
    while True:
        channel = conn.recv()
        if channel == True:
            continue
        typem, content = channel
        if typem == "channel":
            output = langident.classify_from_channel(content)
        else:
            output = langident.classify_single(content)
        conn.send(output)



class LanguageIdentifier:
    def __init__(self, debug=False):
        self._parent_conn, child_conn = Pipe()
        self._process = Process(target=lang_loop, args=(child_conn, debug))
        self._process.start()
        self.loaded = False


    def classify_from_channel(self, channel: "Channel"):
        if not self.loaded:
            return None, None
        self._parent_conn.send(("channel", channel))
        return self._parent_conn.recv()


    def classify_single(self, message):
        if not self.loaded:
            return None
        self._parent_conn.send(("message", message))
        return self._parent_conn.recv()


    def ready(self) -> bool:
        if self.loaded:
            return True
        if self._parent_conn.poll():
            status = self._parent_conn.recv()
            self.loaded = True
            return status
        return False


    def __del__(self):
        if self._process.is_alive():
            self._process.terminate()
