#! /usr/bin/env python

import pyslet.xml20081126.structures as xml
import pyslet.xmlnames20091208 as xmlns
import pyslet.xsdatatypes20041028 as xsi
import pyslet.html40_19991224 as html
import pyslet.rfc2396 as uri

import pyslet.qtiv2.core as core
import pyslet.qtiv2.content as content
import pyslet.qtiv2.metadata as metadata

import sys


class AssessmentItem(core.QTIElement, core.DeclarationContainer):

    """An assessment item encompasses the information that is presented to a
    candidate and information about how to score the item::

            <xsd:attributeGroup name="assessmentItem.AttrGroup">
                    <xsd:attribute name="identifier" type="string.Type" use="required"/>
                    <xsd:attribute name="title" type="string.Type" use="required"/>
                    <xsd:attribute name="label" type="string256.Type" use="optional"/>
                    <xsd:attribute ref="xml:lang"/>
                    <xsd:attribute name="adaptive" type="boolean.Type" use="required"/>
                    <xsd:attribute name="timeDependent" type="boolean.Type" use="required"/>
                    <xsd:attribute name="toolName" type="string256.Type" use="optional"/>
                    <xsd:attribute name="toolVersion" type="string256.Type" use="optional"/>
            </xsd:attributeGroup>

            <xsd:group name="assessmentItem.ContentGroup">
                    <xsd:sequence>
                            <xsd:element ref="responseDeclaration" minOccurs="0" maxOccurs="unbounded"/>
                            <xsd:element ref="outcomeDeclaration" minOccurs="0" maxOccurs="unbounded"/>
                            <xsd:element ref="templateDeclaration" minOccurs="0" maxOccurs="unbounded"/>
                            <xsd:element ref="templateProcessing" minOccurs="0" maxOccurs="1"/>
                            <xsd:element ref="stylesheet" minOccurs="0" maxOccurs="unbounded"/>
                            <xsd:element ref="itemBody" minOccurs="0" maxOccurs="1"/>
                            <xsd:element ref="responseProcessing" minOccurs="0" maxOccurs="1"/>
                            <xsd:element ref="modalFeedback" minOccurs="0" maxOccurs="unbounded"/>
                    </xsd:sequence>
            </xsd:group>"""
    XMLNAME = (core.IMSQTI_NAMESPACE, 'assessmentItem')
    XMLATTR_adaptive = ('adaptive', xsi.DecodeBoolean, xsi.EncodeBoolean)
    XMLATTR_identifier = 'identifier'
    XMLATTR_label = 'label'
    XMLATTR_timeDependent = (
        'timeDependent', xsi.DecodeBoolean, xsi.EncodeBoolean)
    XMLATTR_title = 'title'
    XMLCONTENT = xmlns.ElementContent

    def __init__(self, parent):
        core.QTIElement.__init__(self, parent)
        core.DeclarationContainer.__init__(self)
        self.identifier = None
        self.title = None
        self.label = None
        self.adaptive = False
        self.timeDependent = False
        self.ResponseDeclaration = []
        self.OutcomeDeclaration = []
        self.TemplateDeclaration = []
        self.TemplateProcessing = None
        self.StyleSheet = []
        self.ItemBody = None
        self.ResponseProcessing = None
        self.QTIModalFeedback = []
        self.metadata = metadata.QTIMetadata(None)

    def GetChildren(self):
        for d in self.ResponseDeclaration:
            yield d
        for d in self.OutcomeDeclaration:
            yield d
        for d in self.TemplateDeclaration:
            yield d
        if self.ItemBody:
            yield self.ItemBody
        if self.ResponseProcessing:
            yield self.ResponseProcessing
        for child in self.QTIModalFeedback:
            yield child

    def ContentChanged(self):
        self.SortDeclarations()

    def SortDeclarations(self):
        """Sort each of the variable declaration lists so that they are in
        identifier order.  This is not essential but it does help ensure that
        output is predictable. This method is called automatically when reading
        items from XML files."""
        self.ResponseDeclaration.sort()
        self.OutcomeDeclaration.sort()
        self.TemplateDeclaration.sort()

    def RenderHTML(self, itemState, htmlParent=None):
        """Renders this item in html, adding nodes to *htmlParent*.  The state
        of the item (e.g., the values of any controls and template variables),
        is taken from *itemState*, a :py:class:`variables.ItemSessionState`
        instance.

        The result is the top-level div containing the item added to the
        htmlParent. If htmlParent is None then a parentless div is created. If
        the item has no itemBody then an empty Div is returned."""
        if self.ItemBody:
            htmlDiv = self.ItemBody.RenderHTML(
                htmlParent, content.HTMLProfile, itemState)
        else:
            if htmlParent:
                htmlDiv = htmlParent.ChildElement(html.Div)
            else:
                htmlDiv = html.Div(None)
        return htmlDiv

    def AddToContentPackage(self, cp, lom, dName=None):
        """Adds a resource and associated files to the content package."""
        resourceID = cp.manifest.GetUniqueID(self.identifier)
        resource = cp.manifest.root.Resources.ChildElement(
            cp.manifest.root.Resources.ResourceClass)
        resource.SetID(resourceID)
        resource.type = core.IMSQTI_ITEM_RESOURCETYPE
        resourceMetadata = resource.ChildElement(resource.MetadataClass)
        # resourceMetadata.AdoptChild(lom)
        # resourceMetadata.AdoptChild(self.metadata.Copy())
        lom.Copy(resourceMetadata)
        self.metadata.Copy(resourceMetadata)
        # Security alert: we're leaning heavily on ValidateIdentifier assuming
        # it returns a good file name
        fPath = (core.ValidateIdentifier(resourceID) +
                 '.xml').encode(sys.getfilesystemencoding())
        if dName:
            fPath = cp.FilePath(dName, fPath)
        fPath = cp.GetUniqueFile(fPath)
        # This will be the path to the file in the package
        fullPath = cp.dPath.join(fPath)
        base = uri.URIFactory.URLFromVirtualFilePath(fullPath)
        if isinstance(self.parent, xml.Document):
            # we are the root so we change the document base
            self.parent.SetBase(base)
        else:
            self.SetBase(base)
        # Turn this file path into a relative URL in the context of the new
        # resource
        href = resource.RelativeURI(base)
        f = cp.File(resource, href)
        resource.SetEntryPoint(f)
        for child in self.GetChildren():
            if isinstance(child, core.QTIElement):
                child.AddToCPResource(cp, resource, {})
        return resource
