cns = self.constraint = pb.constraints.new(self.type)
        cns.name = self.name
        cns.influence = self.influence
        cns.active = self.active
        cns.show_expanded = self.expanded
        cns.target_space = self.targsp
        cns.owner_space = self.ownsp
        return cns

#  Authors:             Thomas Larsson
#  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,

import math

from .flags import *

class CConstraint:
    def __init__(self, type, name, flags, inf):
        self.constraint = None
        self.name = name
        self.type = type
        self.influence = inf
        self.active = (flags & C_ACT == 0)
        self.expanded = (flags & C_EXP != 0)

        ow = flags & C_OW_MASK
        if ow == 0:
            self.ownsp = 'WORLD'
        elif ow == C_OW_LOCAL:
            self.ownsp = 'LOCAL'
        elif ow == C_OW_LOCPAR:
            self.ownsp = 'LOCAL_WITH_PARENT'
        elif ow == C_OW_POSE:
            self.ownsp = 'POSE'

        tg = flags & C_TG_MASK
        if tg == 0:
            self.targsp = 'WORLD'
        elif tg == C_TG_LOCAL:
            self.targsp = 'LOCAL'
        elif tg == C_TG_LOCPAR:
            self.targsp = 'LOCAL_WITH_PARENT'
        elif tg == C_TG_POSE:
            self.targsp = 'POSE'

    def update(self, parser, bone):
        return
        raise NameError("Unknown constraint: bone %s cns %s type %s" % (bone.name, self.name, self.type))

    def __repr__(self):
        return ("<CConstraint %s %s>" % (self.name, self.type))

    def build(self, pb, rig, parser):

#   Constraint subclasses

class CIkConstraint(CConstraint):
    def __init__(self, flags, inf, data, lockLoc, lockRot):
        CConstraint.__init__(self, "IK", data[0], flags, inf)
        self.subtar = data[1]
        self.chainlen = data[2]
        self.pole = data[3]
        if self.pole:
            (self.angle, self.ptar) = self.pole
        else:
            (self.angle, self.ptar) = (0, None)
        (self.useLoc, self.useRot, self.useStretch) = data[4]
        if len(data) > 5:
            self.useTail = data[5]
        else:
            self.useTail = True
        self.lockLoc = lockLoc
        self.lockRot = lockRot
        (lockRotX, lockRotY, lockRotZ) = lockRot

    def build(self, pb, rig, parser):
        cns = CConstraint.build(self, pb, rig, parser)
        cns.target = rig
        cns.subtarget = self.subtar
        cns.use_tail = self.useTail

        cns.reference_axis = 'BONE'
        cns.chain_count =  self.chainlen
        cns.ik_type = 'COPY_POSE'
        cns.iterations = 500
        cns.limit_mode = 'LIMITDIST_INSIDE'
        cns.orient_weight = 1

        if self.pole:
            cns.pole_angle = self.angle*D
            cns.pole_subtarget = self.ptar
            cns.pole_target = rig

        cns.use_location = self.useLoc
        cns.use_rotation = self.useRot
        cns.use_stretch = self.useStretch
        cns.weight = 1

    def update(self, parser, bone):
        if self.chainlen == 1:
            target = parser.bones[self.subtar]
            head = bone.getHead()
            goal = target.getHead()
            vec = goal - head
            dist = math.sqrt(vec.dot(vec))
            goal = head + vec*(bone.length/dist)
            bone.stretchTo(goal, False)

            if self.ptar:
                pole = parser.bones[self.ptar].getHead()
                bone.poleTargetCorrect(head, goal, pole, self.angle)
        else:
            raise NameError("IK chainlen %d %s" % (self.chainlen, bone.name))

class CActionConstraint(CConstraint):
    def __init__(self, flags, inf, data):
        CConstraint.__init__(self, "ACTION", data[0], flags, inf)
        self.action = data[1]
        self.subtar = data[2]
        self.channel = data[3]
        (self.sframe, self.eframe) = data[4]
        (self.amin, self.amax) = data[5]

    def build(self, pb, rig, parser):
        cns = CConstraint.build(self, pb, rig, parser)
        scale = parser.scale
        cns.target = rig
        cns.action = self.action
        cns.frame_start = self.sframe
        cns.frame_end = self.eframe
        if channel[0:3] == 'LOC':
            cns.maximum = self.amax*scale
            cns.minimum = self.amin*scale
        else:
            cns.maximum = self.amax
            cns.minimum = self.amin
        cns.subtarget = self.subtar
        cns.transform_channel = self.channel

class CCopyRotConstraint(CConstraint):
    def __init__(self, flags, inf, data):
        CConstraint.__init__(self, "COPY_ROTATION", data[0], flags, inf)
        self.subtar = data[1]
        self.use = data[2]
        self.invert = data[3]
        self.useOffs = data[4]

    def build(self, pb, rig, parser):
        cns = CConstraint.build(self, pb, rig, parser)
        cns.target = rig
        cns.use_x,cns.use_y,cns.use_z = self.use
        cns.invert_x,cns.invert_y,cns.invert_z = self.invert
        if isinstance(self.subtar, tuple):
            bname1,bname2 = self.subtar
            if bname1 in rig.data.bones.keys():
                cns.subtarget = bname1
            else:
                cns.subtarget = bname2
        else:
            cns.subtarget = self.subtar
        cns.use_offset = self.useOffs

class CCopyLocConstraint(CConstraint):
    def __init__(self, flags, inf, data):
        CConstraint.__init__(self, "COPY_LOCATION", data[0], flags, inf)
        self.subtar = data[1]
        self.use = data[2]
        self.invert = data[3]
        self.head_tail = data[4]
        self.useOffs = data[5]

    def build(self, pb, rig, parser):
        cns = CConstraint.build(self, pb, rig, parser)
        cns.target = rig
        cns.use_x,cns.use_y,cns.use_z = self.use
        cns.invert_x,cns.invert_y,cns.invert_z = self.invert
        cns.head_tail = self.head_tail
        cns.subtarget = self.subtar
        cns.use_offset = self.useOffs

class CCopyScaleConstraint(CConstraint):
    def __init__(self, flags, inf, data):
        CConstraint.__init__(self, "COPY_SCALE", data[0], flags, inf)
        self.subtar = data[1]
        self.use = data[2]
        self.useOffs = data[3]

    def build(self, pb, rig, parser):
        cns = CConstraint.build(self, pb, rig, parser)
        cns.target = rig
        cns.use_x,cns.use_y,cns.use_z = self.use
        cns.subtarget = self.subtar
        cns.use_offset = self.useOffs

class CCopyTransConstraint(CConstraint):
    def __init__(self, flags, inf, data):
        CConstraint.__init__(self, "COPY_TRANSFORMS", data[0], flags, inf)
        self.subtar = data[1]
        self.head_tail = data[2]

    def build(self, pb, rig, parser):
        cns = CConstraint.build(self, pb, rig, parser)
        cns.target = rig
        cns.head_tail = self.head_tail
        cns.subtarget = self.subtar

class CLimitRotConstraint(CConstraint):
    def __init__(self, flags, inf, data):
        CConstraint.__init__(self, "LIMIT_ROTATION", data[0], flags, inf)
        (self.xmin, self.xmax, self.ymin, self.ymax, self.zmin, self.zmax) = data[1]
        (self.usex, self.usey, self.usez) = data[2]
        self.ltra = (flags & C_LTRA != 0)

    def build(self, pb, rig, parser):
        cns = CConstraint.build(self, pb, rig, parser)
        cns.use_transform_limit = self.ltra
        cns.max_x = self.xmax*D
        cns.max_y = self.ymax*D
        cns.max_z = self.zmax*D
        cns.min_x = self.xmin*D
        cns.min_y = self.ymin*D
        cns.min_z = self.zmin*D
        cns.use_limit_x = self.usex
        cns.use_limit_y = self.usey
        cns.use_limit_z = self.usez

class CLimitLocConstraint(CConstraint):
    def __init__(self, flags, inf, data):
        CConstraint.__init__(self, "LIMIT_LOCATION", data[0], flags, inf)
        (self.xmin, self.xmax, self.ymin, self.ymax, self.zmin, self.zmax) = data[1]
        (self.useminx, self.usemaxx, self.useminy, self.usemaxy, self.useminz, self.usemaxz) = data[2]

    def build(self, pb, rig, parser):
        cns = CConstraint.build(self, pb, rig, parser)
        scale = parser.scale

        cns.use_transform_limit = True
        cns.max_x = self.xmax*scale
        cns.max_y = self.ymax*scale
        cns.max_z = self.zmax*scale
        cns.min_x = self.xmin*scale
        cns.min_y = self.ymin*scale
        cns.min_z = self.zmin*scale
        cns.use_max_x = self.usemaxx
        cns.use_max_y = self.usemaxy
        cns.use_max_z = self.usemaxz
        cns.use_min_x = self.useminx
        cns.use_min_y = self.useminy
        cns.use_min_z = self.useminz

class CLimitScaleConstraint(CConstraint):
    def __init__(self, flags, inf, data):
        CConstraint.__init__(self, "LIMIT_SCALE", data[0], flags, inf)
        (self.xmin, self.xmax, self.ymin, self.ymax, self.zmin, self.zmax) = data[1]
        (self.usex, self.usey, self.usez) = data[2]

    def build(self, pb, rig, parser):
        cns = CConstraint.build(self, pb, rig, parser)
        cns.max_x = self.xmax
        cns.max_y = self.ymax
        cns.max_z = self.zmax
        cns.min_x = self.xmin
        cns.min_y = self.ymin
        cns.min_z = self.zmin
        cns.use_max_x = self.usex
        cns.use_max_y = self.usey
        cns.use_max_z = self.usez
        cns.use_min_x = self.usex
        cns.use_min_y = self.usey
        cns.use_min_z = self.usez

class CTransformConstraint(CConstraint):
    def __init__(self, flags, inf, data):
        CConstraint.__init__(self, "TRANSFORM", data[0], flags, inf)
        self.subtar = data[1]
        self.map_from = data[2]
        self.from_min = data[3]
        self.from_max = data[4]
        self.map_to_from = data[5]
        self.map_to = data[6]
        self.to_min = data[7]
        self.to_max = data[8]

    def build(self, pb, rig, parser):
        cns = CConstraint.build(self, pb, rig, parser)
        cns.target = rig
        cns.subtarget = self.subtar
        cns.map_from = self.map_from
        cns.from_min_x = self.from_min[0]
        cns.from_min_y = self.from_min[1]
        cns.from_min_z = self.from_min[2]
        cns.from_max_x = self.from_max[0]
        cns.from_max_y = self.from_max[1]
        cns.from_max_z = self.from_max[2]
        cns.map_to = self.map_to
        cns.map_to_x_from = self.map_to_from[0]
        cns.map_to_y_from = self.map_to_from[1]
        cns.map_to_z_from = self.map_to_from[2]
        cns.to_min_x = self.to_min[0]
        cns.to_min_y = self.to_min[1]
        cns.to_min_z = self.to_min[2]
        cns.to_max_x = self.to_max[0]
        cns.to_max_y = self.to_max[1]
        cns.to_max_z = self.to_max[2]

class CDampedTrackConstraint(CConstraint):
    def __init__(self, flags, inf, data):
        CConstraint.__init__(self, "DAMPED_TRACK", data[0], flags, inf)
        self.subtar = data[1]
        self.track = data[2]
        self.headtail = data[3]

    def build(self, pb, rig, parser):
        cns = CConstraint.build(self, pb, rig, parser)
        cns.target = parser.name
        cns.subtarget = self.subtar
        cns.head_tail = self.headtail
        cns.track_axis = self.track

class CLockedTrackConstraint(CConstraint):
    def __init__(self, flags, inf, data):
        CConstraint.__init__(self, "LOCKED_TRACK", data[0], flags, inf)
        self.subtar = data[1]
        self.trackAxis = data[2]

    def build(self, pb, rig, parser):
        cns = CConstraint.build(self, pb, rig, parser)
        cns.target = rig
        cns.subtarget = self.subtar
        cns.track_axis = self.trackAxis

class CStretchToConstraint(CConstraint):
    def __init__(self, flags, inf, data, parser=None):
        CConstraint.__init__(self, "STRETCH_TO", data[0], flags, inf)
        self.subtar = data[1]
        self.head_tail = data[2]
        self.bulge = data[3]
        if (parser != None) and (len(data) > 4):
            if isinstance(data[4], tuple):
                start,end = data[4]
                self.rest_length = parser.parser.distance(start,end)
            else:
                self.rest_length = data[4]
        else:
            self.rest_length = None
        if flags & C_VOLXZ:
            self.volume = 'VOLUME_XZX'
        elif flags & C_VOLX:
            self.volume = 'VOLUME_X'
        elif flags & C_VOLZ:
            self.volume = 'VOLUME_Z'
        else:
            self.volume = 'NO_VOLUME'
        if flags & C_PLANEZ:
            self.axis = 'PLANE_Z'
        else:
            self.axis = 'PLANE_X'

    def build(self, pb, rig, parser):
        cns = CConstraint.build(self, pb, rig, parser)
        scale = parser.scale
        cns.target = rig
        cns.bulge = self.bulge
        cns.head_tail = self.head_tail
        cns.keep_axis = self.axis
        cns.subtarget = self.subtar
        cns.volume = self.volume
        if self.rest_length != None:
            cns.rest_length = self.rest_length*scale

    def update(self, parser, bone):
        target = parser.bones[self.subtar]
        goal = (1-self.head_tail)*target.getHead() + self.head_tail*target.getTail()
        bone.stretchTo(goal, True)

class CTrackToConstraint(CConstraint):
    def __init__(self, flags, inf, data):
        CConstraint.__init__(self, "TRACK_TO", data[0], flags, inf)
        self.subtar = data[1]
        self.head_tail = data[2]
        self.track_axis = data[3]
        self.up_axis = data[4]
        self.use_target_z = data[5]

    def build(self, pb, rig, parser):
        cns = CConstraint.build(self, pb, rig, parser)
        cns.target = rig
        cns.head_tail = self.head_tail
        cns.track_axis = self.track_axis
        cns.up_axis = self.up_axis
        cns.subtarget = self.subtar
        cns.use_target_z = self.use_target_z

class CLimitDistConstraint(CConstraint):
    def __init__(self, flags, inf, data):
        CConstraint.__init__(self, "LIMIT_DISTANCE", data[0], flags, inf)
        self.subtar = data[1]
        self.limit_mode = data[2]

    def build(self, pb, rig, parser):
        cns = CConstraint.build(self, pb, rig, parser)
        cns.target = rig
        cns.limit_mode = self.limit_mode
        cns.subtarget = self.subtar

class CChildOfConstraint(CConstraint):
    def __init__(self, flags, inf, data):
        CConstraint.__init__(self, "CHILD_OF", data[0], flags, inf)
        self.subtar = data[1]
        (self.locx, self.locy, self.locz) = data[2]
        (self.rotx, self.roty, self.rotz) = data[3]
        (self.scalex, self.scaley, self.scalez) = data[4]

    def build(self, pb, rig, parser):
        cns = CConstraint.build(self, pb, rig, parser)
        cns.target = rig
        cns.subtarget = self.subtar
        cns.use_location_x = self.locx
        cns.use_location_y = self.locy
        cns.use_location_z = self.locz
        cns.use_rotation_x = self.rotx
        cns.use_rotation_y = self.roty
        cns.use_rotation_z = self.rotz
        cns.use_scale_x = self.scalex
        cns.use_scale_y = self.scaley
        cns.use_scale_z = self.scalez

class CSplineIkConstraint(CConstraint):
    def __init__(self, flags, inf, data):
        CConstraint.__init__(self, "SPLINE_IK", data[0], flags, inf)
        self.target = data[1]
        self.count = data[2]

    def build(self, pb, rig, parser):
        cns = CConstraint.build(self, pb, rig, parser)
        cns.target = rig
        cns.chain_count = self.count
        cns.use_chain_offset = False
        cns.use_curve_radius = True
        cns.use_even_divisions = False
        cns.use_y_stretch = True
        cns.xz_scale_mode = None

class CFloorConstraint(CConstraint):
    def __init__(self, flags, inf, data):
        CConstraint.__init__(self, "FLOOR", data[0], flags, inf)
        self.subtar = data[1]
        self.floor_location = data[2]
        self.offset = data[3]
        self.use_rotation = data[4]
        self.use_sticky = data[5]

    def build(self, pb, rig, parser):
        cns = CConstraint.build(self, pb, rig, parser)
        cns.target = rig
        cns.floor_location = self.floor_location
        cns.offset= self.offset
        cns.subtarget = self.subtar
        cns.use_rotation = self.use_rotation
        cns.use_sticky = self.use_sticky

def addConstraint(cdef, lockLoc=(False,False,False), lockRot=(False,False,False)):
    (type, flags, inf, data) = cdef
    if type == 'IK':
        return CIkConstraint(flags, inf, data, lockLoc, lockRot)
    elif type == 'Action':
        return CActionConstraint(flags, inf, data)
    elif type == 'CopyRot':
        return CCopyRotConstraint(flags, inf, data)
    elif type == 'CopyLoc':
        return CCopyLocConstraint(flags, inf, data)
    elif type == 'CopyScale':
        return CCopyScaleConstraint(flags, inf, data)
    elif type == 'CopyTrans':
        return CCopyTransConstraint(flags, inf, data)
    elif type == 'LimitRot':
        return CLimitRotConstraint(flags, inf, data)
    elif type == 'LimitLoc':
        return CLimitLocConstraint(flags, inf, data)
    elif type == 'LimitScale':
        return CLimitScaleConstraint(flags, inf, data)
    elif type == 'Transform':
        return CTransformConstraint(flags, inf, data)
    elif type == 'LockedTrack':
        return CLockedTrackConstraint(flags, inf, data)
    elif type == 'DampedTrack':
        return CDampedTrackConstraint(flags, inf, data)
    elif type == 'StretchTo':
        return CStretchToConstraint(flags, inf, data)
    elif type == 'TrackTo':
        return CTrackToConstraint(flags, inf, data)
    elif type == 'LimitDist':
        return CLimitDistConstraint(flags, inf, data)
    elif type == 'ChildOf':
        return CChildOfConstraint(flags, inf, data)
    elif type == 'SplineIK':
        return CSplineIkConstraint(flags, inf, data)
    elif type == 'Floor':
        return CFloorConstraint(flags, inf, data)
    else:
        raise RuntimeError("Unknown constraint type %s" % type)


#  Authors:             Thomas Larsson
#  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,

import math

from .flags import *

class CConstraint:
    def __init__(self, type, name, flags, inf):
        self.constraint = None
        self.name = name
        self.type = type
        self.influence = inf
        self.active = (flags & C_ACT == 0)
        self.expanded = (flags & C_EXP != 0)

        ow = flags & C_OW_MASK
        if ow == 0:
            self.ownsp = 'WORLD'
        elif ow == C_OW_LOCAL:
            self.ownsp = 'LOCAL'
        elif ow == C_OW_LOCPAR:
            self.ownsp = 'LOCAL_WITH_PARENT'
        elif ow == C_OW_POSE:
            self.ownsp = 'POSE'

        tg = flags & C_TG_MASK
        if tg == 0:
            self.targsp = 'WORLD'
        elif tg == C_TG_LOCAL:
            self.targsp = 'LOCAL'
        elif tg == C_TG_LOCPAR:
            self.targsp = 'LOCAL_WITH_PARENT'
        elif tg == C_TG_POSE:
            self.targsp = 'POSE'

    def update(self, parser, bone):
        return
        raise NameError("Unknown constraint: bone %s cns %s type %s" % (bone.name, self.name, self.type))

    def __repr__(self):
        return ("<CConstraint %s %s>" % (self.name, self.type))

    def build(self, pb, rig, parser):
        cns = self.constraint = pb.constraints.new(self.type)
        cns.name = self.name
        cns.influence = self.influence
        cns.active = self.active
        cns.show_expanded = self.expanded
        cns.target_space = self.targsp
        cns.owner_space = self.ownsp
        return cns

#   Constraint subclasses

class CIkConstraint(CConstraint):
    def __init__(self, flags, inf, data, lockLoc, lockRot):
        CConstraint.__init__(self, "IK", data[0], flags, inf)
        self.subtar = data[1]
        self.chainlen = data[2]
        self.pole = data[3]
        if self.pole:
            (self.angle, self.ptar) = self.pole
        else:
            (self.angle, self.ptar) = (0, None)
        (self.useLoc, self.useRot, self.useStretch) = data[4]
        if len(data) > 5:
            self.useTail = data[5]
        else:
            self.useTail = True
        self.lockLoc = lockLoc
        self.lockRot = lockRot
        (lockRotX, lockR

    def build(self, pb, rig, parser):
        cns = CConstraint.build(self, pb, rig, parser)
        cns.target = rig
        cns.subtarget = self.subtar
        cns.use_tail = self.useTail

        cns.reference_axis = 'BONE'
        cns.chain_count =  self.chainlen
        cns.ik_type = 'COPY_POSE'
        cns.iterations = 500
        cns.limit_mode = 'LIMITDIST_INSIDE'
        cns.orient_weight = 1

        if self.pole:
            cns.pole_angle = self.angle*D
            cns.pole_subtarget = self.ptar
            cns.pole_target = rig

        cns.use_location = self.useLoc
        cns.use_rotation = self.useRot
        cns.use_stretch = self.useStretch
        cns.weight = 1

    def update(self, parser, bone):
        if self.chainlen == 1:
            target = parser.bones[self.subtar]
            head = bone.getHead()
            goal = target.getHead()
            vec = goal - head
            dist = math.sqrt(vec.dot(vec))
            goal = head + vec*(bone.length/dist)
            bone.stretchTo(goal, False)

            if self.ptar:
                pole = parser.bones[self.ptar].getHead()
                bone.poleTargetCorrect(head, goal, pole, self.angle)
        else:
            raise NameError("IK chainlen %d %s" % (self.chainlen, bone.name))

class CActionConstraint(CConstraint):
    def __init__(self, flags, inf, data):
        CConstraint.__init__(self, "ACTION", data[0], flags, inf)
        self.action = data[1]
        self.subtar = data[2]
        self.channel = data[3]
        (self.sframe, self.eframe) = data[4]
        (self.amin, self.amax) = data[5]

    def build(self, pb, rig, parser):
        cns = CConstraint.build(self, pb, rig, parser)
        scale = parser.scale
        cns.target = rig
        cns.action = self.action
        cns.frame_start = self.sframe
        cns.frame_end = self.eframe
        if channel[0:3] == 'LOC':
            cns.maximum = self.amax*scale
            cns.minimum = self.amin*scale
        else:
            cns.maximum = self.amax
            cns.minimum = self.amin
        cns.subtarget = self.subtar
        cns.transform_channel = self.channel

class CCopyRotConstraint(CConstraint):
    def __init__(self, flags, inf, data):
        CConstraint.__init__(self, "COPY_ROTATION", data[0], flags, inf)
        self.subtar = data[1]
        self.use = data[2]
        self.invert = data[3]
        self.useOffs = data[4]

    def build(self, pb, rig, parser):
        cns = CConstraint.build(self, pb, rig, parser)
        cns.target = rig
        cns.use_x,cns.use_y,cns.use_z = self.use
        cns.invert_x,cns.invert_y,cns.invert_z = self.invert
        if isinstance(self.subtar, tuple):
            bname1,bname2 = self.subtar
            if bname1 in rig.data.bones.keys():
                cns.subtarget = bname1
            else:
                cns.subtarget = bname2
        else:
            cns.subtarget = self.subtar
        cns.use_offset = self.useOffs

class CCopyLocConstraint(CConstraint):
    def __init__(self, flags, inf, data):
        CConstraint.__init__(self, "COPY_LOCATION", data[0], flags, inf)
        self.subtar = data[1]
        self.use = data[2]
        self.invert = data[3]
        self.head_tail = data[4]
        self.useOffs = data[5]

    def build(self, pb, rig, parser):
        cns = CConstraint.build(self, pb, rig, parser)
        cns.target = rig
        cns.use_x,cns.use_y,cns.use_z = self.use
        cns.invert_x,cns.invert_y,cns.invert_z = self.invert
        cns.head_tail = self.head_tail
        cns.subtarget = self.subtar
        cns.use_offset = self.useOffs

class CCopyScaleConstraint(CConstraint):
    def __init__(self, flags, inf, data):
        CConstraint.__init__(self, "COPY_SCALE", data[0], flags, inf)
        self.subtar = data[1]
        self.use = data[2]
        self.useOffs = data[3]

    def build(self, pb, rig, parser):
        cns = CConstraint.build(self, pb, rig, parser)
        cns.target = rig
        cns.use_x,cns.use_y,cns.use_z = self.use
        cns.subtarget = self.subtar
        cns.use_offset = self.useOffs

class CCopyTransConstraint(CConstraint):
    def __init__(self, flags, inf, data):
        CConstraint.__init__(self, "COPY_TRANSFORMS", data[0], flags, inf)
        self.subtar = data[1]
        self.head_tail = data[2]

    def build(self, pb, rig, parser):
        cns = CConstraint.build(self, pb, rig, parser)
        cns.target = rig
        cns.head_tail = self.head_tail
        cns.subtarget = self.subtar

class CLimitRotConstraint(CConstraint):
    def __init__(self, flags, inf, data):
        CConstraint.__init__(self, "LIMIT_ROTATION", data[0], flags, inf)
        (self.xmin, self.xmax, self.ymin, self.ymax, self.zmin, self.zmax) = data[1]
        (self.usex, self.usey, self.usez) = data[2]
        self.ltra = (flags & C_LTRA != 0)

    def build(self, pb, rig, parser):
        cns = CConstraint.build(self, pb, rig, parser)
        cns.use_transform_limit = self.ltra
        cns.max_x = self.xmax*D
        cns.max_y = self.ymax*D
        cns.max_z = self.zmax*D
        cns.min_x = self.xmin*D
        cns.min_y = self.ymin*D
        cns.min_z = self.zmin*D
        cns.use_limit_x = self.usex
        cns.use_limit_y = self.usey
        cns.use_limit_z = self.usez

class CLimitLocConstraint(CConstraint):
    def __init__(self, flags, inf, data):
        CConstraint.__init__(self, "LIMIT_LOCATION", data[0], flags, inf)
        (self.xmin, self.xmax, self.ymin, self.ymax, self.zmin, self.zmax) = data[1]
        (self.useminx, self.usemaxx, self.useminy, self.usemaxy, self.useminz, self.usemaxz) = data[2]

    def build(self, pb, rig, parser):
        cns = CConstraint.build(self, pb, rig, parser)
        scale = parser.scale

        cns.use_transform_limit = True
        cns.max_x = self.xmax*scale
        cns.max_y = self.ymax*scale
        cns.max_z = self.zmax*scale
        cns.min_x = self.xmin*scale
        cns.min_y = self.ymin*scale
        cns.min_z = self.zmin*scale
        cns.use_max_x = self.usemaxx
        cns.use_max_y = self.usemaxy
        cns.use_max_z = self.usemaxz
        cns.use_min_x = self.useminx
        cns.use_min_y = self.useminy
        cns.use_min_z = self.useminz

class CLimitScaleConstraint(CConstraint):
    def __init__(self, flags, inf, data):
        CConstraint.__init__(self, "LIMIT_SCALE", data[0], flags, inf)
        (self.xmin, self.xmax, self.ymin, self.ymax, self.zmin, self.zmax) = data[1]
        (self.usex, self.usey, self.usez) = data[2]

    def build(self, pb, rig, parser):
        cns = CConstraint.build(self, pb, rig, parser)
        cns.max_x = self.xmax
        cns.max_y = self.ymax
        cns.max_z = self.zmax
        cns.min_x = self.xmin
        cns.min_y = self.ymin
        cns.min_z = self.zmin
        cns.use_max_x = self.usex
        cns.use_max_y = self.usey
        cns.use_max_z = self.usez
        cns.use_min_x = self.usex
        cns.use_min_y = self.usey
        cns.use_min_z = self.usez

class CTransformConstraint(CConstraint):
    def __init__(self, flags, inf, data):
        CConstraint.__init__(self, "TRANSFORM", data[0], flags, inf)
        self.subtar = data[1]
        self.map_from = data[2]
        self.from_min = data[3]
        self.from_max = data[4]
        self.map_to_from = data[5]
        self.map_to = data[6]
        self.to_min = data[7]
        self.to_max = data[8]

    def build(self, pb, rig, parser):
        cns = CConstraint.build(self, pb, rig, parser)
        cns.target = rig
        cns.subtarget = self.subtar
        cns.map_from = self.map_from
        cns.from_min_x = self.from_min[0]
        cns.from_min_y = self.from_min[1]
        cns.from_min_z = self.from_min[2]
        cns.from_max_x = self.from_max[0]
        cns.from_max_y = self.from_max[1]
        cns.from_max_z = self.from_max[2]
        cns.map_to = self.map_to
        cns.map_to_x_from = self.map_to_from[0]
        cns.map_to_y_from = self.map_to_from[1]
        cns.map_to_z_from = self.map_to_from[2]
        cns.to_min_x = self.to_min[0]
        cns.to_min_y = self.to_min[1]
        cns.to_min_z = self.to_min[2]
        cns.to_max_x = self.to_max[0]
        cns.to_max_y = self.to_max[1]
        cns.to_max_z = self.to_max[2]

class CDampedTrackConstraint(CConstraint):
    def __init__(self, flags, inf, data):
        CConstraint.__init__(self, "DAMPED_TRACK", data[0], flags, inf)
        self.subtar = data[1]
        self.track = data[2]
        self.headtail = data[3]

    def build(self, pb, rig, parser):
        cns = CConstraint.build(self, pb, rig, parser)
        cns.target = parser.name
        cns.subtarget = self.subtar
        cns.head_tail = self.headtail
        cns.track_axis = self.track

class CLockedTrackConstraint(CConstraint):
    def __init__(self, flags, inf, data):
        CConstraint.__init__(self, "LOCKED_TRACK", data[0], flags, inf)
        self.subtar = data[1]
        self.trackAxis = data[2]

    def build(self, pb, rig, parser):
        cns = CConstraint.build(self, pb, rig, parser)
        cns.target = rig
        cns.subtarget = self.subtar
        cns.track_axis = self.trackAxis

class CStretchToConstraint(CConstraint):
    def __init__(self, flags, inf, data, parser=None):
        CConstraint.__init__(self, "STRETCH_TO", data[0], flags, inf)
        self.subtar = data[1]
        self.head_tail = data[2]
        self.bulge = data[3]
        if (parser != None) and (len(data) > 4):
            if isinstance(data[4], tuple):
                start,end = data[4]
                self.rest_length = parser.parser.distance(start,end)
            else:
                self.rest_length = data[4]
        else:
            self.rest_length = None
        if flags & C_VOLXZ:
            self.volume = 'VOLUME_XZX'
        elif flags & C_VOLX:
            self.volume = 'VOLUME_X'
        elif flags & C_VOLZ:
            self.volume = 'VOLUME_Z'
        else:
            self.volume = 'NO_VOLUME'
        if flags & C_PLANEZ:
            self.axis = 'PLANE_Z'
        else:
            self.axis = 'PLANE_X'

    def build(self, pb, rig, parser):
        cns = CConstraint.build(self, pb, rig, parser)
        scale = parser.scale
        cns.target = rig
        cns.bulge = self.bulge
        cns.head_tail = self.head_tail
        cns.keep_axis = self.axis
        cns.subtarget = self.subtar
        cns.volume = self.volume
        if self.rest_length != None:
            cns.rest_length = self.rest_length*scale

    def update(self, parser, bone):
        target = parser.bones[self.subtar]
        goal = (1-self.head_tail)*target.getHead() + self.head_tail*target.getTail()
        bone.stretchTo(goal, True)

class CTrackToConstraint(CConstraint):
    def __init__(self, flags, inf, data):
        CConstraint.__init__(self, "TRACK_TO", data[0], flags, inf)
        self.subtar = data[1]
        self.head_tail = data[2]
        self.track_axis = data[3]
        self.up_axis = data[4]
        self.use_target_z = data[5]

    def build(self, pb, rig, parser):
        cns = CConstraint.build(self, pb, rig, parser)
        cns.target = rig
        cns.head_tail = self.head_tail
        cns.track_axis = self.track_axis
        cns.up_axis = self.up_axis
        cns.subtarget = self.subtar
        cns.use_target_z = self.use_target_z

class CLimitDistConstraint(CConstraint):
    def __init__(self, flags, inf, data):
        CConstraint.__init__(self, "LIMIT_DISTANCE", data[0], flags, inf)
        self.subtar = data[1]
        self.limit_mode = data[2]

    def build(self, pb, rig, parser):
        cns = CConstraint.build(self, pb, rig, parser)
        cns.target = rig
        cns.limit_mode = self.limit_mode
        cns.subtarget = self.subtar

class CChildOfConstraint(CConstraint):
    def __init__(self, flags, inf, data):
        CConstraint.__init__(self, "CHILD_OF", data[0], flags, inf)
        self.subtar = data[1]
        (self.locx, self.locy, self.locz) = data[2]
        (self.rotx, self.roty, self.rotz) = data[3]
        (self.scalex, self.scaley, self.scalez) = data[4]

    def build(self, pb, rig, parser):
        cns = CConstraint.build(self, pb, rig, parser)
        cns.target = rig
        cns.subtarget = self.subtar
        cns.use_location_x = self.locx
        cns.use_location_y = self.locy
        cns.use_location_z = self.locz
        cns.use_rotation_x = self.rotx
        cns.use_rotation_y = self.roty
        cns.use_rotation_z = self.rotz
        cns.use_scale_x = self.scalex
        cns.use_scale_y = self.scaley
        cns.use_scale_z = self.scalez

class CSplineIkConstraint(CConstraint):
    def __init__(self, flags, inf, data):
        CConstraint.__init__(self, "SPLINE_IK", data[0], flags, inf)
        self.target = data[1]
        self.count = data[2]

    def build(self, pb, rig, parser):
        cns = CConstraint.build(self, pb, rig, parser)
        cns.target = rig
        cns.chain_count = self.count
        cns.use_chain_offset = False
        cns.use_curve_radius = True
        cns.use_even_divisions = False
        cns.use_y_stretch = True
        cns.xz_scale_mode = None

class CFloorConstraint(CConstraint):
    def __init__(self, flags, inf, data):
        CConstraint.__init__(self, "FLOOR", data[0], flags, inf)
        self.subtar = data[1]
        self.floor_location = data[2]
        self.offset = data[3]
        self.use_rotation = data[4]
        self.use_sticky = data[5]

    def build(self, pb, rig, parser):
        cns = CConstraint.build(self, pb, rig, parser)
        cns.target = rig
        cns.floor_location = self.floor_location
        cns.offset= self.offset
        cns.subtarget = self.subtar
        cns.use_rotation = self.use_rotation
        cns.use_sticky = self.use_sticky

def addConstraint(cdef, lockLoc=(False,False,False), lockRot=(False,False,False)):
    (type, flags, inf, data) = cdef
    if type == 'IK':
        return CIkConstraint(flags, inf, data, lockLoc, lockRot)
    elif type == 'Action':
        return CActionConstraint(flags, inf, data)
    elif type == 'CopyRot':
        return CCopyRotConstraint(flags, inf, data)
    elif type == 'CopyLoc':
        return CCopyLocConstraint(flags, inf, data)
    elif type == 'CopyScale':
        return CCopyScaleConstraint(flags, inf, data)
    elif type == 'CopyTrans':
        return CCopyTransConstraint(flags, inf, data)
    elif type == 'LimitRot':
        return CLimitRotConstraint(flags, inf, data)
    elif type == 'LimitLoc':
        return CLimitLocConstraint(flags, inf, data)
    elif type == 'LimitScale':
        return CLimitScaleConstraint(flags, inf, data)
    elif type == 'Transform':
        return CTransformConstraint(flags, inf, data)
    elif type == 'LockedTrack':
        return CLockedTrackConstraint(flags, inf, data)
    elif type == 'DampedTrack':
        return CDampedTrackConstraint(flags, inf, data)
    elif type == 'StretchTo':
        return CStretchToConstraint(flags, inf, data)
    elif type == 'TrackTo':
        return CTrackToConstraint(flags, inf, data)
    elif type == 'LimitDist':
        return CLimitDistConstraint(flags, inf, data)
    elif type == 'ChildOf':
        return CChildOfConstraint(flags, inf, data)
    elif type == 'SplineIK':
        return CSplineIkConstraint(flags, inf, data)
    elif type == 'Floor':
        return CFloorConstraint(flags, inf, data)
    else:
        raise RuntimeError("Unknown constraint type %s" % type)


from tkinter import *
from tkinter.ttk import *
class Shape:

self.master = master

		self.create()

	def create(self):

		self.canvas = Canvas(self.master)

		self.canvas.create_oval(10, 10, 80, 80,
							outline = "black", fill = "white",
							width = 2)

		self.canvas.create_oval(110, 10, 210, 80,
							outline = "red", fill = "green",
							width = 2)

		self.canvas.create_rectangle(230, 10, 290, 60,
								outline = "black", fill = "blue",
								width = 2)

		self.canvas.create_arc(30, 200, 90, 100, start = 0,
						extent = 210, outline = "green",
						fill = "red", width = 2)

		points = [150, 100, 200, 120, 240, 180,
				210, 200, 150, 150, 100, 200]

		self.canvas.create_polygon(points, outline = "blue",
							fill = "orange", width = 2)

		self.canvas.pack(fill = BOTH, expand = 1)

master = Tk()
shape = Shape(master)
master.title("Shapes")
master.geometry("330x220 + 300 + 300")

mainloop()


from tkinter import *
from tkinter.ttk import *
class Shape:
	def __init__(self, master = None):

	def create(self):

		self.canvas = Canvas(self.master)

		self.canvas.create_oval(10, 10, 80, 80,
							outline = "black", fill = "white",
							width = 2)

		self.canvas.create_oval(110, 10, 210, 80,
							outline = "red", fill = "green",
							width = 2)

		self.canvas.create_rectangle(230, 10, 290, 60,
								outline = "black", fill = "blue",
								width = 2)

		self.canvas.create_arc(30, 200, 90, 100, start = 0,
						extent = 210, outline = "green",
						fill = "red", width = 2)

		points = [150, 100, 200, 120, 240, 180,
				210, 200, 150, 150, 100, 200]

		self.canvas.create_polygon(points, outline = "blue",
							fill = "orange", width = 2)

		self.canvas.pack(fill = BOTH, expand = 1)

master = Tk()
shape = Shape(master)
master.title("Shapes")
master.geometry("330x220 + 300 + 300")

mainloop()


from tkinter import *
from tkinter.ttk import *
class Shape:
	def __init__(self, master = None):
		self.mast

		self.create()

	def create(self):

		self.canvas = Canvas(self.master)

		self.canvas.create_oval(10, 10, 80, 80,
							outline = "black", fill = "white",
							width = 2)

		self.canvas.create_oval(110, 10, 210, 80,
							outline = "red", fill = "green",
							width = 2)

		self.canvas.create_rectangle(230, 10, 290, 60,
								outline = "black", fill = "blue",
								width = 2)

		self.canvas.create_arc(30, 200, 90, 100, start = 0,
						extent = 210, outline = "green",
						fill = "red", width = 2)

		points = [150, 100, 200, 120, 240, 180,
				210, 200, 150, 150, 100, 200]

		self.canvas.create_polygon(points, outline = "blue",
							fill = "orange", width = 2)

		self.canvas.pack(fill = BOTH, expand = 1)

master = Tk()
shape = Shape(master)
master.title("Shapes")
master.geometry("330x220 + 300 + 300")

mainloop()


def __init__(self, indent='  ', encoding='utf-8'):

'''

    HTML formatting (HTML->XHTML conversion as well)
'''

import sys

try:
    from HTMLParser import HTMLParser
    pyver = 2
except ImportError:
    from html.parser import HTMLParser
    pyver = 3

try:
    file
except NameError:
    from io import TextIOWrapper as file

from .constants import PREFORMATTED_TAGS, IMPLICIT_SELF_CLOSING_TAGS, PRESERVE_CONTENTS_TAGS, INVISIBLE_ROOT_TAG, INVISIBLE_ROOT_TAG_START, INVISIBLE_ROOT_TAG_END
from .exceptions import MultipleRootNodeException
from .Tags import AdvancedTag

from .utils import addStartTag, stripIEConditionals

import codecs

__all__ = ('AdvancedHTMLFormatter', 'AdvancedHTMLMiniFormatter', 'AdvancedHTMLSlimTagFormatter', 'AdvancedHTMLSlimTagMiniFormatter')

class AdvancedHTMLFormatter(HTMLParser):
    '''
        A formatter for HTML. Note this does not understand CSS, so if you are enabling preformatted text based on css rules, it will not work.
        It does, however, understand "pre", "code" and "script" tags and will not try to format their contents.
    '''

'''
            Create a pretty formatter.

            @param indent <str/int>, Default '    ' [4 spaces] - Either a space/tab/newline that represents one level of indent, or an integer to use that number of spaces

            @param encoding <str/None>, Default 'utf-8', - Use this encoding for the document. None to not mess with encoding
        '''
        HTMLParser.__init__(self)

        # Do not automatically convert charrefs in python3
        self.convert_charrefs = False

        self.parsedData = []
        self.reset = self._reset
        self.decl = None
        self.currentIndentLevel = 0
        self.indent = indent
        self.encoding = encoding

        self.inPreformatted = 0

        self.root = None

        self._inTag = []
        self.doctype = None

    def feed(self, contents):
        '''
            feed - Load contents

            @param contents - HTML contents
        '''
        contents = stripIEConditionals(contents)
        try:
            HTMLParser.feed(self, contents)
        except MultipleRootNodeException:
            self.reset()

            HTMLParser.feed(self, "%s%s" %(addStartTag(contents, INVISIBLE_ROOT_TAG_START), INVISIBLE_ROOT_TAG_END))

    def getHTML(self):
        '''
            getHTML - Get the full HTML as contained within this tree, converted to  valid XHTML
                @returns - String
        '''
        root = self.getRoot()
        if root is None:
            raise ValueError('Cannot format, use feed to load contents.')

        if self.doctype:
            doctypeStr = '<!%s>\n' %(self.doctype)
        else:
            doctypeStr = ''

        # 6.6.0: If we have a real root tag, print the outerHTML. If we have a fake root tag (for multiple root condition),
        #   then print the innerHTML (skipping the outer root tag). Otherwise, we will miss
        #   untagged text (between the multiple root nodes).
        rootNode = self.getRoot()
        if rootNode.tagName == INVISIBLE_ROOT_TAG:
            return doctypeStr + rootNode.innerHTML
        else:
            return doctypeStr + rootNode.outerHTML

    def getRoot(self):
        '''
            getRoot - returns the root Tag
                @return - AdvancedTag at root. If you provided multiple root nodes, this will be a "holder" with tagName value as constants.INVISIBLE_ROOT_TAG
        '''
        return self.root

    def getRootNodes(self):
        '''
            getRootNodes - Gets all objects at the "root" (first level; no parent). Use this if you may have multiple roots (not children of )
                Use this method to get objects, for example, in an AJAX request where  may not be your root.

                Note: If there are multiple root nodes (i.e. no  at the top), getRoot will return a special tag. This function automatically
                  handles that, and returns all root nodes.

                @return list<AdvancedTag> - A list of AdvancedTags which are at the root level of the tree.
        '''
        root = self.root
        if not root:
            return []
        if root.tagName == INVISIBLE_ROOT_TAG:
            return list(root.children)
        return [root]

    def setRoot(self, root):
        '''
            setRoot - Sets the root node, and reprocesses the indexes

            @param root - AdvancedTag to be new root
        '''
        self.root = root

    def _reset(self):
        HTMLParser.reset(self)
        self.currentIndentLevel = 0
        self.parsedData = []
        self._inTag = []
        self.root = None
        self.doctype = None
        self.inPreformatted = 0

    def _getIndent(self):
        return '\n' + (self.indent * self.currentIndentLevel)

    def handle_starttag(self, tagName, attributeList, isSelfClosing=False):
        '''
            handle_starttag - Internal for parsing
        '''
        tagName = tagName.lower()
        inTag = self._inTag

        if isSelfClosing is False and tagName in IMPLICIT_SELF_CLOSING_TAGS:
            isSelfClosing = True

        newTag = AdvancedTag(tagName, attributeList, isSelfClosing)
        if self.root is None:
            self.root = newTag
        elif len(inTag) > 0:
            inTag[-1].appendChild(newTag)
        else:
            raise MultipleRootNodeException()

        if self.inPreformatted is 0:
            newTag._indent = self._getIndent()

        if tagName in PREFORMATTED_TAGS:
            self.inPreformatted += 1

        if isSelfClosing is False:
            inTag.append(newTag)
            if tagName != INVISIBLE_ROOT_TAG:
                self.currentIndentLevel += 1

    def handle_startendtag(self, tagName, attributeList):
        '''
            handle_startendtag - Internal for parsing
        '''
        return self.handle_starttag(tagName, attributeList, True)

    def handle_endtag(self, tagName):
        '''
            handle_endtag - Internal for parsing
        '''
        inTag = self._inTag
        try:
            # Handle closing tags which should have been closed but weren't
            foundIt = False
            for i in range(len(inTag)):
                if inTag[i].tagName == tagName:
                    foundIt = True
                    break

            if not foundIt:
                sys.stderr.write('WARNING: found close tag with no matching start.\n')
                return

            while inTag[-1].tagName != tagName:
                oldTag = inTag.pop()
                if oldTag.tagName in PREFORMATTED_TAGS:
                    self.inPreformatted -= 1

                self.currentIndentLevel -= 1

            inTag.pop()
            if tagName != INVISIBLE_ROOT_TAG:
                self.currentIndentLevel -= 1
            if tagName in PREFORMATTED_TAGS:
                self.inPreformatted -= 1
        except:
            pass

    def handle_data(self, data):
        '''
            handle_data - Internal for parsing
        '''
        if data:
            inTag = self._inTag
            if len(inTag) > 0:
                if inTag[-1].tagName not in PRESERVE_CONTENTS_TAGS:
                    data = data.replace('\t', ' ').strip('\r\n')
                    if data.startswith(' '):
                        data = ' ' + data.lstrip()
                    if data.endswith(' '):
                        data = data.rstrip() + ' '
                inTag[-1].appendText(data)
            elif data.strip():
                # Must be text prior to or after root node
                raise MultipleRootNodeException()

    def handle_entityref(self, entity):
        '''
            Internal for parsing
        '''
        inTag = self._inTag
        if len(inTag) > 0:
            inTag[-1].appendText('&%s;' %(entity,))
        else:
            raise MultipleRootNodeException()

    def handle_charref(self, charRef):
        '''
            Internal for parsing
        '''
        inTag = self._inTag
        if len(inTag) > 0:
            inTag[-1].appendText('&#%s;' %(charRef,))
        else:
            raise MultipleRootNodeException()

    def handle_comment(self, comment):
        '''
            Internal for parsing
        '''
        inTag = self._inTag
        if len(inTag) > 0:
            inTag[-1].appendText('' %(comment,))
        else:
            raise MultipleRootNodeException()

    def handle_decl(self, decl):
        '''
            Internal for parsing
        '''
        self.doctype = decl

    def unknown_decl(self, decl):
        '''
            Internal for parsing
        '''
        if not self.doctype:
            self.doctype = decl

    def parseFile(self, filename):
        '''
            parseFile - Parses a file and creates the DOM tree and indexes

                @param filename <str/file> - A string to a filename or a file object. If file object, it will not be closed, you must close.
        '''
        self.reset()

        if isinstance(filename, file):
            contents = filename.read()
        else:
            with codecs.open(filename, 'r', encoding=self.encoding) as f:
                contents = f.read()
        self.feed(contents)

    def parseStr(self, html):
        '''
            parseStr - Parses a string and creates the DOM tree and indexes.

                @param html <str> - valid HTML
        '''
        self.reset()
        if isinstance(html, bytes):
            self.feed(html.decode(self.encoding))
        else:
            self.feed(html)

class AdvancedHTMLMiniFormatter(AdvancedHTMLFormatter):
    '''
        AdvancedHTMLMiniFormatter - A formatter that will reformat a document, keeping only functional
            whitespace and removing any and all indentation and nesting spaces.
    '''

    def __init__(self, encoding='utf-8'):
        '''
            Create a mini formatter.

            @param encoding <str/None>, Default 'utf-8', - Use this encoding for the document. None to not mess with encoding
        '''
        AdvancedHTMLFormatter.__init__(self, indent='', encoding=encoding)

    def _getIndent(self):
        return ''

class AdvancedTagSlim(AdvancedTag):
    '''
        AdvancedTagSlim - A special class which extends AdvancedTag, but uses

            slim-endings (which may have parsing issues on some old/strange parsers)

            I.e. instead of  you would have

            We still by default keep
 as having the space because of xhtml attribute rules,
              but can be disabled y passing slimSelfClosing=True to __init__

            @NOTE: You should NOT use this directly, they are for use by the SlimTagFormatter s
    '''

    def __init__(self, *args, **kwargs):
        '''
            __init__ - Create an AdvancedTagSlim object.

                @see AdvancedTag

                Extra arguments:

                  slimSelfClosing <bool> default False - If True, will use slim-endings on self-closing tags,

                    i.e.
 instead of

                    This may break xhtml compatibility but modern browsers are okay with it.
        '''
        if 'slimSelfClosing' in kwargs:
            slimSelfClosing = kwargs.pop('slimSelfClosing')
        else:
            slimSelfClosing = False
        AdvancedTag.__init__(self, *args, **kwargs)

        object.__setattr__(self, 'slimSelfClosing', slimSelfClosing)

    def getStartTag(self, *args, **kwargs):
        '''
            getStartTag - Override the end-spacing rules

              @see AdvancedTag.getStartTag
        '''

        ret = AdvancedTag.getStartTag(self, *args, **kwargs)

        if ret.endswith(' >'):
            ret = ret[:-2] + '>'
        elif object.__getattribute__(self, 'slimSelfClosing') and ret.endswith(' />'):
            ret = ret[:-3] + '/>'

        return ret

def handle_starttag_slim(self, tagName, attributeList, isSelfClosing=False):
    '''
        handle_starttag_slim - Handles parsing a start tag, but with "slim" start tags

            @see AdvancedHTMLFormatter.handle_starttag
    '''
    tagName = tagName.lower()
    inTag = self._inTag

    if isSelfClosing is False and tagName in IMPLICIT_SELF_CLOSING_TAGS:
        isSelfClosing = True

    newTag = AdvancedTagSlim(tagName, attributeList, isSelfClosing, slimSelfClosing=self.slimSelfClosing)
    if self.root is None:
        self.root = newTag
    elif len(inTag) > 0:
        inTag[-1].appendChild(newTag)
    else:
        raise MultipleRootNodeException()

    if self.inPreformatted is 0:
        newTag._indent = self._getIndent()

    if tagName in PREFORMATTED_TAGS:
        self.inPreformatted += 1

    if isSelfClosing is False:
        inTag.append(newTag)
        if tagName != INVISIBLE_ROOT_TAG:
            self.currentIndentLevel += 1

class AdvancedHTMLSlimTagFormatter(AdvancedHTMLFormatter):
    '''
        AdvancedHTMLSlimTagFormatter - Formats HTML with slim start tags,
            which may break some xhtml-compatible parsers.

        For example  will become .

        Remainder will be pretty-printed. For mini-printing, @see AdvancedHTMLSlimTagMiniFormatter

        If slimSelfClosing=True on __init__,
 will become
 as well
    '''

    def __init__(self, indent='    ', encoding='utf-8', slimSelfClosing=False):
        '''
            __init__ - Construct an AdvancedHTMLSlimTagFormatter

                @see AdvancedHTMLFormatter

                @param slimSelfClosing <bool> Default False - If True, will use slim self-closing tags,

                    e.x.
 becomes

        '''

        AdvancedHTMLFormatter.__init__(self, indent=indent, encoding=encoding)

        self.slimSelfClosing = slimSelfClosing

    handle_starttag = handle_starttag_slim

class AdvancedHTMLSlimTagMiniFormatter(AdvancedHTMLMiniFormatter):
    '''
        AdvancedHTMLSlimTagMiniFormatter - A "mini" formatter that
            removes all non-functional whitespace (including all indentations)

        Also uses "slim" start tags, @see AdvancedHTMLSlimTagFormatter for more info
    '''

    def __init__(self, encoding='utf-8', slimSelfClosing=False):
        '''
            __init__ - Create an AdvancedHTMLSlimTagMiniFormatter

                @see AdvancedHTMLMiniFormatter

                @param slimSelfClosing <bool> Default False - If True, will use slim self-closing tags,

                    e.x.
 becomes

        '''

        AdvancedHTMLMiniFormatter.__init__(self, encoding=encoding)

        self.slimSelfClosing = slimSelfClosing

    handle_starttag = handle_starttag_slim


'''
            Internal for parsing
        '''
        inTag = self._inTag
        if len(inTag) > 0:
            inTag[-1].appendText('&%s;' %(entity,))
        else:
            raise MultipleRootNodeException()

'''

    HTML formatting (HTML->XHTML conversion as well)
'''

import sys

try:
    from HTMLParser import HTMLParser
    pyver = 2
except ImportError:
    from html.parser import HTMLParser
    pyver = 3

try:
    file
except NameError:
    from io import TextIOWrapper as file

from .constants import PREFORMATTED_TAGS, IMPLICIT_SELF_CLOSING_TAGS, PRESERVE_CONTENTS_TAGS, INVISIBLE_ROOT_TAG, INVISIBLE_ROOT_TAG_START, INVISIBLE_ROOT_TAG_END
from .exceptions import MultipleRootNodeException
from .Tags import AdvancedTag

from .utils import addStartTag, stripIEConditionals

import codecs

__all__ = ('AdvancedHTMLFormatter', 'AdvancedHTMLMiniFormatter', 'AdvancedHTMLSlimTagFormatter', 'AdvancedHTMLSlimTagMiniFormatter')

class AdvancedHTMLFormatter(HTMLParser):
    '''
        A formatter for HTML. Note this does not understand CSS, so if you are enabling preformatted text based on css rules, it will not work.
        It does, however, understand "pre", "code" and "script" tags and will not try to format their contents.
    '''

    def __init__(self, indent='  ', encoding='utf-8'):
        '''
            Create a pretty formatter.

            @param indent <str/int>, Default '    ' [4 spaces] - Either a space/tab/newline that represents one level of indent, or an integer to use that number of spaces

            @param encoding <str/None>, Default 'utf-8', - Use this encoding for the document. None to not mess with encoding
        '''
        HTMLParser.__init__(self)

        # Do not automatically convert charrefs in python3
        self.convert_charrefs = False

        self.parsedData = []
        self.reset = self._reset
        self.decl = None
        self.currentIndentLevel = 0
        self.indent = indent
        self.encoding = encoding

        self.inPreformatted = 0

        self.root = None

        self._inTag = []
        self.doctype = None

    def feed(self, contents):
        '''
            feed - Load contents

            @param contents - HTML contents
        '''
        contents = stripIEConditionals(contents)
        try:
            HTMLParser.feed(self, contents)
        except MultipleRootNodeException:
            self.reset()

            HTMLParser.feed(self, "%s%s" %(addStartTag(contents, INVISIBLE_ROOT_TAG_START), INVISIBLE_ROOT_TAG_END))

    def getHTML(self):
        '''
            getHTML - Get the full HTML as contained within this tree, converted to  valid XHTML
                @returns - String
        '''
        root = self.getRoot()
        if root is None:
            raise ValueError('Cannot format, use feed to load contents.')

        if self.doctype:
            doctypeStr = '<!%s>\n' %(self.doctype)
        else:
            doctypeStr = ''

        # 6.6.0: If we have a real root tag, print the outerHTML. If we have a fake root tag (for multiple root condition),
        #   then print the innerHTML (skipping the outer root tag). Otherwise, we will miss
        #   untagged text (between the multiple root nodes).
        rootNode = self.getRoot()
        if rootNode.tagName == INVISIBLE_ROOT_TAG:
            return doctypeStr + rootNode.innerHTML
        else:
            return doctypeStr + rootNode.outerHTML

    def getRoot(self):
        '''
            getRoot - returns the root Tag
                @return - AdvancedTag at root. If you provided multiple root nodes, this will be a "holder" with tagName value as constants.INVISIBLE_ROOT_TAG
        '''
        return self.root

    def getRootNodes(self):
        '''
            getRootNodes - Gets all objects at the "root" (first level; no parent). Use this if you may have multiple roots (not children of <html>)
                Use this method to get objects, for example, in an AJAX request where <html> may not be your root.

                Note: If there are multiple root nodes (i.e. no <html> at the top), getRoot will return a special tag. This function automatically
                  handles that, and returns all root nodes.

                @return list<AdvancedTag> - A list of AdvancedTags which are at the root level of the tree.
        '''
        root = self.root
        if not root:
            return []
        if root.tagName == INVISIBLE_ROOT_TAG:
            return list(root.children)
        return [root]

    def setRoot(self, root):
        '''
            setRoot - Sets the root node, and reprocesses the indexes

            @param root - AdvancedTag to be new root
        '''
        self.root = root

    def _reset(self):
        HTMLParser.reset(self)
        self.currentIndentLevel = 0
        self.parsedData = []
        self._inTag = []
        self.root = None
        self.doctype = None
        self.inPreformatted = 0

    def _getIndent(self):
        return '\n' + (self.indent * self.currentIndentLevel)

    def handle_starttag(self, tagName, attributeList, isSelfClosing=False):
        '''
            handle_starttag - Internal for parsing
        '''
        tagName = tagName.lower()
        inTag = self._inTag

        if isSelfClosing is False and tagName in IMPLICIT_SELF_CLOSING_TAGS:
            isSelfClosing = True

        newTag = AdvancedTag(tagName, attributeList, isSelfClosing)
        if self.root is None:
            self.root = newTag
        elif len(inTag) > 0:
            inTag[-1].appendChild(newTag)
        else:
            raise MultipleRootNodeException()

        if self.inPreformatted is 0:
            newTag._indent = self._getIndent()

        if tagName in PREFORMATTED_TAGS:
            self.inPreformatted += 1

        if isSelfClosing is False:
            inTag.append(newTag)
            if tagName != INVISIBLE_ROOT_TAG:
                self.currentIndentLevel += 1

    def handle_startendtag(self, tagName, attributeList):
        '''
            handle_startendtag - Internal for parsing
        '''
        return self.handle_starttag(tagName, attributeList, True)

    def handle_endtag(self, tagName):
        '''
            handle_endtag - Internal for parsing
        '''
        inTag = self._inTag
        try:
            # Handle closing tags which should have been closed but weren't
            foundIt = False
            for i in range(len(inTag)):
                if inTag[i].tagName == tagName:
                    foundIt = True
                    break

            if not foundIt:
                sys.stderr.write('WARNING: found close tag with no matching start.\n')
                return

            while inTag[-1].tagName != tagName:
                oldTag = inTag.pop()
                if oldTag.tagName in PREFORMATTED_TAGS:
                    self.inPreformatted -= 1

                self.currentIndentLevel -= 1

            inTag.pop()
            if tagName != INVISIBLE_ROOT_TAG:
                self.currentIndentLevel -= 1
            if tagName in PREFORMATTED_TAGS:
                self.inPreformatted -= 1
        except:
            pass

    def handle_data(self, data):
        '''
            handle_data - Internal for parsing
        '''
        if data:
            inTag = self._inTag
            if len(inTag) > 0:
                if inTag[-1].tagName not in PRESERVE_CONTENTS_TAGS:
                    data = data.replace('\t', ' ').strip('\r\n')
                    if data.startswith(' '):
                        data = ' ' + data.lstrip()
                    if data.endswith(' '):
                        data = data.rstrip() + ' '
                inTag[-1].appendText(data)
            elif data.strip():
                # Must be text prior to or after root node
                raise MultipleRootNodeException()

    def handle_entityref(self, entity):

def handle_charref(self, charRef):
        '''
            Internal for parsing
        '''
        inTag = self._inTag
        if len(inTag) > 0:
            inTag[-1].appendText('&#%s;' %(charRef,))
        else:
            raise MultipleRootNodeException()

    def handle_comment(self, comment):
        '''
            Internal for parsing
        '''
        inTag = self._inTag
        if len(inTag) > 0:
            inTag[-1].appendText('' %(comment,))
        else:
            raise MultipleRootNodeException()

    def handle_decl(self, decl):
        '''
            Internal for parsing
        '''
        self.doctype = decl

    def unknown_decl(self, decl):
        '''
            Internal for parsing
        '''
        if not self.doctype:
            self.doctype = decl

    def parseFile(self, filename):
        '''
            parseFile - Parses a file and creates the DOM tree and indexes

                @param filename <str/file> - A string to a filename or a file object. If file object, it will not be closed, you must close.
        '''
        self.reset()

        if isinstance(filename, file):
            contents = filename.read()
        else:
            with codecs.open(filename, 'r', encoding=self.encoding) as f:
                contents = f.read()
        self.feed(contents)

    def parseStr(self, html):
        '''
            parseStr - Parses a string and creates the DOM tree and indexes.

                @param html <str> - valid HTML
        '''
        self.reset()
        if isinstance(html, bytes):
            self.feed(html.decode(self.encoding))
        else:
            self.feed(html)

class AdvancedHTMLMiniFormatter(AdvancedHTMLFormatter):
    '''
        AdvancedHTMLMiniFormatter - A formatter that will reformat a document, keeping only functional
            whitespace and removing any and all indentation and nesting spaces.
    '''

    def __init__(self, encoding='utf-8'):
        '''
            Create a mini formatter.

            @param encoding <str/None>, Default 'utf-8', - Use this encoding for the document. None to not mess with encoding
        '''
        AdvancedHTMLFormatter.__init__(self, indent='', encoding=encoding)

    def _getIndent(self):
        return ''

class AdvancedTagSlim(AdvancedTag):
    '''
        AdvancedTagSlim - A special class which extends AdvancedTag, but uses

            slim-endings (which may have parsing issues on some old/strange parsers)

            I.e. instead of  you would have

            We still by default keep
 as having the space because of xhtml attribute rules,
              but can be disabled y passing slimSelfClosing=True to __init__

            @NOTE: You should NOT use this directly, they are for use by the SlimTagFormatter s
    '''

    def __init__(self, *args, **kwargs):
        '''
            __init__ - Create an AdvancedTagSlim object.

                @see AdvancedTag

                Extra arguments:

                  slimSelfClosing <bool> default False - If True, will use slim-endings on self-closing tags,

                    i.e.
 instead of

                    This may break xhtml compatibility but modern browsers are okay with it.
        '''
        if 'slimSelfClosing' in kwargs:
            slimSelfClosing = kwargs.pop('slimSelfClosing')
        else:
            slimSelfClosing = False
        AdvancedTag.__init__(self, *args, **kwargs)

        object.__setattr__(self, 'slimSelfClosing', slimSelfClosing)

    def getStartTag(self, *args, **kwargs):
        '''
            getStartTag - Override the end-spacing rules

              @see AdvancedTag.getStartTag
        '''

        ret = AdvancedTag.getStartTag(self, *args, **kwargs)

        if ret.endswith(' >'):
            ret = ret[:-2] + '>'
        elif object.__getattribute__(self, 'slimSelfClosing') and ret.endswith(' />'):
            ret = ret[:-3] + '/>'

        return ret

def handle_starttag_slim(self, tagName, attributeList, isSelfClosing=False):
    '''
        handle_starttag_slim - Handles parsing a start tag, but with "slim" start tags

            @see AdvancedHTMLFormatter.handle_starttag
    '''
    tagName = tagName.lower()
    inTag = self._inTag

    if isSelfClosing is False and tagName in IMPLICIT_SELF_CLOSING_TAGS:
        isSelfClosing = True

    newTag = AdvancedTagSlim(tagName, attributeList, isSelfClosing, slimSelfClosing=self.slimSelfClosing)
    if self.root is None:
        self.root = newTag
    elif len(inTag) > 0:
        inTag[-1].appendChild(newTag)
    else:
        raise MultipleRootNodeException()

    if self.inPreformatted is 0:
        newTag._indent = self._getIndent()

    if tagName in PREFORMATTED_TAGS:
        self.inPreformatted += 1

    if isSelfClosing is False:
        inTag.append(newTag)
        if tagName != INVISIBLE_ROOT_TAG:
            self.currentIndentLevel += 1

class AdvancedHTMLSlimTagFormatter(AdvancedHTMLFormatter):
    '''
        AdvancedHTMLSlimTagFormatter - Formats HTML with slim start tags,
            which may break some xhtml-compatible parsers.

        For example  will become .

        Remainder will be pretty-printed. For mini-printing, @see AdvancedHTMLSlimTagMiniFormatter

        If slimSelfClosing=True on __init__,
 will become
 as well
    '''

    def __init__(self, indent='    ', encoding='utf-8', slimSelfClosing=False):
        '''
            __init__ - Construct an AdvancedHTMLSlimTagFormatter

                @see AdvancedHTMLFormatter

                @param slimSelfClosing <bool> Default False - If True, will use slim self-closing tags,

                    e.x.
 becomes

        '''

        AdvancedHTMLFormatter.__init__(self, indent=indent, encoding=encoding)

        self.slimSelfClosing = slimSelfClosing

    handle_starttag = handle_starttag_slim

class AdvancedHTMLSlimTagMiniFormatter(AdvancedHTMLMiniFormatter):
    '''
        AdvancedHTMLSlimTagMiniFormatter - A "mini" formatter that
            removes all non-functional whitespace (including all indentations)

        Also uses "slim" start tags, @see AdvancedHTMLSlimTagFormatter for more info
    '''

    def __init__(self, encoding='utf-8', slimSelfClosing=False):
        '''
            __init__ - Create an AdvancedHTMLSlimTagMiniFormatter

                @see AdvancedHTMLMiniFormatter

                @param slimSelfClosing <bool> Default False - If True, will use slim self-closing tags,

                    e.x.
 becomes

        '''

        AdvancedHTMLMiniFormatter.__init__(self, encoding=encoding)

        self.slimSelfClosing = slimSelfClosing

    handle_starttag = handle_starttag_slim


'''

    HTML formatting (HTML->XHTML conversion as well)
'''

import sys

try:
    from HTMLParser import HTMLParser
    pyver = 2
except ImportError:
    from html.parser import HTMLParser
    pyver = 3

try:
    file
except NameError:
    from io import TextIOWrapper as file

from .constants import PREFORMATTED_TAGS, IMPLICIT_SELF_CLOSING_TAGS, PRESERVE_CONTENTS_TAGS, INVISIBLE_ROOT_TAG, INVISIBLE_ROOT_TAG_START, INVISIBLE_ROOT_TAG_END
from .exceptions import MultipleRootNodeException
from .Tags import AdvancedTag

from .utils import addStartTag, stripIEConditionals

import codecs

__all__ = ('AdvancedHTMLFormatter', 'AdvancedHTMLMiniFormatter', 'AdvancedHTMLSlimTagFormatter', 'AdvancedHTMLSlimTagMiniFormatter')

class AdvancedHTMLFormatter(HTMLParser):
    '''
        A formatter for HTML. Note this does not understand CSS, so if you are enabling preformatted text based on css rules, it will not work.
        It does, however, understand "pre", "code" and "script" tags and will not try to format their contents.
    '''

    def __init__(self, indent='  ', encoding='utf-8'):
        '''
            Create a pretty formatter.

            @param indent <str/int>, Default '    ' [4 spaces] - Either a space/tab/newline that represents one level of indent, or an integer to use that number of spaces

            @param encoding <str/None>, Default 'utf-8', - Use this encoding for the document. None to not mess with encoding
        '''
        HTMLParser.__init__(self)

        # Do not automatically convert charrefs in python3
        self.convert_charrefs = False

        self.parsedData = []
        self.reset = self._reset
        self.decl = None
        self.currentIndentLevel = 0
        self.indent = indent
        self.encoding = encoding

        self.inPreformatted = 0

        self.root = None

        self._inTag = []
        self.doctype = None

    def feed(self, contents):
        '''
            feed - Load contents

            @param contents - HTML contents
        '''
        contents = stripIEConditionals(contents)
        try:
            HTMLParser.feed(self, contents)
        except MultipleRootNodeException:
            self.reset()

            HTMLParser.feed(self, "%s%s" %(addStartTag(contents, INVISIBLE_ROOT_TAG_START), INVISIBLE_ROOT_TAG_END))

    def getHTML(self):
        '''
            getHTML - Get the full HTML as contained within this tree, converted to  valid XHTML
                @returns - String
        '''
        root = self.getRoot()
        if root is None:
            raise ValueError('Cannot format, use feed to load contents.')

        if self.doctype:
            doctypeStr = '<!%s>\n' %(self.doctype)
        else:
            doctypeStr = ''

        # 6.6.0: If we have a real root tag, print the outerHTML. If we have a fake root tag (for multiple root condition),
        #   then print the innerHTML (skipping the outer root tag). Otherwise, we will miss
        #   untagged text (between the multiple root nodes).
        rootNode = self.getRoot()
        if rootNode.tagName == INVISIBLE_ROOT_TAG:
            return doctypeStr + rootNode.innerHTML
        else:
            return doctypeStr + rootNode.outerHTML

    def getRoot(self):
        '''
            getRoot - returns the root Tag
                @return - AdvancedTag at root. If you provided multiple root nodes, this will be a "holder" with tagName value as constants.INVISIBLE_ROOT_TAG
        '''
        return self.root

    def getRootNodes(self):
        '''
            getRootNodes - Gets all objects at the "root" (first level; no parent). Use this if you may have multiple roots (not children of <html>)
                Use this method to get objects, for example, in an AJAX request where <html> may not be your root.

                Note: If there are multiple root nodes (i.e. no <html> at the top), getRoot will return a special tag. This function automatically
                  handles that, and returns all root nodes.

                @return list<AdvancedTag> - A list of AdvancedTags which are at the root level of the tree.
        '''
        root = self.root
        if not root:
            return []
        if root.tagName == INVISIBLE_ROOT_TAG:
            return list(root.children)
        return [root]

    def setRoot(self, root):
        '''
            setRoot - Sets the root node, and reprocesses the indexes

            @param root - AdvancedTag to be new root
        '''
        self.root = root

    def _reset(self):
        HTMLParser.reset(self)
        self.currentIndentLevel = 0
        self.parsedData = []
        self._inTag = []
        self.root = None
        self.doctype = None
        self.inPreformatted = 0

    def _getIndent(self):
        return '\n' + (self.indent * self.currentIndentLevel)

    def handle_starttag(self, tagName, attributeList, isSelfClosing=False):
        '''
            handle_starttag - Internal for parsing
        '''
        tagName = tagName.lower()
        inTag = self._inTag

        if isSelfClosing is False and tagName in IMPLICIT_SELF_CLOSING_TAGS:
            isSelfClosing = True

        newTag = AdvancedTag(tagName, attributeList, isSelfClosing)
        if self.root is None:
            self.root = newTag
        elif len(inTag) > 0:
            inTag[-1].appendChild(newTag)
        else:
            raise MultipleRootNodeException()

        if self.inPreformatted is 0:
            newTag._indent = self._getIndent()

        if tagName in PREFORMATTED_TAGS:
            self.inPreformatted += 1

        if isSelfClosing is False:
            inTag.append(newTag)
            if tagName != INVISIBLE_ROOT_TAG:
                self.currentIndentLevel += 1

    def handle_startendtag(self, tagName, attributeList):
        '''
            handle_startendtag - Internal for parsing
        '''
        return self.handle_starttag(tagName, attributeList, True)

    def handle_endtag(self, tagName):
        '''
            handle_endtag - Internal for parsing
        '''
        inTag = self._inTag
        try:
            # Handle closing tags which should have been closed but weren't
            foundIt = False
            for i in range(len(inTag)):
                if inTag[i].tagName == tagName:
                    foundIt = True
                    break

            if not foundIt:
                sys.stderr.write('WARNING: found close tag with no matching start.\n')
                return

            while inTag[-1].tagName != tagName:
                oldTag = inTag.pop()
                if oldTag.tagName in PREFORMATTED_TAGS:
                    self.inPreformatted -= 1

                self.currentIndentLevel -= 1

            inTag.pop()
            if tagName != INVISIBLE_ROOT_TAG:
                self.currentIndentLevel -= 1
            if tagName in PREFORMATTED_TAGS:
                self.inPreformatted -= 1
        except:
            pass

    def handle_data(self, data):
        '''
            handle_data - Internal for parsing
        '''
        if data:
            inTag = self._inTag
            if len(inTag) > 0:
                if inTag[-1].tagName not in PRESERVE_CONTENTS_TAGS:
                    data = data.replace('\t', ' ').strip('\r\n')
                    if data.startswith(' '):
                        data = ' ' + data.lstrip()
                    if data.endswith(' '):
                        data = data.rstrip() + ' '
                inTag[-1].appendText(data)
            elif data.strip():
                # Must be text prior to or after root node
                raise MultipleRootNodeException()

    def handle_entityref(self, entity):
        '''
            Internal for parsing
        '''
        inTag = self._inTag
        if len(inTag) > 0:
            inTag[-1].appendText('&%s;' %(entity,))
        else:
            raise MultipleRootNodeException()

    def handle_charref(self, charRef):
        '''
            Internal for parsing
        '''
        inTag = self._inTag
        if len(inTag) > 0:
            inTag[-1].appendText('&#%s;' %(charRef,))
        else:
            raise MultipleRootNodeException()

    def handle_comment(self, comment):
        '''
            Internal for parsing
        '''
        inTag = self._inTag
        if len(inTag) > 0:
            inTag[-1].appendText('<!-- %s -->' %(comment,))
        else:
            raise MultipleRootNodeException()

    def handle_decl(self, decl):
        '''
            Internal for parsing
        '''
        self.doctype = decl

    def unknown_decl(self, decl):
        '''
            Internal for parsing
        '''
        if not self.doctype:
            self.doctype = decl

    def parseFile(self, filename):
        '''
            parseFile - Parses a file and creates the DOM tree and indexes

                @param filename <str/file> - A string to a filename or a file object. If file object, it will not be closed, you must close.
        '''
        self.res

if isinstance(filename, file):
            contents = filename.read()
        else:
            with codecs.open(filename, 'r', encoding=self.encoding) as f:
                contents = f.read()
        self.feed(contents)

    def parseStr(self, html):
        '''
            parseStr - Parses a string and creates the DOM tree and indexes.

                @param html <str> - valid HTML
        '''
        self.reset()
        if isinstance(html, bytes):
            self.feed(html.decode(self.encoding))
        else:
            self.feed(html)

class AdvancedHTMLMiniFormatter(AdvancedHTMLFormatter):
    '''
        AdvancedHTMLMiniFormatter - A formatter that will reformat a document, keeping only functional
            whitespace and removing any and all indentation and nesting spaces.
    '''

    def __init__(self, encoding='utf-8'):
        '''
            Create a mini formatter.

            @param encoding <str/None>, Default 'utf-8', - Use this encoding for the document. None to not mess with encoding
        '''
        AdvancedHTMLFormatter.__init__(self, indent='', encoding=encoding)

    def _getIndent(self):
        return ''

class AdvancedTagSlim(AdvancedTag):
    '''
        AdvancedTagSlim - A special class which extends AdvancedTag, but uses

            slim-endings (which may have parsing issues on some old/strange parsers)

            I.e. instead of  you would have

            We still by default keep
 as having the space because of xhtml attribute rules,
              but can be disabled y passing slimSelfClosing=True to __init__

            @NOTE: You should NOT use this directly, they are for use by the SlimTagFormatter s
    '''

    def __init__(self, *args, **kwargs):
        '''
            __init__ - Create an AdvancedTagSlim object.

                @see AdvancedTag

                Extra arguments:

                  slimSelfClosing <bool> default False - If True, will use slim-endings on self-closing tags,

                    i.e.
 instead of

                    This may break xhtml compatibility but modern browsers are okay with it.
        '''
        if 'slimSelfClosing' in kwargs:
            slimSelfClosing = kwargs.pop('slimSelfClosing')
        else:
            slimSelfClosing = False
        AdvancedTag.__init__(self, *args, **kwargs)

        object.__setattr__(self, 'slimSelfClosing', slimSelfClosing)

    def getStartTag(self, *args, **kwargs):
        '''
            getStartTag - Override the end-spacing rules

              @see AdvancedTag.getStartTag
        '''

        ret = AdvancedTag.getStartTag(self, *args, **kwargs)

        if ret.endswith(' >'):
            ret = ret[:-2] + '>'
        elif object.__getattribute__(self, 'slimSelfClosing') and ret.endswith(' />'):
            ret = ret[:-3] + '/>'

        return ret

def handle_starttag_slim(self, tagName, attributeList, isSelfClosing=False):
    '''
        handle_starttag_slim - Handles parsing a start tag, but with "slim" start tags

            @see AdvancedHTMLFormatter.handle_starttag
    '''
    tagName = tagName.lower()
    inTag = self._inTag

    if isSelfClosing is False and tagName in IMPLICIT_SELF_CLOSING_TAGS:
        isSelfClosing = True

    newTag = AdvancedTagSlim(tagName, attributeList, isSelfClosing, slimSelfClosing=self.slimSelfClosing)
    if self.root is None:
        self.root = newTag
    elif len(inTag) > 0:
        inTag[-1].appendChild(newTag)
    else:
        raise MultipleRootNodeException()

    if self.inPreformatted is 0:
        newTag._indent = self._getIndent()

    if tagName in PREFORMATTED_TAGS:
        self.inPreformatted += 1

    if isSelfClosing is False:
        inTag.append(newTag)
        if tagName != INVISIBLE_ROOT_TAG:
            self.currentIndentLevel += 1

class AdvancedHTMLSlimTagFormatter(AdvancedHTMLFormatter):
    '''
        AdvancedHTMLSlimTagFormatter - Formats HTML with slim start tags,
            which may break some xhtml-compatible parsers.

        For example  will become .

        Remainder will be pretty-printed. For mini-printing, @see AdvancedHTMLSlimTagMiniFormatter

        If slimSelfClosing=True on __init__,
 will become
 as well
    '''

    def __init__(self, indent='    ', encoding='utf-8', slimSelfClosing=False):
        '''
            __init__ - Construct an AdvancedHTMLSlimTagFormatter

                @see AdvancedHTMLFormatter

                @param slimSelfClosing <bool> Default False - If True, will use slim self-closing tags,

                    e.x.
 becomes

        '''

        AdvancedHTMLFormatter.__init__(self, indent=indent, encoding=encoding)

        self.slimSelfClosing = slimSelfClosing

    handle_starttag = handle_starttag_slim

class AdvancedHTMLSlimTagMiniFormatter(AdvancedHTMLMiniFormatter):
    '''
        AdvancedHTMLSlimTagMiniFormatter - A "mini" formatter that
            removes all non-functional whitespace (including all indentations)

        Also uses "slim" start tags, @see AdvancedHTMLSlimTagFormatter for more info
    '''

    def __init__(self, encoding='utf-8', slimSelfClosing=False):
        '''
            __init__ - Create an AdvancedHTMLSlimTagMiniFormatter

                @see AdvancedHTMLMiniFormatter

                @param slimSelfClosing <bool> Default False - If True, will use slim self-closing tags,

                    e.x.
 becomes

        '''

        AdvancedHTMLMiniFormatter.__init__(self, encoding=encoding)

        self.slimSelfClosing = slimSelfClosing

    handle_starttag = handle_starttag_slim


        AdvancedHTMLFormatter.__init__(self, indent=indent, encoding=encoding)

        self.slimSelfClosing = slimSelfClosing

'''

    HTML formatting (HTML->XHTML conversion as well)
'''

import sys

try:
    from HTMLParser import HTMLParser
    pyver = 2
except ImportError:
    from html.parser import HTMLParser
    pyver = 3

try:
    file
except NameError:
    from io import TextIOWrapper as file

from .constants import PREFORMATTED_TAGS, IMPLICIT_SELF_CLOSING_TAGS, PRESERVE_CONTENTS_TAGS, INVISIBLE_ROOT_TAG, INVISIBLE_ROOT_TAG_START, INVISIBLE_ROOT_TAG_END
from .exceptions import MultipleRootNodeException
from .Tags import AdvancedTag

from .utils import addStartTag, stripIEConditionals

import codecs

__all__ = ('AdvancedHTMLFormatter', 'AdvancedHTMLMiniFormatter', 'AdvancedHTMLSlimTagFormatter', 'AdvancedHTMLSlimTagMiniFormatter')

class AdvancedHTMLFormatter(HTMLParser):
    '''
        A formatter for HTML. Note this does not understand CSS, so if you are enabling preformatted text based on css rules, it will not work.
        It does, however, understand "pre", "code" and "script" tags and will not try to format their contents.
    '''

    def __init__(self, indent='  ', encoding='utf-8'):
        '''
            Create a pretty formatter.

            @param indent <str/int>, Default '    ' [4 spaces] - Either a space/tab/newline that represents one level of indent, or an integer to use that number of spaces

            @param encoding <str/None>, Default 'utf-8', - Use this encoding for the document. None to not mess with encoding
        '''
        HTMLParser.__init__(self)

        # Do not automatically convert charrefs in python3
        self.convert_charrefs = False

        self.parsedData = []
        self.reset = self._reset
        self.decl = None
        self.currentIndentLevel = 0
        self.indent = indent
        self.encoding = encoding

        self.inPreformatted = 0

        self.root = None

        self._inTag = []
        self.doctype = None

    def feed(self, contents):
        '''
            feed - Load contents

            @param contents - HTML contents
        '''
        contents = stripIEConditionals(contents)
        try:
            HTMLParser.feed(self, contents)
        except MultipleRootNodeException:
            self.reset()

            HTMLParser.feed(self, "%s%s" %(addStartTag(contents, INVISIBLE_ROOT_TAG_START), INVISIBLE_ROOT_TAG_END))

    def getHTML(self):
        '''
            getHTML - Get the full HTML as contained within this tree, converted to  valid XHTML
                @returns - String
        '''
        root = self.getRoot()
        if root is None:
            raise ValueError('Cannot format, use feed to load contents.')

        if self.doctype:
            doctypeStr = '<!%s>\n' %(self.doctype)
        else:
            doctypeStr = ''

        # 6.6.0: If we have a real root tag, print the outerHTML. If we have a fake root tag (for multiple root condition),
        #   then print the innerHTML (skipping the outer root tag). Otherwise, we will miss
        #   untagged text (between the multiple root nodes).
        rootNode = self.getRoot()
        if rootNode.tagName == INVISIBLE_ROOT_TAG:
            return doctypeStr + rootNode.innerHTML
        else:
            return doctypeStr + rootNode.outerHTML

    def getRoot(self):
        '''
            getRoot - returns the root Tag
                @return - AdvancedTag at root. If you provided multiple root nodes, this will be a "holder" with tagName value as constants.INVISIBLE_ROOT_TAG
        '''
        return self.root

    def getRootNodes(self):
        '''
            getRootNodes - Gets all objects at the "root" (first level; no parent). Use this if you may have multiple roots (not children of )
                Use this method to get objects, for example, in an AJAX request where  may not be your root.

                Note: If there are multiple root nodes (i.e. no  at the top), getRoot will return a special tag. This function automatically
                  handles that, and returns all root nodes.

                @return list<AdvancedTag> - A list of AdvancedTags which are at the root level of the tree.
        '''
        root = self.root
        if not root:
            return []
        if root.tagName == INVISIBLE_ROOT_TAG:
            return list(root.children)
        return [root]

    def setRoot(self, root):
        '''
            setRoot - Sets the root node, and reprocesses the indexes

            @param root - AdvancedTag to be new root
        '''
        self.root = root

    def _reset(self):
        HTMLParser.reset(self)
        self.currentIndentLevel = 0
        self.parsedData = []
        self._inTag = []
        self.root = None
        self.doctype = None
        self.inPreformatted = 0

    def _getIndent(self):
        return '\n' + (self.indent * self.currentIndentLevel)

    def handle_starttag(self, tagName, attributeList, isSelfClosing=False):
        '''
            handle_starttag - Internal for parsing
        '''
        tagName = tagName.lower()
        inTag = self._inTag

        if isSelfClosing is False and tagName in IMPLICIT_SELF_CLOSING_TAGS:
            isSelfClosing = True

        newTag = AdvancedTag(tagName, attributeList, isSelfClosing)
        if self.root is None:
            self.root = newTag
        elif len(inTag) > 0:
            inTag[-1].appendChild(newTag)
        else:
            raise MultipleRootNodeException()

        if self.inPreformatted is 0:
            newTag._indent = self._getIndent()

        if tagName in PREFORMATTED_TAGS:
            self.inPreformatted += 1

        if isSelfClosing is False:
            inTag.append(newTag)
            if tagName != INVISIBLE_ROOT_TAG:
                self.currentIndentLevel += 1

    def handle_startendtag(self, tagName, attributeList):
        '''
            handle_startendtag - Internal for parsing
        '''
        return self.handle_starttag(tagName, attributeList, True)

    def handle_endtag(self, tagName):
        '''
            handle_endtag - Internal for parsing
        '''
        inTag = self._inTag
        try:
            # Handle closing tags which should have been closed but weren't
            foundIt = False
            for i in range(len(inTag)):
                if inTag[i].tagName == tagName:
                    foundIt = True
                    break

            if not foundIt:
                sys.stderr.write('WARNING: found close tag with no matching start.\n')
                return

            while inTag[-1].tagName != tagName:
                oldTag = inTag.pop()
                if oldTag.tagName in PREFORMATTED_TAGS:
                    self.inPreformatted -= 1

                self.currentIndentLevel -= 1

            inTag.pop()
            if tagName != INVISIBLE_ROOT_TAG:
                self.currentIndentLevel -= 1
            if tagName in PREFORMATTED_TAGS:
                self.inPreformatted -= 1
        except:
            pass

    def handle_data(self, data):
        '''
            handle_data - Internal for parsing
        '''
        if data:
            inTag = self._inTag
            if len(inTag) > 0:
                if inTag[-1].tagName not in PRESERVE_CONTENTS_TAGS:
                    data = data.replace('\t', ' ').strip('\r\n')
                    if data.startswith(' '):
                        data = ' ' + data.lstrip()
                    if data.endswith(' '):
                        data = data.rstrip() + ' '
                inTag[-1].appendText(data)
            elif data.strip():
                # Must be text prior to or after root node
                raise MultipleRootNodeException()

    def handle_entityref(self, entity):
        '''
            Internal for parsing
        '''
        inTag = self._inTag
        if len(inTag) > 0:
            inTag[-1].appendText('&%s;' %(entity,))
        else:
            raise MultipleRootNodeException()

    def handle_charref(self, charRef):
        '''
            Internal for parsing
        '''
        inTag = self._inTag
        if len(inTag) > 0:
            inTag[-1].appendText('&#%s;' %(charRef,))
        else:
            raise MultipleRootNodeException()

    def handle_comment(self, comment):
        '''
            Internal for parsing
        '''
        inTag = self._inTag
        if len(inTag) > 0:
            inTag[-1].appendText('' %(comment,))
        else:
            raise MultipleRootNodeException()

    def handle_decl(self, decl):
        '''
            Internal for parsing
        '''
        self.doctype = decl

    def unknown_decl(self, decl):
        '''
            Internal for parsing
        '''
        if not self.doctype:
            self.doctype = decl

    def parseFile(self, filename):
        '''
            parseFile - Parses a file and creates the DOM tree and indexes

                @param filename <str/file> - A string to a filename or a file object. If file object, it will not be closed, you must close.
        '''
        self.reset()

        if isinstance(filename, file):
            contents = filename.read()
        else:
            with codecs.open(filename, 'r', encoding=self.encoding) as f:
                contents = f.read()
        self.feed(contents)

    def parseStr(self, html):
        '''
            parseStr - Parses a string and creates the DOM tree and indexes.

                @param html <str> - valid HTML
        '''
        self.reset()
        if isinstance(html, bytes):
            self.feed(html.decode(self.encoding))
        else:
            self.feed(html)

class AdvancedHTMLMiniFormatter(AdvancedHTMLFormatter):
    '''
        AdvancedHTMLMiniFormatter - A formatter that will reformat a document, keeping only functional
            whitespace and removing any and all indentation and nesting spaces.
    '''

    def __init__(self, encoding='utf-8'):
        '''
            Create a mini formatter.

            @param encoding <str/None>, Default 'utf-8', - Use this encoding for the document. None to not mess with encoding
        '''
        AdvancedHTMLFormatter.__init__(self, indent='', encoding=encoding)

    def _getIndent(self):
        return ''

class AdvancedTagSlim(AdvancedTag):
    '''
        AdvancedTagSlim - A special class which extends AdvancedTag, but uses

            slim-endings (which may have parsing issues on some old/strange parsers)

            I.e. instead of  you would have

            We still by default keep
 as having the space because of xhtml attribute rules,
              but can be disabled y passing slimSelfClosing=True to __init__

            @NOTE: You should NOT use this directly, they are for use by the SlimTagFormatter s
    '''

    def __init__(self, *args, **kwargs):
        '''
            __init__ - Create an AdvancedTagSlim object.

                @see AdvancedTag

                Extra arguments:

                  slimSelfClosing <bool> default False - If True, will use slim-endings on self-closing tags,

                    i.e.
 instead of

                    This may break xhtml compatibility but modern browsers are okay with it.
        '''
        if 'slimSelfClosing' in kwargs:
            slimSelfClosing = kwargs.pop('slimSelfClosing')
        else:
            slimSelfClosing = False
        AdvancedTag.__init__(self, *args, **kwargs)

        object.__setattr__(self, 'slimSelfClosing', slimSelfClosing)

    def getStartTag(self, *args, **kwargs):
        '''
            getStartTag - Override the end-spacing rules

              @see AdvancedTag.getStartTag
        '''

        ret = AdvancedTag.getStartTag(self, *args, **kwargs)

        if ret.endswith(' >'):
            ret = ret[:-2] + '>'
        elif object.__getattribute__(self, 'slimSelfClosing') and ret.endswith(' />'):
            ret = ret[:-3] + '/>'

        return ret

def handle_starttag_slim(self, tagName, attributeList, isSelfClosing=False):
    '''
        handle_starttag_slim - Handles parsing a start tag, but with "slim" start tags

            @see AdvancedHTMLFormatter.handle_starttag
    '''
    tagName = tagName.lower()
    inTag = self._inTag

    if isSelfClosing is False and tagName in IMPLICIT_SELF_CLOSING_TAGS:
        isSelfClosing = True

    newTag = AdvancedTagSlim(tagName, attributeList, isSelfClosing, slimSelfClosing=self.slimSelfClosing)
    if self.root is None:
        self.root = newTag
    elif len(inTag) > 0:
        inTag[-1].appendChild(newTag)
    else:
        raise MultipleRootNodeException()

    if self.inPreformatted is 0:
        newTag._indent = self._getIndent()

    if tagName in PREFORMATTED_TAGS:
        self.inPreformatted += 1

    if isSelfClosing is False:
        inTag.append(newTag)
        if tagName != INVISIBLE_ROOT_TAG:
            self.currentIndentLevel += 1

class AdvancedHTMLSlimTagFormatter(AdvancedHTMLFormatter):
    '''
        AdvancedHTMLSlimTagFormatter - Formats HTML with slim start tags,
            which may break some xhtml-compatible parsers.

        For example  will become .

        Remainder will be pretty-printed. For mini-printing, @see AdvancedHTMLSlimTagMiniFormatter

        If slimSelfClosing=True on __init__,
 will become
 as well
    '''

    def __init__(self, indent='    ', encoding='utf-8', slimSelfClosing=False):
        '''
            __init__ - Construct an AdvancedHTMLSlimTagFormatter

                @see AdvancedHTMLFormatter

                @param slimSelfClosing <bool> Default False - If True, will use slim self-closing tags,

                    e.x.
 becomes

        '''

handle_starttag = handle_starttag_slim

class AdvancedHTMLSlimTagMiniFormatter(AdvancedHTMLMiniFormatter):
    '''
        AdvancedHTMLSlimTagMiniFormatter - A "mini" formatter that
            removes all non-functional whitespace (including all indentations)

        Also uses "slim" start tags, @see AdvancedHTMLSlimTagFormatter for more info
    '''

    def __init__(self, encoding='utf-8', slimSelfClosing=False):
        '''
            __init__ - Create an AdvancedHTMLSlimTagMiniFormatter

                @see AdvancedHTMLMiniFormatter

                @param slimSelfClosing <bool> Default False - If True, will use slim self-closing tags,

                    e.x.
 becomes

        '''

        AdvancedHTMLMiniFormatter.__init__(self, encoding=encoding)

        self.slimSelfClosing = slimSelfClosing

    handle_starttag = handle_starttag_slim