from functools import partial
from collections import Sequence
from flask import Blueprint, g, abort, Response, jsonify, request, \
    render_template, escape
 
from werkzeug.utils import cached_property
from flask.ext.introspect import Tree, TreeRootView, NOTEXIST
 
 
def setup_tree(
    objects,
    root_class,
    roots=(),
    leaf_classes=(),
    template_base=None,
    xhr_template_base=None,
    tree_class=Tree,
    ):
 
    if callable(objects):
        objects = objects()
 
    if not isinstance(objects, root_class.__type__):
        abort(500,
              'Root objects type (%s) does not matches with %s.__type___ (%s)'
               % (escape(type(objects)), root_class.__name__,
              escape(root_class.__type__)))
 
    if roots:
        g.tree = tree_class(objects, roots, root_class=root_class,
                            leaf_classes=leaf_classes)
    if template_base:
        g.template_base = template_base
    if xhr_template_base:
        g.xhr_template_base = xhr_template_base
 
 
def path(path=None):
    if path is None:
        if not hasattr(g.tree.root, 'view'):
            abort(404)
 
        g.item = g.tree.root
    else:
 
        (treeitem, tail) = g.tree.get(path)
        if treeitem is NOTEXIST:
            abort(404)
        if not hasattr(treeitem, 'view'):
            abort(404)
 
        g.item = treeitem
    result = g.item.view().dispatch_request(g.item)
 
    if result is None:
        return ''
 
    if isinstance(result, (Response, basestring)):
        return result
 
    if not (isinstance(result, Sequence) and len(result) == 2):
        if isinstance(result, Sequence):
            return jsonify(*result)
        else:
            return jsonify(result)
 
    (template, context) = result
 
    # view methods can override this g attributes to change base template
 
    if request.is_xhr:
        base = getattr(g, 'xhr_template_base', None)
    else:
        base = getattr(g, 'template_base', None)
 
    if base:
        return render_template(base, template=template, **context)
    else:
        return render_template(template, **context)
 
 
def blueprint(
    name,
    import_name,
    objects,
    setup=setup_tree,
    path=path,
    methods=['get', 'post', 'put', 'patch', 'delete', 'head'],
    super_root_class=TreeRootView,
    **kwargs
    ):
 
    bp = Blueprint(name, import_name)
    bp.before_request(partial(setup_tree, objects, super_root_class,
                      **kwargs))
 
    if hasattr(super_root_class, 'view'):
        bp.route('/', methods=methods)(path)
 
    bp.route('/<path:path>', methods=methods)(path)
    return bp