
import logging
log = logging.getLogger(__name__)

from flask import request

from bokeh import protocol

from .bbauth import (
    check_read_authentication_and_create_client,
    check_write_authentication_and_create_client
)
from ..app import bokeh_app
from ..crossdomain import crossdomain
from ..serverbb import prune
from ..views import make_json

def init_bokeh(clientdoc):
    request.bokeh_server_document = clientdoc
    clientdoc.autostore = False
    clientdoc.autoadd = False

# Management Functions

@bokeh_app.route("/bokeh/bb/<docid>/reset", methods=['GET'])
@check_write_authentication_and_create_client
def reset(docid):
    ''' Reset a specified :class:`Document <bokeh.document.Document>`.

    Deletes all stored objects except for the current
    :class:`PlotContext <bokeh.objects.PlotContext>`, which has all of
    its children removed.

    :param docid: id of the :class:`Document <bokeh.document.Document>`
        to reset

    :status 200: when user is authorized
    :status 401: when user is not authorized

    '''
    clientdoc = bokeh_app.backbone_storage.get_document(docid)
    prune(clientdoc)
    for m in clientdoc._models:
        if not m.typename.endswith('PlotContext'):
            bokeh_app.backbone_storage.del_obj(docid, m)
        else:
            m.children = []
            bokeh_app.backbone_storage.store_objects(docid, m)
    return 'success'

@bokeh_app.route("/bokeh/bb/<docid>/rungc", methods=['GET'])
@check_write_authentication_and_create_client
def rungc(docid):
    ''' Run the Bokeh Server garbage collector for a given
    :class:`Document <bokeh.document.Document>`.

    :param docid: id of the :class:`Document <bokeh.document.Document>`
        to collect

    :status 200: when user is authorized
    :status 401: when user is not authorized

    '''
    clientdoc = bokeh_app.backbone_storage.get_document(docid)
    prune(clientdoc, delete=True)
    return 'success'

@bokeh_app.route("/bokeh/bb/<docid>/callbacks", methods=['POST'])
@check_write_authentication_and_create_client
def callbacks_post(docid):
    ''' Update callbacks for a given :class:`Document <bokeh.document.Document>`.

    :param docid: id of the :class:`Document <bokeh.document.Document>`
        to update callbacks for

    :status 200: when user is authorized
    :status 401: when user is not authorized

    '''
    # broken...
    clientdoc = bokeh_app.backbone_storage.get_document(docid)
    prune(clientdoc)
    jsondata = protocol.deserialize_json(request.data.decode('utf-8'))
    bokeh_app.backbone_storage.push_callbacks(jsondata)
    return make_json(protocol.serialize_json(jsondata))

@bokeh_app.route("/bokeh/bb/<docid>/callbacks", methods=['GET'])
@check_write_authentication_and_create_client
def callbacks_get(docid):
    ''' Retrieve callbacks for a given :class:`Document <bokeh.document.Document>`.

    :param docid: id of the :class:`Document <bokeh.document.Document>`
        to get callbacks for

    :status 200: when user is authorized
    :status 401: when user is not authorized

    '''
    # broken...
    clientdoc = bokeh_app.backbone_storage.get_document(docid)
    prune(clientdoc)
    jsondata = bokeh_app.backbone_storage.load_callbacks()
    return make_json(protocol.serialize_json(jsondata))

# bulk upsert
@bokeh_app.route("/bokeh/bb/<docid>/bulkupsert", methods=['POST'])
@check_write_authentication_and_create_client
def bulk_upsert(docid):
    ''' Update or insert new objects for a given :class:`Document <bokeh.document.Document>`.

    :param docid: id of the :class:`Document <bokeh.document.Document>`
        to update or insert into

    :status 200: when user is authorized
    :status 401: when user is not authorized

    '''
    # endpoint is only used by python, therefore we don't process
    # callbacks here
    client = request.headers.get('client', 'python')
    clientdoc = bokeh_app.backbone_storage.get_document(docid)
    prune(clientdoc)
    data = protocol.deserialize_json(request.data.decode('utf-8'))
    if client == 'python':
        clientdoc.load(*data, events='none', dirty=True)
    else:
        clientdoc.load(*data, events='existing', dirty=True)
    changed = bokeh_app.backbone_storage.store_document(clientdoc)
    msg = ws_update(clientdoc, changed)
    return make_json(msg)

def ws_update(clientdoc, models):
    attrs = clientdoc.dump(*models)
    msg = protocol.serialize_json({'msgtype' : 'modelpush',
                                   'modelspecs' : attrs
                               })
    bokeh_app.publisher.send("bokehplot:" + clientdoc.docid, msg)
    return msg

def ws_delete(clientdoc, models):
    attrs = clientdoc.dump(*models)
    msg = {
        'msgtype'    : 'modeldel',
        'modelspecs' : attrs,
    }
    msg = protocol.serialize_json(msg)
    bokeh_app.wsmanager.send("bokehplot:" + clientdoc.docid, msg)
    return msg

# backbone functionality
@bokeh_app.route("/bokeh/bb/<docid>/<typename>/", methods=['POST'])
@check_write_authentication_and_create_client