class Array(object):
    """
    API Reference: https://www.contentful.com/developers/docs/references/content-delivery-api/#/introduction/collection-resources-and-pagination
    """

    def __init__(self, json, items):
        self.items = items
        self.skip = json.get('skip', 0)
        self.limit = json.get('limit', 100)
        self.total = json.get('total', 0)

    def __iter__(self):
        for item in self.items:
            yield item

    def __getitem__(self, index):
        return self.items[index]

    def __len__(self):
        return len(self.items)

    def __repr__(self):
        return "<Array size='{0}' total='{1}' limit='{2}' skip='{3}'>".format(
            len(self),
            self.total,
            self.limit,
            self.skip
        ) class Entry(FieldsResource):
    """
    API Reference: https://www.contentful.com/developers/docs/references/content-delivery-api/#/reference/entries
    """

    def _coerce(self, field_id, value, localized, includes, errors, resources=None):
        if is_link(value):
            if unresolvable(value, errors):
                return None
            return self._build_nested_resource(
                value,
                localized,
                includes,
                errors,
                resources=resources
            )
        elif is_link_array(value):
            items = []
            for link in value:
                if unresolvable(link, errors):
                    continue
                items.append(
                    self._build_nested_resource(
                        link,
                        localized,
                        includes,
                        errors,
                        resources=resources
                    )
                )

            return items

        content_type = ContentTypeCache.get(
            self.sys['content_type'].id
        )
        if content_type is not None:
            content_type_field = content_type.field_for(field_id)
            if content_type_field is not None:
                return content_type_field.coerce(
                    value,
                    includes=includes,
                    errors=errors,
                    resources=resources,
                    default_locale=self.default_locale,
                    locale=self.sys.get('locale', '*')
                )

        return super(Entry, self)._coerce(
            field_id,
            value,
            localized,
            includes,
            errors,
            resources
        )

    def _build_nested_resource(self, value, localized, includes, errors, resources=None):
        # Maximum include Depth is 10 in the API, but we raise it to 20 (default),
        # in case one of the included items has a reference in an upper level,
        # so we can keep the include chain for that object as well
        # Any included object after the 20th level of depth will be just a Link.
        # When using reuse_entries, this is not the case if the entry was previously
        # cached.

        resource = resource_for_link(
            value,
            includes,
            resources=resources,
            locale=self.sys.get('locale', '*')
        )

        if isinstance(resource, FieldsResource):  # Resource comes from instance cache
            return resource

        if self._depth < self._max_depth:
            if resource is not None:
                return self._resolve_include(
                    resource,
                    localized,
                    includes,
                    errors,
                    resources=resources
                )

        return self._build_link(value)

    def _resolve_include(self, resource, localized, includes, errors, resources=None):
        from .resource_builder import ResourceBuilder
        return ResourceBuilder(
            self.default_locale,
            localized,
            resource,
            includes_for_single=includes,
            errors_for_single=errors,
            reuse_entries=bool(resources),
            resources=resources,
            depth=self._depth + 1,
            max_depth=self._max_depth
        ).build()

    def incoming_references(self, client=None, query=None):
        """Fetches all entries referencing the entry

        API Reference: https://www.contentful.com/developers/docs/references/content-delivery-api/#/reference/search-parameters/links-to-asset

        :param client Client instance
        :param query: (optional) Dict with API options.
        :return: List of :class:`Entry <contentful.entry.Entry>` objects.
        :rtype: List of contentful.entry.Entry

        Usage:
            >>> entries = entry.incoming_references(client)
            [<Entry[cat] id='happycat'>]
        """
        if query is None:
            query = {}

        if client is None:
            return False

        query.update({'links_to_entry': self.id})
        return client.entries(query)

    def __repr__(self):
        return "<Entry[{0}] id='{1}'>".format(
            self.sys['content_type'].sys.get('id', ''),
            self.sys.get('id', '')
        ) class Asset(FieldsResource):
    """
    API Reference: https://www.contentful.com/developers/docs/references/content-delivery-api/#/reference/assets
    """

    def url(self, **kwargs):
        """Returns a formatted URL for the Asset's File
        with serialized parameters.

        Usage:
            >>> my_asset.url()
            "//images.contentful.com/spaces/foobar/..."
            >>> my_asset.url(w=120, h=160)
            "//images.contentful.com/spaces/foobar/...?w=120&h=160"
        """

        if not hasattr(self, 'file') or not self.file:
            return ""

        url = self.file['url']
        args = ['{0}={1}'.format(k, v) for k, v in kwargs.items()]

        if args:
            url += '?{0}'.format('&'.join(args))

        return url

    def incoming_references(self, client=None, query=None):
        """Fetches all entries referencing the asset

        API Reference: https://www.contentful.com/developers/docs/references/content-delivery-api/#/reference/search-parameters/links-to-asset
        :param client Client instance
        :param query: (optional) Dict with API options.
        :return: List of :class:`Entry <contentful.entry.Entry>` objects.
        :rtype: List of contentful.entry.Entry

        Usage:
            >>> entries = asset.incoming_references(client)
            [<Entry[cat] id='happycat'>]
        """
        if query is None:
            query = {}

        if client is None:
            return False

        query.update({'links_to_asset': self.id})
        return client.entries(query)

    def __repr__(self):
        return "<Asset id='{0}' url='{1}'>".format(
            self.sys.get('id', ''),
            self.url()
        ) class Space(Resource):
    """
    API Reference: https://www.contentful.com/developers/docs/references/content-delivery-api/#/reference/spaces
    """

    def __init__(self, item, **kwargs):
        super(Space, self).__init__(item, **kwargs)
        self.name = item.get('name', '')
        self.locales = [Locale(locale) for locale in item.get('locales', [])]

    def __repr__(self):
        return "<Space[{0}] id='{1}'>".format(
            self.name,
            self.id
        ) class ContentType(Resource):
    """
    API Reference: https://www.contentful.com/developers/docs/references/content-delivery-api/#/reference/content-types
    """

    def __init__(self, item, **kwargs):
        super(ContentType, self).__init__(item, **kwargs)
        self.name = item.get('name', '')
        self.description = item.get('description', '')
        self.display_field = item.get('displayField', '')
        self.fields = [ContentTypeField(field)
                       for field in item.get('fields', [])]

    def field_for(self, field_id):
        """Fetches the field for the given Field ID.

        :param field_id: ID for Field to fetch.
        :return: :class:`ContentTypeField <ContentTypeField>` object.
        :rtype: contentful.ContentTypeField
        """

        for field in self.fields:
            if field.id == field_id:
                return field
        return None

    def __repr__(self):
        return "<ContentType[{0}] id='{1}'>".format(
            self.name,
            self.sys.get('id', '')
        ) class DeletedAsset(Resource):
    """
    API Reference: https://www.contentful.com/developers/docs/references/content-delivery-api/#/reference/synchronization
    """

    def __repr__(self):
        return "<DeletedAsset id='{0}'>".format(
            self.sys.get('id', '')
        ) class DeletedEntry(Resource):
    """
    API Reference: https://www.contentful.com/developers/docs/references/content-delivery-api/#/reference/synchronization
    """

    def __repr__(self):
        return "<DeletedEntry id='{0}'>".format(
            self.sys.get('id', '')
        ) class Locale(Resource):
    """
    API Reference: https://www.contentful.com/developers/docs/references/content-delivery-api/#/reference/localization
    """

    def __init__(self, item, **kwargs):
        super(Locale, self).__init__(item, **kwargs)
        self.code = item.get('code', '')
        self.name = item.get('name', '')
        self.fallback_code = item.get('fallbackCode', '')
        self.default = item.get('default', False)
        self.optional = item.get('optional', False)

    def __repr__(self):
        return "<Locale[{0}] code='{1}' default={2} fallback_code={3} optional={4}>".format(
            self.name,
            self.code,
            self.default,
            "'{0}'".format(
              self.fallback_code
            ) if self.fallback_code is not None else 'None',
            self.optional
        ) class SyncPage(Resource):
    """
    API Reference: https://www.contentful.com/developers/docs/references/content-delivery-api/#/reference/synchronization
    """

    def __init__(self, item, **kwargs):
        super(SyncPage, self).__init__(item, **kwargs)

        self.next_sync_url = item.get('nextSyncUrl', '')
        self.next_page_url = item.get('nextPageUrl', '')
        self.next_sync_token = self._get_sync_token()
        self.items = self._hydrate_items()

    def next(self, client):
        """Fetches next SyncPage

        :param client: CDA Client.
        :return: :class:`SyncPage <SyncPage>`
        :rtype: contentful.sync_page.SyncPage
        """

        return client.sync({'sync_token': self.next_sync_token})

    def _get_sync_token(self):
        url_parts = urlsplit(self.next_sync_url or self.next_page_url)
        querystring = parse_qs(url_parts.query)
        return querystring['sync_token'][0]

    def _hydrate_items(self):
        from .resource_builder import ResourceBuilder
        items = []
        for item in self.raw.get('items', []):
            items.append(
                ResourceBuilder(
                    self.default_locale,
                    True,
                    item
                ).build()
            )

        return items

    def __repr__(self):
        return "<SyncPage next_sync_token='{0}'>".format(
            self.next_sync_token
        ) def unresolvable(item, errors):
    if not item:
        return True

    for error in errors:
        if error.get('details', {}).get('id', None) == item['sys']['id']:
            return True
    return False
from .array import Array
from .entry import Entry
from .asset import Asset
from .space import Space
from .content_type import ContentType
from .deleted_asset import DeletedAsset
from .deleted_entry import DeletedEntry
from .locale import Locale
from .sync_page import SyncPage
from .utils import unresolvable


"""
contentful.resource_builder
~~~~~~~~~~~~~~~~~~~~~~~~~~~

This module implements the Resource Builder class.

:copyright: (c) 2016 by Contentful GmbH.
:license: MIT, see LICENSE for more details.
"""


class ResourceBuilder(object):
    """Creates objects of the proper Resource Type"""

    def __init__(
            self,
            default_locale,
            localized,
            json,
            includes_for_single=None,
            errors_for_single=None,
            reuse_entries=False,
            resources=None,
            depth=0,
            max_depth=20):
        self.default_locale = default_locale
        self.localized = localized
        self.json = json
        self.includes_for_single = includes_for_single
        self.errors_for_single = errors_for_single
        self.reuse_entries = reuse_entries
        self.depth = depth
        self.max_depth = max_depth

        if resources is None:
            resources = {} if self.reuse_entries else None
        self.resources = resources

    def build(self):
        """Creates the objects from the JSON response"""

        if self.json['sys']['type'] == 'Array':
            if any(k in self.json for k in ['nextSyncUrl', 'nextPageUrl']):
                return SyncPage(
                    self.json,
                    default_locale=self.default_locale,
                    localized=True
                )
            return self._build_array()
        return self._build_single()

    def _build_single(self):
        includes = []
        errors = []
        if self.includes_for_single is not None:
            includes = self.includes_for_single
        if self.errors_for_single is not None:
            errors = self.errors_for_single

        return self._build_item(
            self.json,
            includes=includes,
            errors=errors
        )

    def _build_array(self):
        includes = self._includes()
        errors = self._errors()

        items = [self._build_item(
                    item,
                    includes=includes,
                    errors=errors
                 ) for item in self.json['items']
                 if not unresolvable(item, self._errors())]


        return Array(self.json, items)