# -*- coding: utf-8 mode: python -*- vim:sw=4:sts=4:et:ai:si:sta:fenc=utf-8

__all__ = (
    'checked', 'selected', 'disabled', 'required',
    'accesskeyif',
    'classif', 'addclassif',
    'favicon', 'css', 'js', 'jscss',
    'litteral', 'p',
    'Action', 'Menu',
)

import urlparse, urllib

from ..base import Undef, odict, isseq, seqof
from ..ext import web

from .api import u

def checked(b):
    return u' checked="checked"' if b else u''
def selected(b):
    return u' selected="selected"' if b else u''
def disabled(b):
    return u' disabled="disabled"' if b else u''
def required(b):
    return u' required="required"' if b else u''

def accesskeyif(ak, b=None):
    if b is None: b = ak
    return u' accesskey="%s"' % ak if b else u''

def classif(c, b=None):
    if b is None: b = c
    return u' class="%s"' % c if b else u''
def addclassif(c, b=None, prefix=None):
    if b is None: b = c
    if not prefix: prefix = u''
    if not b: return prefix
    elif prefix: return u'%s %s' % (prefix, c)
    else: return c

def favicon(href):
    return ur'<link rel="shortcut icon" href="%s" />' % href
def css(href, media=None):
    if media is not None:
        media = ur' media="%s"' % media
    return ur'<link rel="stylesheet" href="%s" type="text/css"%s />' % (href, media or u'')
def js(href):
    return ur'<script type="text/javascript" src="%s"></script>' % href
def jscss(href):
    if href.endswith(".min.js"):
        jshref = href
        csshref = "%s.css" % href[:-len(".min.js")]
    elif href.endswith(".js"):
        jshref = href
        csshref = "%s.css" % href[:-len(".js")]
    elif href.endswith(".css"):
        jshref = "%s.js" % href[:-len(".css")]
        csshref = href
    return u"%s\n%s" % (js(jshref), css(csshref))

class litteral(object):
    """Objet proxy vers un texte littéral.
    à utiliser avec uval() pour ne pas mettre en échappement la valeur
    """
    def __init__(self, text): self.text = text
    def __call__(self): return self.text
    __unicode__ = __call__

def p(text):
    """Si text commence par un tag, le laisser tel quel, sinon le mettre entre <p> et </p>
    text doit avoir le cas échéant déjà été traité avec uval
    """
    text = u(text)
    if text[0:1] == '<': return text
    else: return u'<p>%s</p>' % text

################################################################################
# Menus

class Action(odict):
    """Un lien ou une action d'un formulaire

    url: cible de l'action ou du lien (avec querystring)
    text: titre du lien ou de l'élément de menu
    title: description de l'action. vaut text par défaut
    id: identifiant dans un menu
    in_profiles (p): profils dans lequel l'action est valide. le premier profil
        est le profil par défaut
    css (c): classe css à appliquer au lien
    accesskey (ak): accesskey à installer sur le lien
    query (q): paramètres de la requête, sous forme de dictionnaire ou de chaine
    method: type de requête, get ou post
    PROFILE_url, PROFILE_query: valeurs spécifiques à certains profils, PROFILE
        étant une valeur de in_profiles
    to_PROFILE_url, to_PROFILE_query: valeurs spécifiques pour basculer vers le
        profil spécifié, PROFILE étant une valeur de in_profiles

    les valeurs *url et *query sont reconstruites: baseurl et basequery sont
    extrait d'url et query est reconstruit en le fusionnant avec basequery

    Les valeurs suivantes sont générées:
    baseurl: cible de l'action ou du lien (sans querystring)
    """

    def __merge_query(self, query, basequery):
        if query is None: query = {}
        elif not isinstance(query, dict):
            query = urlparse.parse_qs(query, True)
        return dict(basequery, **query)
    def __update_url(self, urlkey, querykey, baseurlkey):
        url = self.get(urlkey, None)
        if url is None: return
        urlsplit = urlparse.urlsplit(url)
        baseurl = urlparse.urlunsplit(urlsplit[:3] + ('',) + urlsplit[4:])
        basequery = urlparse.parse_qs(urlsplit[3], True)
        query = self.get(querykey, None)
        query = self.__merge_query(query, basequery)
        url = urlparse.urlunsplit(
            urlsplit[:3]
            + (urllib.urlencode(query, True),)
            + urlsplit[4:])
        self[baseurlkey] = baseurl
        self[querykey] = query
        self[urlkey] = url

    def __init__(self, url, text=Undef, id=Undef, in_profiles=Undef, css=Undef, accesskey=Undef, query=Undef, title=Undef, method=Undef, **kw):
        text = Undef.sa(text, kw, 't', None)
        id = Undef.sa(id, kw, None, None)
        in_profiles = Undef.sa(in_profiles, kw, 'p', None)
        css = Undef.sa(css, kw, 'c', None)
        accesskey = Undef.sa(accesskey, kw, 'ak', None)
        query = Undef.sa(query, kw, 'q', None)
        title = Undef.sa(title, kw, 'nt')
        method = Undef.sa(method, kw, 'm', None)
        super(Action, self).__init__(**kw)
        if isseq(url):
            if url[1:2]: text = url[1]
            if url[2:3]: id = url[2]
            if url[3:4]: in_profiles = url[3]
            if url[4:5]: css = url[4]
            if url[5:6]: accesskey = url[5]
            if url[6:7]: title = url[6]
            if url[7:8]: method = url[7]
            url = url[0] if url[0:1] else None
        if url is None: raise ValueError("url is required")
        if text is None: text = url
        if in_profiles is None: in_profiles = kw.pop('p', None)
        if css is None: css = kw.pop('c', None)
        if accesskey is None: accesskey = kw.pop('ak', None)
        if query is None: query = kw.pop('q', None)
        if title is Undef: title = text
        if method is None: method = 'get'

        self.text = text
        self.id = id
        self.css = css
        self.accesskey = accesskey
        self.title = title
        self.method = method

        self.url = url
        self.query = query
        self.__update_url('url', 'query', 'baseurl')

        self.in_profiles = seqof(in_profiles, None)
        if self.in_profiles is None: return
        for profile in self.in_profiles:
            self.__update_url(*['%s_%s' % (profile, key) for key in ('url', 'query', 'baseurl')])
            self.__update_url(*['to_%s_%s' % (profile, key) for key in ('url', 'query', 'baseurl')])

    def update_query(self, query):
        return self.__class__(
            self.url, self.text, self.id, self.in_profiles, self.css, self.accesskey,
            query,
            self.title, self.method,
            )

    def __get_key(self, key, profile=None, to=False):
        if profile is not None:
            pkey = '%s%s_%s' % ('to_' if to else '', profile, key)
            value = self.get(pkey, None)
            if value is not None: return value
        return self[key]
    def get_baseurl(self, profile=None, to=False, **kw):
        """obtenir l'url de base
        """
        if profile is None: profile = kw.pop('p', None)
        return self.__get_key('baseurl', profile, to)
    def get_query(self, profile=None, to=False, **kw):
        """obtenir les paramètres de la requête sous forme de dictionnaire
        """
        if profile is None: profile = kw.pop('p', None)
        return self.__get_key('query', profile, to)
    def get_url(self, profile=None, to=False, **kw):
        """obtenir l'url
        """
        if profile is None: profile = kw.pop('p', None)
        return self.__get_key('url', profile, to)

    def get_qs(self, profile=None, to=False, sep=None, **kw):
        """obtenir les paramètres de la requête sous forme de query-string
        sep vaut par défaut '?' mais peut valoir '&'
        """
        if profile is None: profile = kw.pop('p', None)
        query = self.get_query(profile, to)
        if sep is None: sep = '?'
        return '%s%s' % (sep, urllib.urlencode(query, True))
    qs = property(get_qs)
    get_querystring = get_qs; querystring = property(get_qs)

    def get_inputs(self, profile=None, to=False, **kw):
        """obtenir les paramètres de la requête sous forme d'une liste de définitions
        d'éléments de formulaire de type hidden
        """
        if profile is None: profile = kw.pop('p', None)
        query = self.get_query(profile, to)
        Hidden = web.form.Hidden
        inputs = []
        for name, value in query.items():
            if isinstance(value, list):
                for value in value:
                    inputs.append(Hidden(name=name, value=value).render())
            else:
                inputs.append(Hidden(name=name, value=value).render())
        return u"\n".join(inputs)
    inputs = property(get_inputs)

class Menu(odict):
    """Un menu"""

    nextid = None
    idmap = None

    def __nextid(self):
        nextid = self.nextid
        self.nextid += 1
        return nextid

    def __init__(self, text=None, mitems=None, profiles=None,
                 id=None, in_profiles=Undef, css=Undef, title=Undef, **kw):
        in_profiles = Undef.sa(in_profiles, kw, 'p', None)
        css = Undef.sa(css, kw, 'c', None)
        title = Undef.sa(title, kw, 'nt')
        super(Menu, self).__init__(**kw)
        self.__dict__['nextid'] = 0
        self.__dict__['idmap'] = {}

        if text is None: text = u""
        if id is None: id = self.__nextid()
        if title is Undef: title = text
        self.text = text
        profiles = seqof(profiles, None)
        self.profiles = profiles
        self.default_profile = profiles[0] if profiles is not None else None
        self.id = str(id)
        self.in_profiles = seqof(p, None)
        self.css = css
        self.title = title
        self.sel_id = None
        self.sel_profile = None
        self.active = False
        self.mitems = []
        for mitem in seqof(mitems):
            self.add(mitem)

    def add(self, mitem, *args, **kw):
        if isinstance(mitem, Menu): pass # sous-menu
        elif not isinstance(mitem, Action):
            mitem = Action(mitem, *args, **kw)
        if mitem.id is None:
            mitem.id = str(self.__nextid())
        #mitem.profiles = self.profiles
        mitem.default_profile = self.default_profile
        mitem.active = False
        self.mitems.append(mitem)
        self.idmap[mitem.id] = mitem

    def reset_selection(self, **kw):
        self.sel_id = None
        self.sel_profile = self.default_profile
        self.active = False
        for mitem in self.mitems:
            if isinstance(mitem, Menu):
                mitem.reset_selection()
            else:
                mitem.active = False

    def select(self, id, profile=Undef, **kw):
        profile = Undef.sa(profile, kw, 'p', None)
        # d'abord déselectionner tout le monde
        self.reset_selection()
        # ensuite chercher le mitem à sélectionner
        id = str(id)
        mitem = self.idmap.get(id, None)
        if mitem is not None:
            self.sel_id = id
            if profile is None and mitem.in_profiles:
                profile = mitem.in_profiles[0]
            if profile is None: profile = self.default_profile
            self.sel_profile = profile
            mitem.active = self.active = True
            return True
        else:
            for mitem in self.mitems:
                if not isinstance(mitem, Menu): continue
                if mitem.select(id):
                    self.sel_id = id
                    self.sel_profile = mitem.sel_profile
                    self.active = True
                    return True
            return False

    def get_mitem(self, id=Undef, **kw):
        """retourner l'élément de menu correspondant à la sélection courante
        """
        id = Undef.sa(id, kw, None, self.sel_id)
        if id is None: return None
        id = str(id)
        mitem = self.idmap.get(id, None)
        if mitem is not None: return mitem
        for mitem in self.mitems:
            if not isinstance(mitem, Menu): continue
            mitem = mitem.get_mitem(id)
            if mitem is not None: return mitem
        return None
    selection = property(get_mitem)
