"""This module holds the resource mechanism definition of the ``import_export`` app."""

_registry = {}


def register(model, resource):
    _registry[model] = resource


def get_resource(model):
    try:
        return _registry[model]
    except KeyError:
        register(model, Resource)
        return get_resource(model)  # We return a Resource empty


def transform_field_to_display_field(field, model):
    return "%s (%s)" % (field, model._meta.verbose_name)


class Resource:
    """ Resource object.

    A Resource object is a container to gather all useful information we need to export/import
    a model object.

    It's use to export/import data related to this resource. With this object we know what
    the associated model, the fields that you want treated and fields that will serve us fetch an
    object of this Resource.

    Attributes
    ----------
    fields : tuple or list
        Fields of object to be exported (and imported soon).
    fields_to_get : tuple or list
        Fields used for fetch an object.
    header_comments : dictionnary
        Comments used to describe header.

    Example
    -------
    ```
    class BrainstormerGroupResource(Resource):
        model = BrainstormerPropositionGroup
        fields = ('name', 'color', 'x', 'y')
        fields_to_get = ('name',)
        header_comments = {
            'name': _('Name of group'),
        }
    ```
    Here we have a example of BrainstormerGroupResource.
    His associated model is BrainstormerPropositionGroup, we want export name, color, x and
    y attributes. And this resource is identified by his name to fetch an object.
    Moreover, a comment will be associated at field. It will be show in export file on headers.
    """

    fields = ()
    fields_to_get = ()
    header_comments = {}
    fields_to_translate = ()
    fk_fields_to_translate = ()
    nested_resource = ()

    @classmethod
    def get_model(cls):
        """Get linked model."""
        for model, resource in _registry.items():
            if resource == cls:
                return model

    @classmethod
    def get_nested_resource(cls, obj=None):
        return cls.nested_resource

    @staticmethod
    def _extract_fields(resource):
        fields = []
        for field in resource.fields:
            if isinstance(field, tuple):
                fields.append(field[0])
            else:
                fields.append(field)
        return fields

    @classmethod
    def get_fields(cls, allow_nested=True, only_nested=False):
        fields = []
        if not only_nested:
            fields += cls._extract_fields(cls)
        if allow_nested:
            for __, resource in cls.get_nested_resource():
                fields += cls._extract_fields(resource)
                if resource.nested_resource:
                    fields += resource.get_fields(only_nested=True)
        return fields

    @staticmethod
    def _extract_display_fields(resource):
        display_fields = []
        for field in resource.fields:
            if isinstance(field, tuple):
                display_fields.append(str(field[1]))
            else:
                display_fields.append(transform_field_to_display_field(field, resource.get_model()))
        return display_fields

    @classmethod
    def get_display_fields(cls, allow_nested=True, only_nested=False):
        display_fields = []
        if not only_nested:
            display_fields += cls._extract_display_fields(cls)
        if allow_nested:
            for __, resource in cls.get_nested_resource():
                display_fields += cls._extract_display_fields(resource)
                if resource.nested_resource:
                    display_fields += resource.get_display_fields(only_nested=True)
        return display_fields

    @classmethod
    def display_field_to_field_name(cls, display_field):
        for field in cls.fields:
            if isinstance(field, tuple) and field[1].lower() == display_field.lower():
                return field[0].lower()
            elif not isinstance(field, tuple):
                nice_field = transform_field_to_display_field(field, cls.get_model())
                if nice_field.lower() == display_field.lower():
                    return field.lower()
        return display_field.lower()

    @classmethod
    def field_name_to_display_field(cls, field_name):
        for field in cls.fields:
            if isinstance(field, tuple) and field[0].lower() == field_name.lower():
                return field[1].lower()
            elif not isinstance(field, tuple):
                if field.lower() == field_name.lower():
                    return transform_field_to_display_field(field, cls.get_model()).lower()
        return field_name.lower()
