import tempfile
import unittest

import itertools
import nltk
from gensim import corpora
import numpy as np

from orangecontrib.text import preprocess
from orangecontrib.text.corpus import Corpus
from orangecontrib.text.preprocess import Preprocessor


def counted(f):
    def wrapped(*args, **kwargs):
        wrapped.calls += 1
        return f(*args, **kwargs)
    wrapped.calls = 0
    return wrapped


class PreprocessTests(unittest.TestCase):
    sentence = "Human machine interface for lab abc computer applications"

    def setUp(self):
        self.corpus = Corpus.from_file('deerwester')

    def test_string_processor(self):
        class StripStringTransformer(preprocess.BaseTransformer):
            @classmethod
            def transform(cls, string):
                return string[:-1]
        p = Preprocessor(transformers=StripStringTransformer())

        np.testing.assert_equal(p(self.corpus).tokens,
                                np.array([[doc[:-1]] for doc in self.corpus.documents]))

        p = Preprocessor(transformers=[StripStringTransformer(),
                                       preprocess.LowercaseTransformer()])

        np.testing.assert_equal(p(self.corpus).tokens,
                                np.array([[doc[:-1].lower()] for doc in self.corpus.documents]))

        self.assertRaises(TypeError, Preprocessor, string_transformers=1)

    def test_tokenizer(self):
        class SpaceTokenizer(preprocess.BaseTokenizer):
            @classmethod
            def tokenize(cls, string):
                return string.split()
        p = Preprocessor(tokenizer=SpaceTokenizer())

        np.testing.assert_equal(p(self.corpus).tokens,
                         np.array([sent.split() for sent in self.corpus.documents]))

    def test_token_normalizer(self):
        class CapTokenNormalizer(preprocess.BaseNormalizer):
            @classmethod
            def normalize(cls, token):
                return token.capitalize()
        p = Preprocessor(normalizer=CapTokenNormalizer())

        np.testing.assert_equal(p(self.corpus).tokens,
                                np.array([[sent.capitalize()] for sent in self.corpus.documents]))

    def test_token_filter(self):
        class SpaceTokenizer(preprocess.BaseTokenizer):
            @classmethod
            def tokenize(cls, string):
                return string.split()

        class LengthFilter(preprocess.BaseTokenFilter):
            @classmethod
            def check(cls, token):
                return len(token) < 4

        p = Preprocessor(tokenizer=SpaceTokenizer(), filters=LengthFilter())
        np.testing.assert_equal(p(self.corpus).tokens,
                         np.array([[token for token in doc.split() if len(token) < 4]
                                   for doc in self.corpus.documents]))

    def test_inplace(self):
        p = Preprocessor(tokenizer=preprocess.RegexpTokenizer('\w'))
        corpus = p(self.corpus, inplace=True)
        self.assertIs(corpus, self.corpus)

        corpus = p(self.corpus, inplace=False)
        self.assertIsNot(corpus, self.corpus)
        self.assertEqual(corpus, self.corpus)

        p = Preprocessor(tokenizer=preprocess.RegexpTokenizer('\w+'))
        corpus = p(self.corpus, inplace=False)
        self.assertIsNot(corpus, self.corpus)
        self.assertNotEqual(corpus, self.corpus)


class TransformationTests(unittest.TestCase):
    def test_call(self):

        class ReverseStringTransformer(preprocess.BaseTransformer):
            name = "Reverse"

            def transform(self, string):
                return string[::-1]

        transformer = ReverseStringTransformer()

        self.assertEqual(transformer('abracadabra'), 'arbadacarba')
        self.assertEqual(transformer(['abra', 'cadabra']), ['arba', 'arbadac'])

        self.assertRaises(TypeError, transformer, 1)

    def test_str(self):
        class ReverseStringTransformer(preprocess.BaseTransformer):
            name = 'reverse'

            def transform(self, string):
                return string[::-1]

        transformer = ReverseStringTransformer()

        self.assertIn('reverse', str(transformer))

    def test_lowercase(self):
        transformer = preprocess.LowercaseTransformer()
        self.assertEqual(transformer.transform('Abra'), 'abra')
        self.assertEqual(transformer.transform('\u00C0bra'), '\u00E0bra')

    def test_strip_accents(self):
        transformer = preprocess.StripAccentsTransformer()
        self.assertEqual(transformer.transform('Abra'), 'Abra')
        self.assertEqual(transformer.transform('\u00C0bra'), 'Abra')

    def test_html(self):
        transformer = preprocess.HtmlTransformer()
        self.assertEqual(transformer('<p>abra<b>cadabra</b><p>'), 'abracadabra')

    def test_url_remover(self):
        url_remover = preprocess.UrlRemover()
        self.assertEqual(url_remover.transform('some link to https://google.com/'), 'some link to ')
        self.assertEqual(url_remover.transform('some link to google.com'), 'some link to google.com')


class TokenNormalizerTests(unittest.TestCase):

    def setUp(self):
        self.stemmer = nltk.PorterStemmer().stem

    def test_str(self):
        stemmer = preprocess.PorterStemmer()
        self.assertIn('porter', str(stemmer).lower())

        stemmer = preprocess.SnowballStemmer('french')
        self.assertIn('french', str(stemmer).lower())

    def test_call(self):
        word = "Testing"
        tokens = ["Testing", "tokenized", "Sentence"]
        stemmer = preprocess.PorterStemmer()
        self.assertEqual(stemmer(word), self.stemmer(word))
        self.assertEqual(stemmer(tokens),
                         [self.stemmer(token) for token in tokens])

    def test_function(self):
        stemmer = preprocess.BaseNormalizer()
        stemmer.normalizer = lambda x: x[:-1]
        self.assertEqual(stemmer.normalize('token'), 'toke')

    def test_snowball(self):
        stemmer = preprocess.SnowballStemmer()
        stemmer.language = 'french'
        token = 'voudrais'
        self.assertEqual(stemmer(token), nltk.SnowballStemmer(language='french').stem(token))

    def test_porter_with_bad_input(self):
        stemmer = preprocess.PorterStemmer()
        self.assertRaises(TypeError, stemmer, 10)

    def test_lookup_normalize(self):
        dln = preprocess.DictionaryLookupNormalizer(dictionary={'aka': 'also known as'})
        self.assertEqual(dln.normalize('aka'), 'also known as')


class FilteringTests(unittest.TestCase):