from __future__ import unicode_literals
import logging
from .storage import StorageAdapter
from .logic import LogicAdapter, MultiLogicAdapter
from .input import InputAdapter
from .output import OutputAdapter
from .queues import ResponseQueue
from .utils import import_module


class ChatBot(object):

    def __init__(self, name, **kwargs):
        self.name = name
        kwargs['name'] = name

        storage_adapter = kwargs.get('storage_adapter',
            'chatterbot.storage.JsonFileStorageAdapter'
        )

        logic_adapters = kwargs.get('logic_adapters', [
            'chatterbot.logic.ClosestMatchAdapter'
        ])

        input_adapter = kwargs.get('input_adapter',
            'chatterbot.input.VariableInputTypeAdapter'
        )

        output_adapter = kwargs.get('output_adapter',
            'chatterbot.output.OutputFormatAdapter'
        )

        # The last 10 statement inputs and outputs
        self.recent_statements = ResponseQueue(maxsize=10)

        # The storage adapter must be an instance of StorageAdapter
        self.validate_adapter_class(storage_adapter, StorageAdapter)

        # The input adapter must be an instance of InputAdapter
        self.validate_adapter_class(input_adapter, InputAdapter)

        # The output adapter must be an instance of OutputAdapter
        self.validate_adapter_class(output_adapter, OutputAdapter)

        self.logic = MultiLogicAdapter(**kwargs)
        self.storage = self.initialize_class(storage_adapter, **kwargs)
        self.input = self.initialize_class(input_adapter, **kwargs)
        self.output = self.initialize_class(output_adapter, **kwargs)

        filters = kwargs.get('filters', tuple())
        self.filters = (import_module(F)() for F in filters)

        # Add required system logic adapter
        self.add_logic_adapter('chatterbot.logic.NoKnowledgeAdapter')

        for adapter in logic_adapters:
            self.add_logic_adapter(adapter, **kwargs)

        # Add the chatbot instance to each adapter to share information such as
        # the name, the current conversation, or other adapters
        self.storage.set_chatbot(self)
        self.logic.set_chatbot(self)
        self.input.set_chatbot(self)
        self.output.set_chatbot(self)

        # Use specified trainer or fall back to the default
        trainer = kwargs.get('trainer', 'chatterbot.trainers.Trainer')
        TrainerClass = import_module(trainer)
        self.trainer = TrainerClass(self.storage, **kwargs)
        self.training_data = kwargs.get('training_data')

        self.logger = kwargs.get('logger', logging.getLogger(__name__))
        self.initialize()

    def initialize(self):
        """
        Do any work that needs to be done before the responses can be returned.
        """
        from .utils import nltk_download_corpus

        # Download the stopwords data only if it is not already downloaded
        nltk_download_corpus('stopwords')

        # Download the wordnet data only if it is not already downloaded
        nltk_download_corpus('wordnet')

        # Download the punkt data only if it is not already downloaded
        nltk_download_corpus('punkt')

    def initialize_class(self, adapter_data, **kwargs):
        """
        :param adapter_data: A string or dictionary containing a import_path attribute.
        """
        if isinstance(adapter_data, dict):
            import_path = adapter_data.pop('import_path')
            adapter_data.update(kwargs)
            Class = import_module(import_path)

            return Class(**adapter_data)
        else:
            Class = import_module(adapter_data)

            return Class(**kwargs)

    def add_logic_adapter(self, adapter, **kwargs):
        self.validate_adapter_class(adapter, LogicAdapter)
        adapter = self.initialize_class(adapter, **kwargs)
        self.logic.add_adapter(adapter)

    def insert_logic_adapter(self, logic_adapter, insert_index, **kwargs):
        """
        Adds a logic adapter at a specified index.

        :param logic_adapter: The string path to the logic adapter to add.
        :type logic_adapter: class

        :param insert_index: The index to insert the logic adapter into the list at.
        :type insert_index: int

        :raises: InvalidAdapterException
        """
        self.validate_adapter_class(logic_adapter, LogicAdapter)

        NewAdapter = import_module(logic_adapter)
        adapter = NewAdapter(**kwargs)

        self.logic.adapters.insert(insert_index, adapter)

    def remove_logic_adapter(self, adapter_name):
        """
        Removes a logic adapter from the chat bot.

        :param adapter_name: The class name of the adapter to remove.
        :type adapter_name: str
        """
        for index, adapter in enumerate(self.logic.adapters):
            if adapter_name == type(adapter).__name__:
                del self.logic.adapters[index]
                return True
        return False

    def validate_adapter_class(self, validate_class, adapter_class):
        """
        Raises an exception if validate_class is not a
        subclass of adapter_class.

        :param validate_class: The class to be validated.
        :type validate_class: class

        :param adapter_class: The class type to check against.
        :type adapter_class: class

        :raises: InvalidAdapterException
        """
        from .adapters import Adapter

        # If a dictionary was passed in, check if it has an import_path attribute
        if isinstance(validate_class, dict):
            origional_data = validate_class.copy()
            validate_class = validate_class.get('import_path')

            if not validate_class:
                raise self.InvalidAdapterException(
                    'The dictionary {} must contain a value for "import_path"'.format(
                        str(origional_data)
                    )
                )

        if not issubclass(import_module(validate_class), Adapter):
            raise self.InvalidAdapterException(
                '{} must be a subclass of {}'.format(
                    validate_class,
                    Adapter.__name__
                )
            )

        if not issubclass(import_module(validate_class), adapter_class):
            raise self.InvalidAdapterException(
                '{} must be a subclass of {}'.format(
                    validate_class,
                    adapter_class.__name__
                )
            )

    def get_last_conversance(self):
        """