#!/usr/bin/env python
"""Definitions of persistent parts of an X12 message.

An X12 message instance is a recursive construction of Loops, sub-Loops and Segments.
The leaf elements are always Segments.  Segments contain, in turn, Composites
and Elements.

There are a number of operations on a Message.

    -   Unmarshalling ("parsing").  This is done by an instance of classes
        defined in the :mod:`X12.parse` package.  The results of parsing is a
        Message Instance, built from classes in this module.

    -   Marshalling.  Each message can marshall itself into a block
        of text.

    -   Persisting.  Since the message instance is build from subclasses
        of :mod:`django.db.models.Model`, a call to :meth:`save()` persists
        the message.

    -   Retrieving.  The  :mod:`X12Message.objects` manager can retreive any
        Message.  Ordinary navigation will fetch Loops and Segments for that
        Message.

This must be changed in two ways to stop using DB-generated PK's.

    - XXX - introduce an application-defined PK,
      at the Message level as the 1st part of all keys in a message structure.

    - XXX - introduce a 2nd part to the key generated by the :class:`Factory` or
      the :class:`X12Message` rather than the key created by the database.

Navigation
==========

Look at :mod:`X12.message` for the XPath-like navigation model.
This leads to a number of navgiation-oriented methods being folded into the
class definitions.

Module Contents
===============

Here are the class definitions within this module.

    -   :class:`X12Message`.  The overall message -- a collection of Loops.

    -   :class:`X12Loop`. A Loop is a collection of Segments and sub-Loops.
        In a SQL implementation, a Loop has an FK to the owning Message, as well
        as an FK to its immediate parent Loop.  Top-level Loops, which belong
        directly to the Message have a NULL parent.

            -   Some Loops are "wrappers": a degenerate case that contain one Segment
                and no subLoops.  This allows a Loop to contain a proper collection of
                Loops.

            -   Some Loops are "explicit": a properly named Loop which contains
                explicit subloops or wrapper loops around.

    -   :class:`X12Segment`. A Segment is a collection of Elements and Composites.
        Segments names are essentially a type name; there can be multiple instances
        within a single Loop, each with different semantics.  In a SQL implementation,
        a Segment has an FK to the owning Message, as well as an FK to the
        immediate parent Loop.

    -   X12Composite.  A Composite is a collection of Elements.  This is
        not actually implemented.  It is merely attributes of a Segment.

    -   X12Element.  An atomic piece of data.  These are identified
        positionally within a segment's source text.
"""
from X12.parse import SegmentToken
import X12.message
from django.db import models
from django.conf import settings
import logging

try:
    debug= settings.MODEL_DEBUG
except AttributeError:
    debug= False

logger= logging.getLogger( __name__ )

class Factory( X12.message.Factory ):
    """Create Django versions of the X12Message structures.

    This object is used by an :mod:`X12.parse` to unmarshall message
    text and create Django objects.
    """
    @staticmethod
    def makeMessage( name, *structure ):
        """Create a new X12Message object.
        
        :param name: name of this Message
        :param structure: the various Loops of this Message
        :returns: X12Message instance
        """
        msg= X12Message( name=name )
        msg.save()
        for part in structure:
            msg.addChild( part )
            part.save()
        return msg
    @staticmethod
    def makeLoop( name, *structure ):
        """Create a new X12Loop object.
        
        :param name: name of this Loop
        :param structure: the various sub-Loops and Segments of this Loop
        :returns: X12Loop instance
        """
        loop= X12Loop( name=name )
        loop.save()
        for part in structure:
            loop.addChild( part )
            part.save()
        return loop
    @staticmethod
    def makeSegment( segmentToken, compositeSep=":", segmentType=None ):
        """Create a Segment from a SegmentToken and an Segment definition.

        Another application can extend this data model to include
        Segment-type specific classes.  That application will
        provide it's own Factory, which is a subclass of this Factory,
        and will override this method to handle Segment-specific bindings.

        :param segmentToken: An :class:`X12.parse.SegmentToken` instance:
            a list-like collection of Element values.  It turns out that a simple
            list of values may also work, if it does NOT have trailing empty
            items omitted.  Real Segment Tokens can have trailing empty items
            omitted.
        :param compositeSep: Composite internal separator from the ISA segment.
        :param segmentType: An :class:`X12.parse.Segment` instance, which
            defines the Elements and Composites of this X12Segment.
        :returns: X12Segment instance
        """
        seg= X12Segment( name=segmentToken[0], text=repr(segmentToken) )
        if segmentType is not None:
            seg.bind( segmentType )
        seg.save()
        return seg

class Location( models.Model ):
    """A named 'location' for a claim, usually geographic, but also possibly
    a line of business.  Often this is a constraint on
    members or providers associated with a claim.
    
    :ivar name: short name of the location
    :ivar description: long description of the location
    """
    name = models.CharField( max_length=16, unique=True )
    description = models.CharField( max_length=96, blank=True, null=True )
    def __str__( self ):
        return self.name
    class Admin:
        pass

class ClaimType( models.Model ):
    """The subtype of the :samp:`837` claim - Professional, Institutional, Dental.
    
    :ivar name: short name of the claim type (usually 1 character)
    :ivar description: long name of the claim type
    """
    name = models.CharField( max_length=8, unique=True )
    description = models.CharField( max_length=32, blank=True, null=True )
    def __str__( self ):
        return self.name
    class Admin:
        pass

class SecondaryCoverage( models.Model ):
    """Any secondary coverage in the claim.
    
    :ivar name: short name of the secondary coverage.
    :ivar description: long name of the secondary coverage.
    """
    name = models.CharField( max_length=16, unique=True )
    description = models.CharField( max_length=96, blank=True, null=True )
    def __str__( self ):
        return self.name
    class Admin:
        pass

class Benefit( models.Model ):
    """The primary benefit for this claim.
    
    :ivar benefit: short name of the benefit
    :ivar description: long description of the benefit.
    """
    benefit = models.CharField( max_length=16, unique=True  )
    description= models.TextField( max_length=96, blank=True, null=True )
    def __str__( self ):
        return self.benefit
    class Admin:
        list_display = ( 'benefit', 'description' )

class TypeOfService( models.Model ):
    """The type of service associated with this benefit.
    A benefit may have more than one type of service.
    The :py:data:`fullName` is the "benefit / type of service" string.

    XXX - needs validator: TOS is just unique for a given benefit, not
    globally unique.

    :ivar benefit: the associated benefit
    :ivar typeOfService: short name of the type of service
    :ivar description: long description of the type of service.
    """
    benefit = models.ForeignKey( Benefit )
    typeOfService = models.CharField( max_length=16, unique=True )
    description = models.TextField( max_length=96, blank=True, null=True )
    def __str__( self ):
        return self.fullName()
    def shortName( self ):
        return "%s-%s" % ( self.benefit.benefit, self.typeOfService )
    shortName.short_description = "Short Name"
    def fullName( self ):
        ben = self.benefit.description if self.benefit.description is not None else self.benefit.benefit
        tos = self.description if self.description is not None else self.typeOfService
        return "%s / %s" % ( ben, tos )
    fullName.short_description = "Full Description"
    class Meta:
        verbose_name_plural = 'Types of service'
    class Admin:
        list_display = ( 'benefit', 'typeOfService', 'fullName', )
        list_display_links = ( 'typeOfService', )

class GroupStatus( models.Model ):
    """The status of a claim group.
    This defines what processing is allowed for claim group.
    Claims in a :samp:`Base` group, for instance, cannot be changed.
    Other status may have additional or different features.
    
    :ivar name: short status string
    :ivar description: definition of the status
    :ivar can_change: can claims in this group be changed?
    """
    name= models.CharField( max_length=16, unique=True )
    description= models.CharField( max_length=96, blank=True, null=True )
    can_change = models.BooleanField( default=True )
    def __str__( self ):
        return self.description
    class Meta:
        verbose_name_plural= 'GroupStatus'
    class Admin:
        list_display = ( 'name', 'description', 'can_change' )

class ClaimGroup( models.Model ):
    """A Group of claims.  A group of claims has a status
    which collects groups together for different kinds of testing.
    A group also may have a dynamic processing state which reflects
    whether or not it has been sent to the claim processing system.
    
    :ivar name: name of the claim group.  :samp:`Base` claims have
        a system-supplied default name.  All other claim status
        have user-supplied names.
    :ivar description: long description of this claim group.
    :ivar status: the Group Status for this Claim Group.
    :ivar owner: the User who created this Claim group.
    :ivar created: the datetime of creation of this group definition.
    :ivar modified: the datetime of modification of the group definition.
    """
    name= models.CharField( max_length=64, unique=True )
    description= models.TextField( blank=True, null=True )
    status= models.ForeignKey( GroupStatus )
    owner= models.CharField( max_length=16 ) # XXX - FK to auth.Users?
    created= models.DateTimeField( auto_now_add=True )
    modified= models.DateTimeField( auto_now=True )
    def count( self ):
        return self.x12message_set.count()
    count.short_description= 'Count'
    def __str__( self ):
        return self.name
    class Admin:
        list_display= ( 'name', 'description', 'status', 'owner', 'count', )
        list_display_links= ( 'name', )
        list_filter= ( 'name', 'status', 'owner', )

class ClaimProperties( models.Model ):
    """Selected properties of a claim.
    XXX - add source system.
    
    :ivar claimType: one of the defined L{ClaimType}s for this claim
    :ivar secondaryCoverage: one of the defined L{SecondaryCoverage}s for this claim
    :ivar location: one of the defined L{Location}s for this claim
    :ivar typeOfService: one of the define L{TypeOfService}s for this claim
    :ivar created: creation datetime for this property
    :ivar modified: modification datetime for this property
    """
    claimType= models.ForeignKey( ClaimType )
    secondaryCoverage= models.ForeignKey( SecondaryCoverage )
    location= models.ForeignKey( Location )
    typeOfService= models.ForeignKey( TypeOfService )
    created= models.DateTimeField( auto_now_add=True )
    modified= models.DateTimeField( auto_now=True )
    def shortName( self ):
        return "%s/%s/%s/%s" % (
            self.claimType.name, self.secondaryCoverage.name,
            self.location.name, self.typeOfService.shortName() )
    def fullName( self ):
        return "%s | %s | %s | %s" % (
            self.claimType.description, self.secondaryCoverage.description,
            self.location.description, self.typeOfService.fullName() )
    def __str__( self ):
        return self.fullName()
    class Meta:
        verbose_name_plural = 'Claim properties'
    class Admin:
        list_display= ( 'claimType', 'secondaryCoverage', 'location', 'benefit', 'typeOfService', 'count' )
        list_display_links= ( 'count', )
        list_filter= ( 'claimType', 'secondaryCoverage', 'location', 'typeOfService', )
    def benefit( self ):
        return self.typeOfService.benefit
    benefit.short_description= 'Benefit'
    def count( self ):
        return self.x12message_set.count()
    count.short_description= 'Count'

class X12Message( models.Model ):
    """An overall X12 Message: essentially a collection of Loops.
    To depict a unique message we can display unique internal identifiers
    tucked away inside the X12 structure, or the Database-assigned ID field,
    or an external identifier assigned at load time.

    Two of these are relatively unpleasant.

    It's challenging to provide the internal identifiers for a message
    since a message is assembled in pieces. We don't have the ISA
    segment or BHT segment until long after the message was initially
    created and assigned a internal ID.

    The internal DB ID is assigned by the save() operation, making it difficult
    to build a message from the top down.  We have to save the empty X12Message
    row in order to correctly associate X12Loops with it.  Then we have to
    save the X12Loops to correctly associate X12Segment instances.

    Therefore, we require a unique message ID to be supplied at load time.
    This is the physical PK -- a DB defined PK is not used.  Each Loop
    gets a PK generated by our application, not a DB-defined PK, so that
    we can build an entire message structure before deciding to persist it.

    :ivar name: Name of this message. XXX - replace with ID as PK.
    :ivar properties: Claim Properties for this message.
    """
    name= models.CharField( max_length=32, editable=False )
    group= models.ForeignKey( ClaimGroup, blank=True, null=True )
    properties= models.ForeignKey( ClaimProperties, blank=True, null=True, )
    created= models.DateTimeField( auto_now_add=True )
    modified= models.DateTimeField( auto_now=True )
    class Admin:
        list_display= ( 'getISAid', 'getBHTid', )
    def __init__( self, *arg, **kw ):
        super( X12Message, self ).__init__( *arg, **kw )
        self.thePosition= 0
    def __str__( self ):
        return "%s %s" % ( self.name, ", ".join( map(str,self.x12loop_set.all()) ) )
    def getISAid( self ):
        """Try to get ID information from the ISA segment.
        ISA09 + ISA10 + ISA13 provide date, time and an ID."""
        try:
            isa= self.descendant( 'segment', "ISA" )[0]
            return ( isa.getByPos(9), isa.getByPos(10), isa.getByPos(13) )
        except Exception, e:
            logger.exception( "getISAid" )
            return None
    getISAid.short_description = 'ISA Date, Time and ID'
    def getBHTid( self ):
        """Try to get ID information from the BHT segment.
        BHT03 might be a unique ID."""
        try:
            bht= self.descendant( 'segment', "BHT" )[0]
            return bht.getByPos(3)
        except Exception, e:
            logger.exception( "getBHTid" )
            return None
    getBHTid.short_description = 'BHT Segment ID'
    def segs( self ):
        """Create the flat list of Segments under this Message.
        
        :returns: list of X12Segment instances.
        """
        segList= []
        for p in self.x12loop_set.all():
            segList.extend( p.segs() )
        return segList
    def marshall( self, segSep="~", eltSep="*" ):
        """Marshall this message into an X12 string.
        
        :param segSep: (optional) Segment Terminator, usually :samp:`~`
        :param eltSep: (optional) Element Separator, usually :samp:`*`
        :returns: String with this X12 message marshalled
        """
        segments= self.segs()
        return segSep.join( [ s.marshall(eltSep) for s in segments ] ) + segSep
    def addChild( self, aLoop ):
        """Add a loop to this Message.
        
        :param aLoop: an X12Loop instance.
        """
        self.save()
        aLoop.message= self
        aLoop.thePosition= self.x12loop_set.count() + 1
        aLoop.theParent= None
        self.x12loop_set.add( aLoop )
        aLoop.save()
    def matches( self, test, name=None, pos= None ):
        """The Xpath self axis"""
        testType= _test2type[test.lower()]
        if isinstance(self, testType ):
            c1, c2 = True, True
            if name is not None:
                c1= self.name == name
            if pos is not None:
                c2= self.thePosition == pos
            return c1 and c2
        return False
    def child( self, test, name=None, pos=None ):
        result= []
        for part in self.x12loop_set.filter( theParent__isnull = True ):
            aMatch= part.matches( test, name, pos )
            if aMatch:
                result.append( part )
        return result
    def descendant( self, test, name=None, pos=None ):
        result= []
        for part in self.x12loop_set.filter( theParent__isnull = True ):
            if part.matches( test, name, pos ):
                result.append( part )
            result.extend( part.descendant(test,name,pos) )
        return result
    def parent( self, test, name=None, pos=None ):
        return []
    def ancestor( self, test, name=None, pos=None ):
        return []

class TestConstraints( models.Model ):
    """The constraints that define how to modify this claim
    for unit test purposes.
    
    :ivar gender: allowed member genders.
    :ivar age_low: minimum member age.
    :ivar age_high: maximum member age.
    """
    gender= models.CharField( max_length=8 ) # XXX - Choices- M, F, U
    age_low= models.PositiveIntegerField( default=16 )
    age_high= models.PositiveIntegerField( default=125 )
    message = models.OneToOneField( X12Message, related_name='constraints' )
    def __str__( self ):
        return "%s | %s-%s" % ( self.gender, self.age_low, self.age_high )
    class Meta:
        verbose_name_plural = 'Test constraints'
    class Admin:
        list_display= ( 'gender', 'age_low', 'age_high', )
        list_filter= ( 'gender', 'age_low', 'age_high', )

class X12Loop( models.Model ):
    """A Loop in an X12 message; it contains either subLoops or a Segment.
    A Loop which contains a Segment is a "Wrapper" and has no other information.
    A Loop which contains subloops is "Explicit" and has a name and could have
    other attributes.

    :ivar name: Name of this Loop
    :ivar kind: Kind of loop: either 'W' for wrappers or 'E' for explicit
    :ivar message: The Message to which this Loop belongs.
        This is set by addChild.
    :ivar sequenceMessage: the sequence of this Loop within the overall Message.
        *XXX - not fully supported.*
    :ivar parent: the parent Loop (or None) if this is a top loop in a message.
        This is set by addChild.
    :ivar thePosition: the sequence of this Loop within the parent.
        This is set by addChild.
    :ivar occurrence: the occurrence of this Loop type within a parent loop.
        This is set by the parser when handling multiple occurrences of loops or
        segments.
    
    ..  todo:: Admin interface for message children should be admin.StackedInline.
    """
    LOOP_KINDS = ( ('W','Wrapper'), ('E','Explicit') )
    name = models.CharField( max_length=16, blank=True, null=True )
    kind = models.CharField( max_length=8, choices=LOOP_KINDS, default='E' )
    message = models.ForeignKey( X12Message, blank=True, null=True )
    messagePosition = models.PositiveIntegerField( default=0 )
    theParent = models.ForeignKey( 'self', blank=True, null=True, related_name='subloop_set' )
    thePosition = models.PositiveIntegerField( default=0 )
    occurrence= models.PositiveIntegerField( default=0 )
    class Admin:
        pass
    class Meta:
        ordering = [ 'thePosition', ]
    def children( self ):
        """Returns the list of meaningful children -- supressing unnamed Wrapper Loops.
        :returns: list of X12Loops mingled with X12Segments in the correct order.
        """
        structure= []
        for p in self.subloop_set.all():
            if p.kind == "W":
                structure.extend( p.x12segment_set.all() )
            else:
                structure.append( p )
        return structure
    def __str__( self ):
        return "%s : ( %s )" % (
            self.name,
            ", ".join(map(str,self.children())) )
    def segs( self ):
        """Create a flat list of Segments under this Loop.
        This is the list of segments used for marshalling this message.
        
        :returns: list of X12Segments.
        """
        segList= []
        for p in self.subloop_set.all():
            if p.kind == "W":
                # Skip over Wrappers and pull up their lonely Segment
                segList.extend( p.x12segment_set.all() )
            else:
                # Expand SubLoops
                segList.extend( p.segs() )
        return segList
    def addChild( self, part ):
        """Add either a Segment or a Loop to this Loop, setting overal
        message, parent and position of this child.

        X12Segments can only be added under "Wrapper" Loops, where
        Loops can be added directly to Loops.  Consequently, we need
        to delegate the work to the part. X12Segments will
        check for a wrapper; X12Loops will simply add themselves.

        NOTE.  Can't add children unless the parent has a DB ID, which
        is assigned at SAVE time; hence, an initial save.

        :param part: the X12Segment or X12Loop to add to this Loop.
        """
        self.save()
        part.addToParent( self )
        part.save()
    def addToParent( self, parent ):
        """Adds ourself to a parent Explicit Loop.
        An X12Segment will do this in a different way; a Segment must
        assure that it's being added to a Wrapper loop.  We're an X12Loop,
        however, and can add ourself to Explicit loops, only.
        
        :param parent: the parent loop to which we're adding ourself.
        """
        if debug:
            logger.debug( "X12Loop: adding Loop %r to Loop %s %s" % ( self, parent.name, parent.kind ) )
        if parent.kind == "W":
            raise Error( "Problem: Attempt to a subLoop to a wrapper" )
        self.message= parent.message
        self.thePosition=parent.subloop_set.count() + 1
        parent.subloop_set.add( self )
    def segmentWrapper( self ):
        """Returns a Wrapper X12Loop for a Segment to add itself to.
        If we are a Wrapper loop, we return ourself.
        If we are an Explicit loop, we need to add a Wrapper and then
        return that Wrapper.
        
        :returns: X12Loop of kind=="W"
        """
        if self.kind == "W":
            # We are a wrapper loop, all we can contain is this X12Segment
            assert self.subloop_set.count() == 0
            return self
        elif self.kind == "E":
            # We are an explicit loop, all we can contain are subloops
            # We must add a "wrapper" Loop for this X12Segment
            wrapper= X12Loop(
                name=None, kind="W", message=self.message,
                thePosition= self.subloop_set.count() + 1
                )
            wrapper.save()
            self.subloop_set.add( wrapper )
            assert self.x12segment_set.count() == 0
            return wrapper
        else:
            raise Error( "Invalid Loop.kind")
    def matches( self, test, name=None, pos= None ):
        """The Xpath self axis"""
        testType= _test2type[test.lower()]
        if isinstance(self, testType ):
            c1, c2 = True, True
            if name is not None:
                c1= self.name == name
            if pos is not None:
                c2= self.thePosition == pos
            return c1 and c2
        return False
    def child( self, test, name=None, pos=None ):
        result= []
        for part in self.subloop_set.all( ):
            if part.kind == "W": # Wrappers need to be treated as Segments
                part= part.x12segment_set.all()[0]
            aMatch= part.matches( test, name, pos )
            if aMatch:
                result.append( part )
        return result
    def descendant( self, test, name=None, pos=None ):
        result= []
        for part in self.subloop_set.all( ):
            if part.kind == "W": # Wrappers need to be treated as Segments
                part= part.x12segment_set.all()[0]
            if part.matches( test, name, pos ):
                result.append( part )
            result.extend( part.descendant(test,name,pos) )
        return result
    def parent( self, test, name=None, pos=None ):
        result= []
        if self.theParent.matches( test, name, pos ):
          result.append( self.theParent )
        return result
    def ancestor( self, test, name=None, pos=None ):
        result= []
        if self.theParent:
            if self.theParent.matches( test, name, pos ):
                result.append( self.theParent )
            result.extend( self.theParent.ancestor(test,name,pos) )
        return result

class X12Segment( models.Model ):
    """A generic segment in an X12 message, created by a parser from a segmentToken.
    Type-specific Segments will have a 1:1 relationship with this
    Segment, but will have detailed Element definitions instead of this
    generic definition.

    To make this generic version work, we store the entire :class:`X12.parse.SegmentToken`
    as a big-ole block of text in the database.  Methods within this object
    unpack the text block to access individual Elements.  Further, if a change
    is made, the text block is repacked from the element list.

    We could use a Django Custom Field definition for this.  It would make
    the conversion back and forth between list and database-friendly String
    transparent.

    :ivar elements: internal list of elements derived from self.text.
    :ivar segType: internal reference to :class:`X12.parse.Segment` definition.
    :ivar name: Name of this Segment
    :ivar text: SegmentToken text for this segment.
    :ivar message: top-most Message which contains this Segment
    :ivar sequence: Message sequence of this Segment within the overall Message.
    :ivar loop: parent Loop for this Segment.  Must be a Wrapper kind of Loop.
    :ivar occurrence: Occurrence of this segment type within the parent Loop.
    """
    name = models.CharField( max_length=16 )
    text = models.TextField( max_length=512 )
    message= models.ForeignKey( X12Message, blank=True, null=True )
    messagePosition = models.PositiveIntegerField( default=0 )
    loop= models.ForeignKey( X12Loop, blank=True, null=True )
    occurrence= models.PositiveIntegerField( default= 0 )
    def __init__( self, *arg, **kw ):
        """Initialize the internal variables: element and segType."""
        super( X12Segment, self ).__init__(*arg,**kw)
        self.elements= None
        self.segType= None
    def __str__( self ):
        return str( self.text )
    def bind( self, segment ):
        """Bind an :class:`X12.parse.Segment` to this data.
        
        :param segment: An :class:`X12.parse.Segment` instance
        """
        self.segType= segment
    def segs( self ):
        """Returns this Segment in a list, consistent with X12Loop and X12Message."""
        return [ self ]
    def getByPos( self, position ):
        """Get the value of an Element by the position number.
        
        :param position: position of the Element
        :returns: string value of the Element.
        """
        if self.elements is None:
            self.elements= eval(self.text)
        return self.elements[int(position)]
    def setByPos( self, position, value ):
        """Set the value of an Element by the position number.
        This updates the text attribute of the persistent object.
        
        :param position: position of the Element
        :param value: String value to replace
        """
        if self.elements is None:
            self.elements= eval(self.text)
        while len(self.elements) <= int(position):
            self.elements.append("")
        self.elements[int(position)]= value
        self.text= repr(self.elements)
    def positionOf( self, name ):
        """Returns the position of a named Element.
        This only works if this X12Segment is bound to an :class:`X12.parse.Segment`.
        
        :param name: Element name
        :returns: numberic position of this Element.
        """
        if self.segType is None:
            raise NotImplementedError( "No Segment bound to this data." )
        for en in range(len(self.segType.structure)):
            if name == self.segType.structure[en].name:
                return en+1
        raise ValueError( "Name %s Is Unknown in Segment %s" % ( name, self.name ))
    def getByName( self, name ):
        """Get the value of an Element by the element name.
        This only works if this X12Segment is bound to an :class:`X12.parse.Segment`.
        
        :param name: name of the Element
        :returns: string value of the Element.
        """
        return self.getByPos( self.positionOf(name) )
    def marshall( self, eltSep="*" ):
        """Marshalls this Segment's elements as a text string.
        
        :returns: String value of this Segment.
        """
        if self.elements is None:
            self.elements= eval(self.text)
        while len(self.elements[-1]) == 0:
            del self.elements[-1]
        txt= eltSep.join( self.elements )
        self.elements= None
        return txt
    def addToParent( self, parentLoop ):
        """X12Segment adding itself to a parent Wrapper Loop.
        Or -- if the parent is Explicit -- adding the needed Wrapper.
        We ask the parent for the "segmentWrapper": if the parent is a
        Wrapper, that's what is returned; if the parent is Explicit,
        the parent inserts the necessary wrapper and returns the wrapper.
        
        :param parentLoop: parent X12Loop to which we must add ourself.
        """
        if debug:
            logger.debug( "X12Loop: adding Segment %r to Loop %s %s" % ( self, parentLoop.name, parentLoop.kind ) )
        self.message= parentLoop.message
        addTo= parentLoop.segmentWrapper()
        self.thePosition= addTo.thePosition
        addTo.x12segment_set.add( self )
    def matches( self, test, name=None, pos= None ):
        """The Xpath self axis"""
        testType= _test2type[test.lower()]
        if isinstance(self, testType ):
            c1, c2 = True, True
            if name is not None:
                c1= self.name == name
            if pos is not None:
                c2= self.loop.thePosition == pos
            return c1 and c2
        return False
    def parent( self, test, name=None, pos=None ):
        # Immediate parent is a Wrapper, grand-parent is an Explicit loop
        result= []
        if self.loop.theParent.matches( test, name, pos ):
          result.append( self.loop.theParent )
        return result
    def ancestor( self, test, name=None, pos=None ):
        # Immediate parent is a Wrapper, grand-parent is an Explicit loop
        result= []
        if self.loop.theParent:
            if self.loop.theParent.matches( test, name, pos ):
                result.append( self.loop.theParent )
            result.extend( self.loop.theParent.ancestor(test,name,pos) )
        return result
    def child( self, test, name=None, pos=None ):
        return []
    def descendant( self, test, name=None, pos=None ):
        return []

_test2type = { "loop" : X12Loop, "segment" : X12Segment, "message" : X12Message }
