# -*- 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.


"""XHTML Tags Module"""


from email.MIMEText import MIMEText
import types

import libxml2

try:
    import tidy # TidyLib Python wrapper <http://utidylib.berlios.de/>
except ImportError:
    tidy = None

import dataholders
import documents
import elements
import locations
import logs
import modules
import namespaces
import stations
import widgets


class Element(elements.Element):
    def __init__(self, *items, **attributes):
        node = attributes.pop("node", None)
        previous = attributes.pop("previous", None)
        parent = attributes.pop("parent", None)
        uriPathFragment = attributes.pop("uriPathFragment", None)
        owner = attributes.pop("owner", "undefined")
        super(Element, self).__init__(
            node, previous = previous, parent = parent,
            uriPathFragment = uriPathFragment, owner = owner)
        for key, value in attributes.items():
            if key[-1] == "_":
                key = key[:-1]
            self.node.setProp(key, value)
        for item in items:
            self.append(item)

    def generateXmlContextAttributes(self, context, newElement):
        filled = False
        attribute = self.node.properties
        while attribute is not None:
            attributeName = attribute.name
            attributeContent = attribute.content
            if attributeName in ("href", "src") and attributeContent:
                attributeContent = context.specimen.constructUri(attributeContent)
            newElement.node.newProp(attributeName, attributeContent)
            filled = True
            attribute = attribute.next
        return filled

    def getAttribute(self, name):
        if self.node.hasProp(name):
            return self.node.prop(name)
        else:
            return None

    def setAttribute(self, name, value):
        self.node.setProp(name, value)


class WidgetElement(widgets.WidgetMixin, Element):
    def isInline(self):
        raise NotImplementedError

    def newContext(self, specimen, *attributes, **keywords):
        return WidgetElementContext(self, specimen, *attributes, **keywords)

    def submitContext(self, context):
        childNode = self.node.children
        while childNode is not None:
            if childNode.type != "element":
                childNode = childNode.next
                continue
            element = self.newElement(childNode)
            elementContext = element.newContext(context.specimen, previous = context)
            elementContext.submit()
            childNode = childNode.next


class WidgetElementContext(elements.ElementContext):
    def deleteFormCreationNeeded(self):
        del self.getParent().formCreationNeeded

    def deleteInForm(self):
        del self.getParent().inForm

    def deleteXformsDefaultModel(self):
        del self.getParent().xformsDefaultModel

    def deleteXformsModelsById(self):
        del self.getParent().xformsModelsById

    def getFormCreationNeeded(self):
        return self.getParent().formCreationNeeded

    def getInForm(self):
        return self.getParent().inForm

    def getXformsDefaultModel(self):
        return self.getParent().xformsDefaultModel

    def getXformsModelsById(self):
        return self.getParent().xformsModelsById

    def setFormCreationNeeded(self, formCreationNeeded):
        self.getParent().formCreationNeeded = formCreationNeeded

    def setInForm(self, inForm):
        self.getParent().inForm = inForm

    def setXformsDefaultModel(self, xformsDefaultModel):
        self.getParent().xformsDefaultModel = xformsDefaultModel

    def setXformsModelsById(self, xformsModelsById):
        self.getParent().xformsModelsById = xformsModelsById

    def submit(self):
        return self.prototype.submitContext(self)

    formCreationNeeded = property(
        getFormCreationNeeded, setFormCreationNeeded, deleteFormCreationNeeded)
    inForm = property(getInForm, setInForm, deleteInForm)
    xformsDefaultModel = property(
        getXformsDefaultModel, setXformsDefaultModel, deleteXformsDefaultModel)
    xformsModelsById = property(getXformsModelsById, setXformsModelsById, deleteXformsModelsById)


class BlockLevelElement(WidgetElement):
    def isInline(self):
        # FIXME TODO.
        return False


class InlineLevelElement(WidgetElement):
    def isInline(self):
        # FIXME TODO.
        return True


class HtmlHolder(dataholders.XmlHolder):
    defaultFileNameExtension = ".html"
    mimeType = "text/html"

    def parseDataFile(self):
        try:
            doc = super(HtmlHolder, self).parseDataFile()
        except (libxml2.parserError, libxml2.treeError):
            # HTML parsing failed. Either its syntax is incorrect or it is pre-XHTML HTML.
            # Use TidyLib to convert this HTML to XHTML and retry.
            if tidy is None:
                raise
            dataFile = self.getDataFile()
            badXhtml = dataFile.read()
            dataFile.close()
            validXhtml = str(tidy.parseString(
                badXhtml, output_xhtml = 1, add_xml_decl = 1, indent = 1, numeric_entities = 1,
                input_encoding = "utf8", output_encoding = "utf8", tidy_mark = 0))
            try:
                doc = libxml2.readDoc(
                    validXhtml, self.getAbsolutePath(), None,
                    libxml2.XML_PARSE_DTDLOAD | libxml2.XML_PARSE_NONET)
            except (libxml2.parserError, libxml2.treeError):
                logs.info('Error while parsing tidyied HTML file')
                raise
        return doc

class a(InlineLevelElement):
    pass


class abbr(InlineLevelElement):
    pass


class acronym(InlineLevelElement):
    pass


class address(InlineLevelElement):
    pass


class applet(BlockLevelElement):
    pass


class area(BlockLevelElement):
    pass


class b(InlineLevelElement):
    pass


class base(WidgetElement):
    pass


class basefont(WidgetElement):
    pass


class bdo(WidgetElement):
    pass


class big(InlineLevelElement):
    pass


class blockquote(BlockLevelElement):
    pass


class body(WidgetElement):
    pass


class br(InlineLevelElement):
    pass


class button(InlineLevelElement):
    pass


class caption(InlineLevelElement):
    pass


class center(BlockLevelElement):
    pass


class cite(InlineLevelElement):
    pass


class code(InlineLevelElement):
    pass


class col(WidgetElement):
    pass


class colgroup(WidgetElement):
    pass


class dd(InlineLevelElement):
    pass


class del_(InlineLevelElement):
    pass


class dfn(InlineLevelElement):
    pass


class dir(BlockLevelElement):
    pass


class div(BlockLevelElement):
    pass


class dl(BlockLevelElement):
    pass


class dt(InlineLevelElement):
    pass


class em(InlineLevelElement):
    pass


class fieldset(BlockLevelElement):
    pass


class font(InlineLevelElement):
    pass


class form(BlockLevelElement):
    pass


class h1(BlockLevelElement):
    pass


class h2(BlockLevelElement):
    pass


class h3(BlockLevelElement):
    pass


class h4(BlockLevelElement):
    pass


class h5(BlockLevelElement):
    pass


class h6(BlockLevelElement):
    pass


class head(WidgetElement):
    pass


class hr(WidgetElement):
    pass


class html(WidgetElement):
    def generateEmailContextMessageBody(self, context):
        import documents
        htmlDocument = documents.newTemporaryHtmlDocument()
        self.generateXmlContext(context, htmlDocument)
        data = htmlDocument.serialize()
        return MIMEText(data, "html", "UTF-8")

    def generateXmlDocument(self):
        htmlDocument = documents.newTemporaryHtmlDocument()
        self.generateXml(htmlDocument)
        return htmlDocument

    def newContext(self, specimen, *attributes, **keywords):
        return HtmlContext(self, specimen, *attributes, **keywords)

    def kupu(self):
        """
        Handles HTTP GET & PUT.
        """

        command = environs.getVar("httpCommand")
        if command not in ("GET", "PUT"):
            raise faults.PathNotFound("")

        try:
            kupuHolder = self.walkToLocation("/javascript/kupu.html")
        except faults.PathNotFound:
            return self, uriPathFragments
        kupu = kupuHolder.getRootElement() # FIXME: We should make a copy.
        iframeNodes = kupu.evaluateXpath("//html:iframe")
        iframeNode = iframeNodes[0]
        iframeNode.setProp("src", self.getActionUri("source"))
        iframeNode.setProp("dst", self.getActionUri("source"))
        return kupu.checkAccessAndWalk([], command, environs.getVar("instruction"))


class HtmlContextMixin(object):
    formCreationNeeded = None # 3 states: True, False & None (= don't know)
    inForm = False
    specimen = None
    xformsDefaultModel = None
    xformsModelsById = None


class HtmlContext(HtmlContextMixin, WidgetElementContext):
    pass


class i(InlineLevelElement):
    pass


class iframe(BlockLevelElement):
    pass


class img(InlineLevelElement):
    pass


class input(InlineLevelElement):
    pass


class ins(InlineLevelElement):
    pass


class isindex(WidgetElement):
    pass


class kbd(InlineLevelElement):
    pass


class label(InlineLevelElement):
    pass


class legend(InlineLevelElement):
    pass


class li(InlineLevelElement):
    pass


class link(WidgetElement):
    pass


class map(BlockLevelElement):
    pass


class menu(BlockLevelElement):
    pass


class meta(WidgetElement):
    pass


class noframes(WidgetElement):
    pass


class noscript(WidgetElement):
    pass


class object(BlockLevelElement):
    pass


class ol(BlockLevelElement):
    pass


class optgroup(WidgetElement):
    pass


class option(WidgetElement):
    pass


class p(BlockLevelElement):
    translatable = True


class param(WidgetElement):
    pass


class pre(BlockLevelElement):
    pass


class q(InlineLevelElement):
    pass


class s(InlineLevelElement):
    pass


class samp(InlineLevelElement):
    pass


class script(WidgetElement):
    pass


class select(InlineLevelElement):
    pass


class small(InlineLevelElement):
    pass


class span(InlineLevelElement):
    pass


class strike(InlineLevelElement):
    pass


class strong(InlineLevelElement):
    pass


class style(WidgetElement):
    pass


class sub(InlineLevelElement):
    pass


class sup(InlineLevelElement):
    pass


class table(BlockLevelElement):
    pass


class tbody(BlockLevelElement):
    pass


class td(InlineLevelElement):
    pass


class textarea(InlineLevelElement):
    pass


class tfoot(BlockLevelElement):
    pass


class th(BlockLevelElement):
    pass


class thead(BlockLevelElement):
    pass


class title(WidgetElement):
    pass


class tr(BlockLevelElement):
    pass


class tt(InlineLevelElement):
    pass


class u(InlineLevelElement):
    pass


class ul(InlineLevelElement):
    pass


class var(InlineLevelElement):
    pass


def areInline(*items):
    for item in items:
        if not isInline(item):
            return False
    return True


def enclose(*items, **attributes):
    if areInline(*items):
        return span(*items, **attributes)
    else:
        return div(*items, **attributes)


def isInline(item):
    if item is None:
        return True
    elif isinstance(item, WidgetElement):
        return item.isInline()
    elif type(item) in (types.ListType, types.TupleType):
        for itemItem in item:
            if not isInline(itemItem):
                return False
        return True
    else:
        return True


elements.registerElement(namespaces.html.uri, "a", a)
elements.registerElement(namespaces.html.uri, "abbr", abbr)
elements.registerElement(namespaces.html.uri, "acronym", acronym)
elements.registerElement(namespaces.html.uri, "address", address)
elements.registerElement(namespaces.html.uri, "applet", applet)
elements.registerElement(namespaces.html.uri, "area", area)
elements.registerElement(namespaces.html.uri, "b", b)
elements.registerElement(namespaces.html.uri, "base", base)
elements.registerElement(namespaces.html.uri, "basefont", basefont)
elements.registerElement(namespaces.html.uri, "bdo", bdo)
elements.registerElement(namespaces.html.uri, "big", big)
elements.registerElement(namespaces.html.uri, "blockquote", blockquote)
elements.registerElement(namespaces.html.uri, "body", body)
elements.registerElement(namespaces.html.uri, "br", br)
elements.registerElement(namespaces.html.uri, "button", button)
elements.registerElement(namespaces.html.uri, "caption", caption)
elements.registerElement(namespaces.html.uri, "center", center)
elements.registerElement(namespaces.html.uri, "cite", cite)
elements.registerElement(namespaces.html.uri, "code", code)
elements.registerElement(namespaces.html.uri, "col", col)
elements.registerElement(namespaces.html.uri, "colgroup", colgroup)
elements.registerElement(namespaces.html.uri, "dd", dd)
elements.registerElement(namespaces.html.uri, "del", del_)
elements.registerElement(namespaces.html.uri, "dfn", dfn)
elements.registerElement(namespaces.html.uri, "dir", dir)
elements.registerElement(namespaces.html.uri, "div", div)
elements.registerElement(namespaces.html.uri, "dl", dl)
elements.registerElement(namespaces.html.uri, "dt", dt)
elements.registerElement(namespaces.html.uri, "em", em)
elements.registerElement(namespaces.html.uri, "fieldset", fieldset)
elements.registerElement(namespaces.html.uri, "font", font)
elements.registerElement(namespaces.html.uri, "form", form)
elements.registerElement(namespaces.html.uri, "h1", h1)
elements.registerElement(namespaces.html.uri, "h2", h2)
elements.registerElement(namespaces.html.uri, "h3", h3)
elements.registerElement(namespaces.html.uri, "h4", h4)
elements.registerElement(namespaces.html.uri, "h5", h5)
elements.registerElement(namespaces.html.uri, "h6", h6)
elements.registerElement(namespaces.html.uri, "head", head)
elements.registerElement(namespaces.html.uri, "hr", hr)
elements.registerElement(namespaces.html.uri, "html", html, holderClass = HtmlHolder)
elements.registerElement(namespaces.html.uri, "i", i)
elements.registerElement(namespaces.html.uri, "iframe", iframe)
elements.registerElement(namespaces.html.uri, "img", img)
elements.registerElement(namespaces.html.uri, "input", input)
elements.registerElement(namespaces.html.uri, "ins", ins)
elements.registerElement(namespaces.html.uri, "isindex", isindex)
elements.registerElement(namespaces.html.uri, "kbd", kbd)
elements.registerElement(namespaces.html.uri, "label", label)
elements.registerElement(namespaces.html.uri, "legend", legend)
elements.registerElement(namespaces.html.uri, "li", li)
elements.registerElement(namespaces.html.uri, "link", link)
elements.registerElement(namespaces.html.uri, "map", map)
elements.registerElement(namespaces.html.uri, "menu", menu)
elements.registerElement(namespaces.html.uri, "meta", meta)
elements.registerElement(namespaces.html.uri, "noframes", noframes)
elements.registerElement(namespaces.html.uri, "noscript", noscript)
elements.registerElement(namespaces.html.uri, "object", object)
elements.registerElement(namespaces.html.uri, "ol", ol)
elements.registerElement(namespaces.html.uri, "optgroup", optgroup)
elements.registerElement(namespaces.html.uri, "option", option)
elements.registerElement(namespaces.html.uri, "p", p)
elements.registerElement(namespaces.html.uri, "param", param)
elements.registerElement(namespaces.html.uri, "pre", pre)
elements.registerElement(namespaces.html.uri, "q", q)
elements.registerElement(namespaces.html.uri, "s", s)
elements.registerElement(namespaces.html.uri, "samp", samp)
elements.registerElement(namespaces.html.uri, "script", script)
elements.registerElement(namespaces.html.uri, "select", select)
elements.registerElement(namespaces.html.uri, "small", small)
elements.registerElement(namespaces.html.uri, "span", span)
elements.registerElement(namespaces.html.uri, "strike", strike)
elements.registerElement(namespaces.html.uri, "strong", strong)
elements.registerElement(namespaces.html.uri, "style", style)
elements.registerElement(namespaces.html.uri, "sub", sub)
elements.registerElement(namespaces.html.uri, "sup", sup)
elements.registerElement(namespaces.html.uri, "table", table)
elements.registerElement(namespaces.html.uri, "tbody", tbody)
elements.registerElement(namespaces.html.uri, "td", td)
elements.registerElement(namespaces.html.uri, "textarea", textarea)
elements.registerElement(namespaces.html.uri, "tfoot", tfoot)
elements.registerElement(namespaces.html.uri, "th", th)
elements.registerElement(namespaces.html.uri, "thead", thead)
elements.registerElement(namespaces.html.uri, "title", title)
elements.registerElement(namespaces.html.uri, "tr", tr)
elements.registerElement(namespaces.html.uri, "tt", tt)
elements.registerElement(namespaces.html.uri, "u", u)
elements.registerElement(namespaces.html.uri, "ul", ul)
elements.registerElement(namespaces.html.uri, "var", var)
