"""
    verktyg.views
    ~~~~~~~~~~~~~

    :copyright: (c) 2014 by Ben Mather.
    :license: BSD, see LICENSE for more details.
"""
import json

from verktyg.responses import Response
from verktyg.dispatch import BindingFactory, Binding


class View(BindingFactory):
    """ Wraps a function or callable so that it can be bound to a name in a
    dispatcher.

    :param name:
        The dispatcher name to bind to the action to.
    :param action:
        A callable object that takes an
        :class:`~verktyg.application.Application` and a
        :class:`~verktyg.requests.Request` and returns a
        :class:`~verktyg.responses.Response`
    :param methods:
        The http methods that this view can handle.  Can be a single method
        represented by a string or an iterable of method strings.
    :param content_type:
        The mime type of the response generated by the action
    :param language:
    :param charset:
    :param qs:
    """
    def __init__(
        self, name, action, *, methods=None,
        content_type=None, language=None, charset=None, qs=None
    ):
        self._name = name

        if methods is None:
            self._methods = {'GET'}
        elif isinstance(methods, str):
            self._methods = {methods}
        else:
            self._methods = set(methods)

        self._content_type = content_type
        self._language = language
        self._charset = charset
        self._qs = qs
        self._action = action

    def __call__(self, env, req, *args, **kwargs):
        return self._action(env, req, *args, **kwargs)

    def get_bindings(self):
        for method in self._methods:
            yield Binding(
                self._name, self, method=method,
                content_type=self._content_type, language=self._language,
                charset=self._charset, qs=self._qs,
            )


class ClassView(BindingFactory):
    def get_bindings(self):
        for method in {'GET', 'HEAD', 'POST', 'PUT', 'DELETE'}:  # TODO
            if hasattr(self, method):
                yield Binding(
                    self.name, getattr(self, method), method=method,
                )


class JsonView(View):
    def __init__(self, name, action, methods=None, qs=None):
        super(JsonView, self).__init__(
            name, action, methods=methods,
            content_type='application/json', qs=qs
        )

    def __call__(self, env, req, *args, **kwargs):
        res = super(JsonView, self).__call__(env, req, *args, **kwargs)

        if isinstance(res, Response):
            # rendering already done
            return res

        if res is None:
            # no content
            return Response(status=204)

        if env.debug:
            json_response = json.dumps(res, indent=4)
        else:
            json_response = json.dumps(res, separators=(',', ':'))

        return Response(json_response, content_type='application/json')


def expose(dispatcher, name, *args, **kwargs):
    def decorator(f):
        dispatcher.add_bindings(View(name, f, *args, **kwargs))
        return f
    return decorator


def expose_json(dispatcher, name, *args, **kwargs):
    def decorator(f):
        dispatcher.add_bindings(JsonView(name, f, *args, **kwargs))
        return f
    return decorator
