# -*- coding: UTF-8 -*-


# Expression
# By: Frederic Peters <fpeters@entrouvert.com>
#     Emmanuel Raviart <eraviart@entrouvert.com>
#
# Copyright (C) 2004 Entr'ouvert, Frederic Peters & Emmanuel Raviart
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.


"""XSLT Module"""


import cStringIO
import sys
import time
import traceback
import os

import libxml2
import libxslt

import dataholders
import elements
import environs
import faults
import html
import locations
import logs
import modules
import namespaces
import nodes
import stations


def _formatTime(ctx, datetime, formatstring):
    """Convert a datetime string (in w3c format <http://www.w3.org/TR/NOTE-datetime>)
       to a string according to formatstring (either in strftime format or expression
       own format.

       Example:
         <xsl:value-of select="yep:format-time(string($issuedDate), '%d-%m-%Y %H:%M')" />
       """
    if not datetime:
        t = time.localtime()
    else:
        try:
            t = time.strptime(datetime[:19], "%Y-%m-%dT%H:%M:%S")
        except ValueError:
            t = time.strptime(datetime[:10], "%Y-%m-%d")

    if not "%(" in formatstring:
        return time.strftime(formatstring, t)
    year, month, day, hour, minute, second, weekday = t[:7]

    weekdayNames = ["Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"]
    monthNames = ["January", "February", "March", "April", "May", "June",
                  "July", "August", "September", "October", "November", "December" ]

    language = environs.getVar("currentStation").getConfigString("yep:language")
    if language == "fr":
        # TODO: do this through gettext
        weekdayNames = ["Lundi", "Mardi", "Mercredi", "Jeudi", "Vendredi", "Samedi", "Dimanche"]
        monthNames = ["janvier", "février", "mars", "avril", "mai", "juin",
                      "juillet", "août", "septembre", "octobre", "novembre", "décembre"]

    weekdayName = weekdayNames[weekday]
    abWeekdayName = weekdayName[:3]

    monthName = monthNames[month-1]
    abMonthName = monthName[:3]

    try:
        return formatstring % locals()
    except (TypeError, KeyError):
        logs.warn("Wrong time format")
        return "Wrong time format"

def _gettext(ctx, text):
    """ translates the given text in the current active locale
    """
    return _(text)

def _getActionUri(ctx, actionName):
    station = environs.getVar("currentStation")
    if isinstance(station, stations.EasyFunctionStation) or \
            isinstance(station, stations.XslTransformStation):
        station = station.getParent()
    return station.getActionUri(actionName)


def _getConfigString(ctx, xpath, default = "none", ignoreGeneralValue = False):
    station = environs.getVar("currentStation")
    return station.getConfigString(xpath, default, ignoreGeneralValue)


def _getCurrentDocument(ctx):
    station = environs.getVar("currentStation").getDataHolder()
    if isinstance(station, dataholders.XmlHolder):
        node = station.getRootElement().node
        #station.getRootElement().node = None
        return [node]
    else:
        return []


def _getCurrentDocumentMetadata(ctx):
    station = environs.getVar("currentStation").getDataHolder().getMetadata()
    return [station.node]


def _getDocument(ctx, url):
    station = environs.getVar("rootStation")
    try:
        station = station.walkToLocation(url)
    except (faults.PathForbidden, faults.PathNotFound):
        station = environs.getVar("currentStation")
        if isinstance(station, stations.EasyFunctionStation) or \
                isinstance(station, stations.XslTransformStation):
            station = station.getParent()
        try:
            station = station.getParent().walkToLocation(url)
        except (faults.PathForbidden, faults.PathNotFound):
            station = None
    if isinstance(station, dataholders.XmlHolder):
        rootElement = station.getRootElement()
        if not rootElement:
            return []
        return [rootElement.node]
    else:
        return []


def _getNewButtonsLayout(ctx):
    try:
        # Insert result into current insertion node.
        pctxt = libxslt.xpathParserContext(_obj = ctx)
        ctxt = pctxt.context()
        tctxt = ctxt.transformContext()
        insertNode = tctxt.insertNode()
        layout = elements.newElement(insertNode, owner = "external")
        station = environs.getVar("currentStation")
        dataHolder = station.getDataHolder()
        actionsByLabel = dataHolder.getNewActionsByLabel()
        labels = actionsByLabel.keys()
        labels.sort()
        for label in labels:
            action = actionsByLabel[label]
            button = html.a(label, class_ = "button", href = dataHolder.getActionUri(action))
            newNode = nodes.copyNodeWithoutXhtmlNamespace(button.node)
            layout.append(newNode)
        return ""
    except:
        logs.exception(
            "An exception occured in XSLT function \"getNewButtonsLayout\":")
        # FIXME: Return an HTTP error code instead of the exception text.
        stdout = sys.stdout
        stderr = sys.stderr
        sys.stdout = sys.stderr = error = cStringIO.StringIO()
        traceback.print_exc()
        sys.stdout = stdout
        sys.stderr = stderr
        return error.getvalue()


def _getSession(ctx):
    if environs.getVar("session") is None:
        return []
    
    node = environs.getVar("session").node
    return [node]


def _getTitle(ctx, hideSiteTitle = False):
    try:
        xmlDocument = environs.getVar("currentXmlDocument")
        titleNodes = xmlDocument.evaluateXpath(
            "/html:html/html:head/html:title | /html/head/title")
        result = [node.content for node in titleNodes]
        if not hideSiteTitle:
            siteTitle = environs.getVar("currentStation").getConfigString(
                "yep:title", default = "Expression")
            if siteTitle:
                if result:
                    result = [siteTitle, " - "] + result
                else:
                    result = [siteTitle]
        return "".join(result)
    except:
        logs.exception(
            """An exception occured in XSLT function "getTitle":""")
        # FIXME: Return an HTTP error code instead of the exception text.
        stdout = sys.stdout
        stderr = sys.stderr
        sys.stdout = sys.stderr = error = cStringIO.StringIO()
        traceback.print_exc()
        sys.stdout = stdout
        sys.stderr = stderr
        return error.getvalue()


def _getUrl(ctx, *arguments):
    # The first argument if it exists is the location (current location is used by default).
    # The other arguments are the URL parameters.
    if len(arguments) % 2 == 0:
        # The number of arguments is even => Use current location for URL.
        url = environs.getVar("currentStation").getUri()
        start = 0
    else:
        # The number of arguments is odd => Use first argument for URL.
        url = environs.getVar("currentStation").constructUri(arguments[0])
        start = 1
    if url.startswith("mailto:"):
        return url
    for i in range(start, len(arguments), 2):
        url = locations.appendParameterToUrl(url, arguments[i], arguments[i + 1])
    return url


def _getUrlInternPath(ctx, location = None):
    station = environs.getVar("currentStation")
    if location is None:
        path = station.getUriInternPath()
    else:
        path = station.constructUriInternPath(location)
    return path


def _getUser(ctx):
    if environs.getVar("user") is None:
        return []
    
    node = environs.getVar("user").node
    return [node]


def _isUserAuthenticated(ctx):
    if environs.getVar("user") is None:
        return ""
    else:
        return "true"


def _httpHeaders(ctx, name):
    """ returns HTTP request headers values
    """
    return environs.getVar("httpRequestHandler").headers[name]


def registerFunctions():
    nsUri = namespaces.yep.uri
    libxslt.registerExtModuleFunction("action-uri", nsUri, _getActionUri)
    libxslt.registerExtModuleFunction("current-document", nsUri, _getCurrentDocument)
    libxslt.registerExtModuleFunction("current-document-metadata", nsUri, _getCurrentDocumentMetadata)
    libxslt.registerExtModuleFunction("document", nsUri, _getDocument)
    libxslt.registerExtModuleFunction("config-string", nsUri, _getConfigString)
    libxslt.registerExtModuleFunction("format-time", nsUri, _formatTime)
    libxslt.registerExtModuleFunction("gettext", nsUri, _gettext)
    libxslt.registerExtModuleFunction("http-headers", nsUri, _httpHeaders)
    libxslt.registerExtModuleFunction("new-buttons-layout", nsUri, _getNewButtonsLayout)
    libxslt.registerExtModuleFunction("session", nsUri, _getSession)
    libxslt.registerExtModuleFunction("site-title", nsUri, _getTitle)
    libxslt.registerExtModuleFunction("url", nsUri, _getUrl)
    libxslt.registerExtModuleFunction("url-intern-path", nsUri, _getUrlInternPath)
    libxslt.registerExtModuleFunction("user", nsUri, _getUser)
    libxslt.registerExtModuleFunction("user-authenticated", nsUri, _isUserAuthenticated)


def unregisterFunctions():
    pass

