#!/usr/bin/env python
import argparse # optparse is deprecated
from itertools import islice # slicing for iterators
from nltk.tokenize import word_tokenize
from nltk.stem import PorterStemmer
from nltk.corpus import wordnet as wn
import numpy as np

ps = PorterStemmer()

def preprocess(sentence):
    i = 0
    length = len(sentence)

    while i<length:
        if not sentence[i].isalnum():
            del sentence[i]
            i -= 1
            length -= 1
        i += 1

    return sentence

def bleu_single_sentence(h, ref, synonyms):

    bp = 1.0

    if len(ref)>=len(h):
        if len(h)!=0:
            bp = np.exp(1-float(len(ref))/float(len(h)))
        else:
            return 0.0

    n_gram_data = [1.0,1.0,1.0,1.0]

    h_stemmed = [ps.stem(word) for word in h]
    ref_stemmed = [ps.stem(word) for word in ref]
    # ref_stemmed_1_gram_set = set(ref_stemmed)
    # ref_stemmed_2_gram_set = set([tuple(ref_stemmed[i:i+2]) for i in range(len(ref_stemmed)-1)])
    # ref_stemmed_3_gram_set = set([tuple(ref_stemmed[i:i+3]) for i in range(len(ref_stemmed)-2)])
    # ref_stemmed_4_gram_set = set([tuple(ref_stemmed[i:i+4]) for i in range(len(ref_stemmed)-3)])

    # synonyms = list()

    # for i in range(len(ref)):
    #     synonyms.append(set())
    #     synsets = wn.synsets(ref[i])
    #     for ss in synsets:
    #         for lemma in ss.lemmas():
    #             if lemma.name() not in synonyms[-1]:
    #                 synonyms[-1].add(lemma.name())

    # 1-gram
    if len(h)>=1:
	    for i in range(len(h)):
	    	flag = 0
	    	for j in range(len(ref)):
		        if h_stemmed[i] in ref_stemmed[j] or h[i] in synonyms[j]:
		            n_gram_data[0] += 1
		            flag = 1
		            break
			if flag == 1:
				continue
	    n_gram_data[0] = float(n_gram_data[0])/float(len(h)+1)

    # 2-gram
    if len(h)>=2:
	    for i in range(len(h)-1):
	    	flag = 0
	    	for j in range(len(ref)-1):
	        	if h_stemmed[i] in ref_stemmed[j] or h[i] in synonyms[j]:
	        		if h_stemmed[i+1] in ref_stemmed[j+1] or h[i+1] in synonyms[j+1]:
	        			n_gram_data[1] += 1
	        			flag = 1
	            		break
			if flag == 1:
				continue
	    n_gram_data[1] = float(n_gram_data[1])/float(len(h)-1+1)

    # 3-gram
    if len(h)>=3:
	    for i in range(len(h)-2):
	    	flag = 0
	        for j in range(len(ref)-2):
	        	if h_stemmed[i] in ref_stemmed[j] or h[i] in synonyms[j]:
	        		if h_stemmed[i+1] in ref_stemmed[j+1] or h[i+1] in synonyms[j+1]:
	        			if h_stemmed[i+2] in ref_stemmed[j+2] or h[i+2] in synonyms[j+2]:
							n_gram_data[2] += 1
							flag = 1
		            		break
			if flag == 1:
				continue
	    n_gram_data[2] = float(n_gram_data[2])/float(len(h)-2+1)

    # 4-gram
    if len(h)>=4:
	    for i in range(len(h)-3):
	    	flag = 0
	        for j in range(len(ref)-3):
	        	if h_stemmed[i] in ref_stemmed[j] or h[i] in synonyms[j]:
	        		if h_stemmed[i+1] in ref_stemmed[j+1] or h[i+1] in synonyms[j+1]:
	        			if h_stemmed[i+2] in ref_stemmed[j+2] or h[i+2] in synonyms[j+2]:
	        				if h_stemmed[i+3] in ref_stemmed[j+3] or h[i+3] in synonyms[j+3]:
								n_gram_data[3] += 1
								flag = 1
			            		break
			if flag == 1:
				continue
	    n_gram_data[3] = float(n_gram_data[3])/float(len(h)-3+1)

    prod = np.array(n_gram_data).prod()

    return bp*np.power(prod, 0.25)

def meteor_evaluator(h, ref, synonyms):

    h_stemmed = [ps.stem(word) for word in h]
    ref_stemmed = [ps.stem(word) for word in ref]

    matches = 0
    rdict = dict()
    is_matched_word = [0 for word in ref_stemmed]

    synonyms = list()

    for i in range(len(ref_stemmed)):
        if rdict.get(ref_stemmed[i], None)==None:
            rdict[ref_stemmed[i]] = []
        rdict[ref_stemmed[i]].append(i)

    for i in range(len(ref)):
        synonyms.append(set())
        synsets = wn.synsets(ref[i])
        for ss in synsets:
            for lemma in ss.lemmas():
                if lemma.name() not in synonyms[-1]:
                    synonyms[-1].add(lemma.name())

    for word in h_stemmed:
        if rdict.get(word, None)!=None:
            is_matched_word[rdict[word][0]] = 1
            matches += 1

            del rdict[word][0]
            if len(rdict[word])==0:
                del rdict[word]
        else:
            for i in range(len(synonyms)):
                if is_matched_word[i]==0:
                    if word in synonyms[i]:
                        is_matched_word[i] = 1
                        break

    if matches==0:
        return 0.0

    chunks = 0
    prev_char = None
    curr_char = None

    for i in range(len(is_matched_word)):
        prev_char = curr_char
        curr_char = is_matched_word[i]

        if prev_char!=curr_char and curr_char==1:
            chunks += 1

    penalty = 0.5*(float(chunks)/float(matches))
    precision = float(matches)/float(len(h_stemmed))
    recall = float(matches)/float(len(ref_stemmed))
    alpha = 0.8

    return (float(precision*recall)/float(alpha*precision + (1-alpha)*recall))*(1.0 - penalty)

def get_synonyms(ref):

	synonyms = list()

	for i in range(len(ref)):
		synonyms.append(set())
		synsets = wn.synsets(ref[i])
		for ss in synsets:
			for lemma in ss.lemmas():
				if lemma.name() not in synonyms[-1]:
					synonyms[-1].add(lemma.name())

	return synonyms
 
def main():
    parser = argparse.ArgumentParser(description='Evaluate translation hypotheses.')
    parser.add_argument('-i', '--input', default='data/hyp1-hyp2-ref',
            help='input file (default data/hyp1-hyp2-ref)')
    parser.add_argument('-n', '--num_sentences', default=None, type=int,
            help='Number of hypothesis pairs to evaluate')
    # note that if x == [1, 2, 3], then x[:None] == x[:] == x (copy); no need for sys.maxint
    opts = parser.parse_args()
 
    # we create a generator and avoid loading all sentences into a list
    def sentences():
        with open(opts.input) as f:
            for pair in f:
                yield [word_tokenize(sentence.decode("utf-8").strip().lower()) for sentence in pair.split(' ||| ')]
 
    # note: the -n option does not work in the original code
    for h1, h2, ref in islice(sentences(), opts.num_sentences):
        ref = preprocess(ref)
        h1 = preprocess(h1)
        h2 = preprocess(h2)
        synonyms = get_synonyms(ref)
        h1_match = 0.58*meteor_evaluator(h1, ref, synonyms) + 0.42*bleu_single_sentence(h1, ref, synonyms)
        h2_match = 0.58*meteor_evaluator(h2, ref, synonyms) + 0.42*bleu_single_sentence(h2, ref, synonyms)
        print(1 if h1_match > h2_match else # \begin{cases}
                (0 if h1_match == h2_match
                    else -1)) # \end{cases}
 
# convention to allow import of this file as a module
if __name__ == '__main__':
    main()
