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

# In[ ]:


import os
import sys
import csv
import re
import torch
from collections import Counter
#from nltk.stem import WordNetLemmatizer
from nltk.corpus import wordnet as wn
from nltk.corpus import stopwords
from sentence_transformers import util
import requests

#lemmatizer = WordNetLemmatizer()
stops = set(stopwords.words('english'))
for e in ['like', 'much', 'man', 'days']:
    stops.add(e)



def return_atomic(path):
    """return KB sentences"""
    atomic_set = []
    with open(path, 'r', encoding='utf-8') as f:
        for line in f:
            line = line.strip()
            atomic_set.append(line)
    return atomic_set


def common_elements(list1, list2):
    """find common words between two lists of splitted sentences"""
    return [element for element in list1 if element in list2]



re_dict = {"each other" : "each_other",
        "," : "",
        "'s" : ""}

def multiple_replace(dict, text):
    regex = re.compile("(%s)" % "|".join(map(re.escape, dict.keys())))
    return regex.sub(lambda x: dict[x.string[x.start():x.end()]], text)

def lemmatize_words(words_list):
    lemmatized = []
    for word in words_list:
        if wn.morphy(word, wn.VERB) != None:
            lemmatized.append(wn.morphy(word, wn.VERB))
        elif wn.morphy(word, wn.NOUN) != None:
            lemmatized.append(wn.morphy(word, wn.NOUN))
        else:
            lemmatized.append(word.lower())
    return lemmatized

def remove_stopwords(list1):
    for stopword in stops:
        try:
            list1.remove(stopword)
        except ValueError:
            pass
    return list1




def return_keywords(premise, hypothesis):
    """return figurative expression and literal expression"""
    p_split = (multiple_replace(re_dict, premise)).split()
    h_split = (multiple_replace(re_dict, hypothesis)).split()
    count = Counter(p_split + h_split)
    keywords = remove_stopwords([item[0] for item in count.items() if item[1] == 1])

    fig_exp = [re.sub(r'each_other', 'each other', word) for word in keywords if word in p_split]
    lit_exp = [re.sub(r'each_other', 'each other', word) for word in keywords if word in h_split]
    return fig_exp, lit_exp, " ".join(fig_exp) + ' and ' + " ".join(lit_exp)



def return_candidates(candidates, premise, hypothesis, fig_exp, lit_exp):
    final = []
    premise = multiple_replace(re_dict, premise)
    hypothesis = multiple_replace(re_dict, hypothesis)

    p_keywords = remove_stopwords(list(set(lemmatize_words(premise.split())))) #include other key-words from premise

    for candidate in candidates:
        split_sent = lemmatize_words((multiple_replace(re_dict, candidate)).split())
        if len(common_elements(split_sent, p_keywords)) >= 1 and len(common_elements(split_sent, lit_exp)) >= 1:
            kb_sent = re.sub('cannot', "can't", candidate)
            final.append(kb_sent)
    return final



def semantic_sim(candidates, key_exp, model, k):
    """
    measure semantic similarity between candidate KB sentence and key expression
    
    return : KB sentences with Top k semantic similarity (cosine_score, KB sentence)
    """
    cosine_list = []
    embeddings1 = model.encode(key_exp, convert_to_tensor=True)
    for candidate in candidates:
        embeddings2 = model.encode(candidate, convert_to_tensor=True)
        cosine_score = util.cos_sim(embeddings1, embeddings2)
        cosine_float = cosine_score[0].cpu().numpy()[0]
        cosine_list.append((cosine_float, candidate))
    cosine_list.sort(reverse=True)
    return cosine_list[:k]



def return_new_premise(premise, topK, limit):
    """
    inject matched KB sentences that have cosine scores above the limit

    return : premise combined with matched KB sentences
    """
    if len(topK) == 0:
        new_premise = premise
    else:
        new_premise = premise
        for a_tuple in topK:
            if a_tuple[0] >= float(limit):
                kb_sent = a_tuple[1]
                new_premise += kb_sent + '. '
    return new_premise



def return_synonyms_cn(h_exp):
    word = str(h_exp)[2:-2]
    obj = requests.get('http://api.conceptnet.io/c/en/'+word).json()
    synonyms = []
    for dic in obj['edges']:
        rel, start = dic['rel'], dic['start']
        if rel['label'] == 'RelatedTo' and start['language'] == 'en':
            synonyms.append(start['label'])
        if rel['label'] == 'Synonym' and start['language'] == 'en':
            synonyms.append(start['label'])
    synonyms = list(set(synonyms))
    try:
        synonyms.remove(word)
    except ValueError:
        pass
    return synonyms


def return_synonyms_wn(h_exp):
    word = str(h_exp)[2:-2]
    a_list, synonyms = [], []
    for syn in wn.synsets(word, pos=wn.ADJ):
        a_list += syn.lemma_names()
    for candidate in list(set(a_list)):
        if '_' in candidate:
            synonyms.append(re.sub('_',' ',candidate))
        else:
            synonyms.append(candidate)
    try:
        synonyms.remove(word)
    except ValueError:
        pass
    return synonyms


def return_hypotheses(hypothesis, synonyms, h_exp):
    """return candidate hypotheses with replaced synonyms"""
    output = []
    for syn in synonyms:
        new_hypothesis = re.sub(str(h_exp)[2:-2], syn, hypothesis)
        output.append(new_hypothesis)
    return output




