from __future__ import absolute_import

import sys
from cStringIO import StringIO
import urllib

import pylons
import tg
from webob.exc import HTTPNotFound, HTTPUnauthorized, HTTPInternalServerError

from bazbase.flavors import FLAVORS
from bazbase.benchmark import benchmarking
from bazbase import wiki, translators, conversion, dependencies, structure
from bazbase.benchmark import benchmarking
from bazbase.get import full_render_any
from bazbase import custom as basecust

from bazki.translators import absolute_url

from . import custom

DEFAULT_DEFAULT = u'.html'

def get(id, type, elm, pname=None, filters={}, visible_only=None):
    if type != u'default':
        formats = (type,)
    else:
        formats = None

    ret,actual = full_render_any(id, formats, elm, pname, filters, visible_only)

    if ret is None:
        if actual:
            raise HTTPNotFound(actual)
        else:
            flash(visible_only)
            raise HTTPUnauthorized()

    ctype, enc = translators.guess_type(id+actual)
    if ctype:
        pylons.response.headers['Content-type'] = ctype + '; charset=utf-8'

    return ret

def type_to_suffix(typ):
    if not typ:
        return ''
    elif not typ.startswith('.'):
        return '>' + typ
    else:
        return typ

def has_get_url(element,dep=None, render=conversion.render):
    return any(g.has_get_url(element, dep, render) for g in registered_getters)

GET_URL = '_get_url'
# !xavid: this render parameter worries me; will this do dependencies right
#         for caching?
def get_url_for(element, dep=None, render=conversion.render, type='',
                args=None):
    if not type:
        type = ''
    argtag = ''
    owner = wiki.get_overridden_element(u'owner')
    if owner is not None:
        argtag += '|' + owner.ename
    if args is not None:
        argtag += '/' + '/'.join(args)
    cache_entry = conversion.get_from_cache(element.ename, GET_URL + argtag,
                                            type)
    if cache_entry is None:
        deps = dependencies.Dependencies()
        with benchmarking('get_url_for(%s, type=%s)' % (element.ename, type)):
            for g in registered_getters:
                if g.has_get_url(element, deps, render):
                    ret = g.get_url_for(element, deps, render, type, args=args)
                    conversion.cache_propval(element.ename, GET_URL,
                                             type, ret, deps, {})
                    break
            else:
                ret = None
    else:
        deps = cache_entry['dependencies']
        ret = cache_entry['value']
    if dep is not None:
        dep.update(deps)
    return ret

# All elements have a get URL for attachments, even if they don't have a
# get URL
def attachment_url_for(element,pname,dep=None, render=conversion.render,
                       type=None, prefixtype=''):
    with benchmarking('attachment_url_for(%s, %s, type=%s)'
                      % (element.ename, pname, type)):
        for g in registered_getters:
            if g.has_get_url(element, dep, render):
                return g.attachment_url_for(element,pname,dep, render, type,
                                            prefixtype=prefixtype)
        return registered_getters[0].attachment_url_for(
            element, pname, dep, render, type, prefixtype=prefixtype)

def filename_split(fname):
    try:
        ext = pylons.request.response_ext
    except TypeError:
        ext = None
    if ext:
        s = fname
        typ = unicode(ext, 'utf-8')
    elif '.' in fname:
        s, typ = fname.rsplit('.', 1)
        typ = '.' + typ
    else:
        slist = fname.rsplit('>', 1)
        typ = slist[1] if len(slist) > 1 else u'default'
        s = slist[0]
    return s, typ

registered_getters = []

class BaseGetter(tg.TGController):
    def __init__(self, get_path):
        self.get_path = get_path
        registered_getters.append(self)

    @tg.expose()
    def _(self, ename, pname):
        namep = u'name'
        elm = structure.get_element(unicode(ename, 'utf-8'))
        pname, typ = filename_split(unicode(pname, 'utf-8'))
        name = u""
        if elm.has_propval(namep):
            name = conversion.render(elm, namep)
        if name == u"":
            name = ename
        return get(name, typ, elm, pname)

    def attachment_url_for(self, element, pname, dep, render, type,
                           prefixtype=''):
        endbit = '/' + pname + type_to_suffix(type)
        return absolute_url(self.get_path + '/_/' + element.ename + endbit,
                            dep, prefixtype=prefixtype)

class Getter(BaseGetter):
    def __init__(self, category_prop=u'category', get_path='',
                 default_category=u''):
        BaseGetter.__init__(self, get_path)
        self.category_prop = category_prop
        self.default_category = default_category

    @tg.expose()
    def _default(self, *args):
        args = [unicode(a, 'utf-8') for a in args]
        
        elm = None
        pname = None
        # We're either category/, category/name or category/name/pname
        # the first one wins
        nameprop = u'name'
        cat = u'/'.join(args)
        with benchmarking('searching for category: ' + cat):
            es = structure.search_elements({self.category_prop: cat})
        if len(es) > 0:
            # get the element with that cat and name Index, or do a not found
            id = u'Index'
            type = u'default'
            elm = structure.get_element_with({self.category_prop: cat,
                                              nameprop: id})
            if elm is None:
                raise HTTPNotFound((cat,id))
        elif len(args) > 0:
            cat ='/'.join(args[:-1])
            es = structure.search_elements({self.category_prop: cat})
            if len(es) > 0:
                # get the element with that cat and name args[-1], or 404
                id,type = filename_split(args[-1])
                id = id.replace('_', ' ')
                elm = structure.get_element_with({self.category_prop: cat,
                                                  nameprop: id})
                if elm is None:
                    raise HTTPNotFound((2,cat,id))
            elif len(args) > 1:
                cat = '/'.join(args[:-2])
                es = structure.search_elements({self.category_prop: cat})
                if len(es) > 0:
                    # get the element with that cat and name args[-2],
                    # and serve its property named args[-1], or 404
                    id = args[-2]
                    assert isinstance(args[-1], unicode), repr(args)
                    pname,type = filename_split(args[-1])
                    assert isinstance(pname, unicode), repr(pname)
                    elm = structure.get_element_with({self.category_prop: cat,
                                                      nameprop: id})
                    if elm is None:
                        raise HTTPNotFound((3,cat,id))
            
        if elm is None:
            raise HTTPNotFound(None)
        else:
            return get(id,type,elm,pname)

    def get_url_for(self, element, dep, render=conversion.render, type=None,
                    suffix='', prefixtype='', args=None):
        namep = u'name'
        if dep is not None:
            dep.addPropvalDep(element, namep)
            dep.addPropvalDep(element, self.category_prop)

        name = render(element, namep)

        typesuffix = type_to_suffix(type)
        if typesuffix == DEFAULT_DEFAULT and '.' not in name:
            typesuffix = ''

        try:
            cat = render(element, self.category_prop)
        except KeyError:
            cat = u''
        assert u'*' not in cat,cat
        if cat:
            cat += u'/'
        if name == u'Index' and type is None and not suffix:
            return absolute_url(self.get_path + '/' + cat, dep,
                                prefixtype=prefixtype)
        else:
            return absolute_url(self.get_path + '/'
                                + cat + urllib.quote(name.encode('utf-8'), '')
                                + suffix
                                + typesuffix,
                                dep, prefixtype=prefixtype)
    def has_get_url(self, element, dep=None, render=conversion.render):
        nameprop = u'name'
        productprop = u'product'
        if dep:
            dep.addPropvalDep(element, nameprop)
            dep.addPropvalDep(element, self.category_prop)
            dep.addPropvalDep(element, productprop)
        try:
            return (render(element, nameprop)
                    and element.has_propval(self.category_prop)
                    and element.has_propval(productprop))
        except KeyError:
            return False
        except wiki.NoContentException:
            return False
    def attachment_url_for(self, element, pname, dep, render, type,
                           prefixtype):
        endbit = '/' + pname + type_to_suffix(type)
        if self.has_get_url(element,dep,render):
            return self.get_url_for(element, dep, render, suffix=endbit,
                                    prefixtype=prefixtype)
        else:
            return BaseGetter.attachment_url_for(self, element, pname, dep,
                                                 render, type, prefixtype)
