from functools import wraps


def _restore_languages_on_generator_exit(method):
    @wraps(method)
    def wrapped(self, *args, **kwargs):
        stored_languages = self.languages[:]
        for language in method(self, *args, **kwargs):
            yield language
        else:
            self.languages[:] = stored_languages

    return wrapped


class BaseLanguageDetector:
    def __init__(self, languages):
        self.languages = languages[:]

    @_restore_languages_on_generator_exit
    def iterate_applicable_languages(self, date_string, settings=None, modify=False):
        languages = self.languages if modify else self.languages[:]
        yield from self._filter_languages(date_string, languages, settings)

    @staticmethod
    def _filter_languages(date_string, languages, settings=None):
        while languages:
            language = languages[0]
            if language.is_applicable(date_string, strip_timezone=False, settings=settings):
                yield language
            elif language.is_applicable(date_string, strip_timezone=True, settings=settings):
                yield language

            languages.pop(0)


class AutoDetectLanguage(BaseLanguageDetector):
    def __init__(self, languages, allow_redetection=False):
        super().__init__(languages=languages[:])
        self.language_pool = languages[:]
        self.allow_redetection = allow_redetection

    @_restore_languages_on_generator_exit
    def iterate_applicable_languages(self, date_string, modify=False, settings=None):
        languages = self.languages if modify else self.languages[:]
        initial_languages = languages[:]
        yield from self._filter_languages(date_string, languages, settings=settings)

        if not self.allow_redetection:
            return

        # Try languages that was not tried before with this date_string
        languages = [language
                     for language in self.language_pool
                     if language not in initial_languages]
        if modify:
            self.languages = languages

        yield from self._filter_languages(date_string, languages, settings=settings)


class ExactLanguages(BaseLanguageDetector):
    def __init__(self, languages):
        if languages is None:
            raise ValueError("language cannot be None for ExactLanguages")
        super().__init__(languages=languages)

    @_restore_languages_on_generator_exit
    def iterate_applicable_languages(self, date_string, modify=False, settings=None):
        yield from super().iterate_applicable_languages(date_string, modify=False, settings=settings)
