import xml.sax
from xml.sax import make_parser, handler
import copy

from MuseParse.classes.ObjectHierarchy.ItemClasses import Directions, Key, BarlinesAndMarkers, Clef, Meter, \
    Meta, Harmony, Note, Mark, Ornaments, Part
from MuseParse.classes import Exceptions
from MuseParse import helpers
from MuseParse.classes.ObjectHierarchy.TreeClasses import PieceTree


def IdAsInt(index):
    if index is not None:
        try:
            return int(index)
        except:
            return index


class MxmlParser(object):

    """
    This class encases a standard XML SAX parser in order to parse MusicXML into a tree of objects. Only one is needed for any parse job
    and it can be reused for multiple files.

    ## Optional input

    - excluded - a list of tags which the parser should ignore. functionality of this is not currently implemented.


    """

    data = {}
    '''A dictionary holding data which needs to be tracked by the parser, but is specific to each piece'''

    def clear(self):
        '''
        Method which resets any variables held by this class, so that the parser can be used again
        :return: Nothing
        '''


        self.tags = []
        '''the current list of tags which have been opened in the XML file'''


        self.chars = {}
        '''the chars held by each tag, indexed by their tag name'''


        self.attribs = {}
        '''the attributes of each tag, indexed by their tag name'''


        self.handler = None
        ''' the method which will handle the current tag, and the data currently in the class '''

        self.piece = PieceTree.PieceTree()
        '''the class tree top'''

        self.isDynamic = False
        '''Indicator of whether the current thing being processed is a dynamic'''

        self.data["note"] = None
        self.data["direction"] = None
        self.data["expression"] = None
        self.data["degree"] = None
        self.data["frame_note"] = None
        self.data["staff_id"] = 1
        self.data["voice"] = 1
        self.data["handleType"] = ""

    def __init__(self, excluded=[]):
        # stuff for parsing. Tags refers to the xml tag list, chars refers to the content of each tag,
        # attribs refers to attributes of each tag, and handler is a method we
        # call to work with each tag


        self.excluded = excluded
        '''this will be put in later, but parser can take in tags we want to ignore, e.g clefs, BarlinesAndMarkerss etc.'''


        self.structure = {
            "movement-title": SetupPiece,
            "credit": SetupPiece,
            "rights": SetupPiece,
            "creator": SetupPiece,
            "defaults": SetupFormat,
            "part": UpdatePart,
            "score-part": UpdatePart,
            "part-group": UpdatePart,
            "measure": HandleMeasures,
            "note": CreateNote,
            "pitch": HandlePitch,
            "unpitched": HandlePitch,
            "articulations": handleArticulation,
            "fermata": HandleFermata,
            "slur": handleOtherNotations,
            "lyric": handleLyrics,
            "technical": handleOtherNotations,
            "backup": HandleMovementBetweenDurations,
            "forward": HandleMovementBetweenDurations}
        '''Dictionary indicating which tags link to which handler methods'''


        self.multiple_attribs = ["beats", "sign"]
        '''not sure this is needed anymore, but tags which we shouldn't clear the previous data for should be added here'''


        self.closed_tags = ["tie", "chord", "note", "measure", "part",
                            "score-part", "sound", "print", "rest", "slur",
                            "accent", "strong-accent", "staccato",
                            "staccatissimo", "up-bow", "down-bow",
                            "cue", "key", "clef", "part-group", "metronome"]
        '''any tags which close instantly in here'''

        self.end_tag = ["tremolo"]
        self.clear()

    def StartTag(self, name, attrs):
        '''
        A method which is called by the SAX parser when a new tag is encountered
        :param name: name of the tag
        :param attrs: the tag's attributes
        :return: none, side effect of modifying bits of the current class
        '''
        if name not in self.excluded:
            if name in self.structure.keys():
                self.handler = self.structure[name]

            self.tags.append(name)
            if attrs is not None:
                self.attribs[name] = attrs
            self.isDynamic = CheckDynamics(name)
            if self.isDynamic and "dynamics" in self.tags:
                self.handler(
                    self.tags, self.attribs, self.chars, self.piece, self.data)
            if name in self.closed_tags and self.handler is not None:
                self.handler(
                    self.tags, self.attribs, self.chars, self.piece, self.data)

    def validateData(self, text):
        '''
        Method which validates the data from each tag, to check whether it is an empty string
        :param text: data to be validated
        :return: True or False depending on the result
        '''
        if text == "\n":
            return False
        for c in text:
            try:
                if str(c) != " ":
                    return True
            except:
                return False
        return False

    def NewData(self, text):
        '''
        Method which is called by the SAX parser upon encountering text inside a tag
        :param text: the text encountered
        :return: None, has side effects modifying the class itself
        '''
        sint = ignore_exception(ValueError)(int)
        if len(self.tags) > 0:
            if self.tags[-1] == "beat-type" or self.tags[-1] == "beats":
                if sint(text) is int:
                    self.chars[self.tags[-1]] = text

        if self.validateData(text):
            if len(self.tags) > 0:
                if self.tags[-1] not in self.chars:
                    self.chars[self.tags[-1]] = text
                else:
                    self.chars[self.tags[-1]] += text

    def CopyNote(self, part, measure_id, new_note):
        '''
         handles copying the latest note into the measure note list.
         done at end of note loading to make sure staff_id is right as staff id could be encountered
         any point during the note tag
        :param part: the part class to copy it into
        :param measure_id: the id of the measure in which the note belongs
        :param new_note: the new note class to be copied in
        :return: None, side effects modifying the piece tree
        '''

        if part.getMeasure(measure_id, self.data["staff_id"]) is None:
            part.addEmptyMeasure(measure_id, self.data["staff_id"])
        measure = part.getMeasure(measure_id, self.data["staff_id"])
        voice_obj = measure.getVoice(self.data["voice"])
        if voice_obj is None:
            measure.addVoice(id=self.data["voice"])
            voice_obj = measure.getVoice(self.data["voice"])
        add = True
        notes = voice_obj.GetChildrenIndexes()
        for n in notes:
            no = voice_obj.GetChild(n)
            if new_note == no:
                add = False
                break
        if add:
            chord = False
            if hasattr(new_note, "chord"):
                chord = new_note.chord

            measure.addNote(new_note, self.data["voice"], chord=chord)
            if hasattr(new_note, "BarlinesAndMarkersRest") and new_note.BarlinesAndMarkersRest:
                measure.rest = True
                voice_obj.rest = True

    def ResetHandler(self, name):
        '''
        Method which assigns handler to the tag encountered before the current, or else
        sets it to None

        :param name: name of the latest tag

        :return:
        '''
        if name in self.tags:
            if len(self.tags) > 1:
                key = len(self.tags) - 2
                self.handler = None
                while key >= 0:
                    if self.tags[key] in self.structure:
                        self.handler = self.structure[self.tags[key]]
                        break
                    key -= 1

            else:
                self.handler = None

    def EndTag(self, name):
        '''
        Method called by the SAX parser when a tag is ended

        :param name: the name of the tag

        :return: None, side effects
        '''
        if self.handler is not None and not self.isDynamic and name not in self.closed_tags:
            self.handler(
                self.tags, self.attribs, self.chars, self.piece, self.data)

        self.ResetHandler(name)

        if name in self.tags:
            self.tags.remove(name)

        if name == "direction":
            # Copy the direction into the appropriate place, and then clear the
            # direction cache
            if self.data["direction"] is not None:
                measure_id = IdAsInt(
                    helpers.GetID(
                        self.attribs,
                        "measure",
                        "number"))
                part_id = helpers.GetID(self.attribs, "part", "id")
                part = self.piece.getPart(part_id)
                if part is not None:
                    if part.getMeasure(measure_id, self.data["staff_id"]) is None:
                        part.addEmptyMeasure(measure_id, self.data["staff_id"])
                    measure =  part.getMeasure(
                        measure_id, self.data["staff_id"])
                    measure.addDirection(
                        copy.deepcopy(self.data["direction"]), self.data["voice"])
                self.data["direction"] = None

            if self.data["expression"] is not None:
                # copy the expression into the appropriate place, then clear
                # the expression cache
                measure_id = IdAsInt(
                    helpers.GetID(
                        self.attribs,
                        "measure",
                        "number"))
                part_id = helpers.GetID(self.attribs, "part", "id")
                part = self.piece.getPart(part_id)
                if part is not None:
                    if part.getMeasure(measure_id, self.data["staff_id"]) is None:
                        part.addEmptyMeasure(measure_id, self.data["staff_id"])
                    measure =  part.getMeasure(
                        measure_id, self.data["staff_id"])
                    measure.addExpression(
                        copy.deepcopy(self.data["expression"]), self.data["voice"])
                self.data["expression"] = None

        if name == "part":
            # do a few checks to confirm barlines are in the right places and
            # to make sure there's no tab in the piece
            part_id = helpers.GetID(self.attribs, "part", "id")
            part = self.piece.getPart(part_id)
            if part is not None:
                part.DoBarlineChecks()
                result = part.CheckIfTabStaff()
                if result is not None:
                    if "TAB" in result:
                        self.piece.removePart(part_id)
                        raise(
                            Exceptions.TabNotImplementedException("Tab notation found: stopping"))
                    if "DRUM" in result:
                        self.piece.removePart(part_id)
                        raise(
                            Exceptions.DrumNotImplementedException("Drum Tab notation found: stopping"))

        if name == "measure":
            # check for a few issues such as divisions not existing in certain
            # BarlinesAndMarkerss
            part_id = helpers.GetID(self.attribs, "part", "id")
            measure_id = IdAsInt(
                helpers.GetID(
                    self.attribs,
                    "measure",
                    "number"))
            part = self.piece.getPart(part_id)
            if part is None:
                part = self.piece.getLastPart()
            part.CheckMeasureDivisions(measure_id)
            part.CheckMeasureMeter(measure_id)
            part.CheckPreviousBarline(self.data["staff_id"])

            measure =  part.getMeasure(measure_id, self.data["staff_id"])
            measure.RunVoiceChecks()
            self.data["staff_id"] = 1
            self.data["voice"] = 1

        # remove the latest data from the other caches
        if name in self.attribs:
            self.attribs.pop(name)
        if name in self.chars:
            self.chars.pop(name)

        if name == "note":
            # copy accross the new note and then clear the cache
            measure_id = IdAsInt(
                helpers.GetID(
                    self.attribs,
                    "measure",
                    "number"))
            part_id = helpers.GetID(self.attribs, "part", "id")
            part = self.piece.getPart(part_id)
            if part is None:
                part = self.piece.getLastPart()
            if part is not None:
                self.CopyNote(
                    part, measure_id, copy.deepcopy(self.data["note"]))
            self.data["note"] = None

        if name == "degree":
            self.data["degree"] = None
        if name == "frame-note":
            self.data["frame_note"] = None

    def parse(self, file):
        '''
        Method the programmer should call when ready to parse a file.
        :param file: exact file path of the file to be processed
        :return: PieceTree object representing the file in memory
        '''
        parser = make_parser()
        self.clear()

        class Extractor(xml.sax.ContentHandler):

            def __init__(self, parent):
                self.parent = parent

            def startElement(self, name, attrs):
                attribs = {}
                for attrname in attrs.getNames():
                    attrvalue = attrs.get(attrname)
                    attribs[attrname] = attrvalue
                self.parent.StartTag(name, attribs)

            def characters(self, text):
                self.parent.NewData(text)

            def endElement(self, name):
                self.parent.EndTag(name)
        parser.setContentHandler(Extractor(self))
        # OFFLINE MODE
        parser.setFeature(handler.feature_external_ges, False)
        fob = open(file, 'r')
        parser.parse(fob)
        return self.piece


def YesNoToBool(entry):
    '''
    Method which takes in either yes or no and converts it to bool. Often found in MusicXML.
    :param entry: the word to convert
    :return: True/False
    '''
    if entry == "yes":
        return True
    if entry == "no":
        return False


def ignore_exception(IgnoreException=Exception, DefaultVal=None):
    """ Decorator for ignoring exception from a function
    e.g.   @ignore_exception(DivideByZero)
    e.g.2. ignore_exception(DivideByZero)(Divide)(2/0)
    borrowed from: http://stackoverflow.com/questions/2262333/is-there-a-built-in-or-more-pythonic-way-to-try-to-parse-a-string-to-an-integer
    """

    def dec(function):
        def _dec(*args, **kwargs):
            try:
                return function(*args, **kwargs)
            except IgnoreException:
                return DefaultVal

        return _dec

    return dec


# HANDLER METHODS: see tag correlations inside the MusicXML parser class
# init method.
def SetupPiece(tag, attrib, content, piece, data):
    return_val = None
    if content is not [] and len(tag) > 0:
        title = None
        composer = None
        if tag[-1] == "movement-title":
            return_val = 1
            if "movement-title" in content:
                title = content["movement-title"]
        if tag[-1] == "creator":
            return_val = 1
            if "creator" in attrib:
                if "type" in attrib["creator"]:
                    if attrib["creator"]["type"] == "composer":
                        if "creator" in content:
                            composer = content["creator"]
        if tag[-1] == "movement-title" or "creator":
            if not hasattr(piece.GetItem(), "meta"):
                piece.GetItem().meta = Meta.Meta(
                    composer=composer,
                    title=title)

            else:
                if composer is not None:
                    piece.GetItem().meta.composer = composer
                if title is not None:
                    piece.GetItem().meta.title = title
        if tag[-1] == "rights":
            if "rights" in content:
                rights = content["rights"] + " "
                if hasattr(piece.GetItem(), "meta"):
                    if not hasattr(piece.GetItem().meta, "copyright"):
                        piece.GetItem().meta.copyright = ""
                    piece.GetItem().meta.copyright += rights
                else:
                    piece.GetItem().meta = Meta.Meta(copyright=rights)
        if "credit" in tag:
            page = 0
            if "credit" in attrib:
                if "page" in attrib["credit"]:
                    page = int(attrib["credit"]["page"])
            if tag[-1] == "credit-type":
                data["handleType"] = content["credit-type"]
            if tag[-1] == "credit-words":
                x = None
                y = None
                size = None
                justify = None
                valign = None
                text = None
                if "credit-words" in attrib:
                    temp = attrib["credit-words"]

                    if "default-x" in temp:
                        x = float(temp["default-x"])
                    if "default-y" in temp:
                        y = float(temp["default-y"])
                    if "font-size" in temp:
                        size = float(temp["font-size"])
                    if "justify" in temp:
                        justify = temp["justify"]
                        if justify == "center" and data["handleType"] == "":
                            data["handleType"] = "title"
                        if justify == "right" and data["handleType"] == "":
                            data["handleType"] = "composer"
                    if "valign" in temp:
                        valign = temp["valign"]
                        if valign == "bottom" and data["handleType"] == "":
                            data["handleType"] = "rights"

                if "credit-words" in content:
                    text = content["credit-words"]

                if data["handleType"] == "":
                    credit = Directions.CreditText(
                        page=page,
                        x=x,
                        y=y,
                        size=size,
                        justify=justify,
                        valign=valign,
                        text=text)
                    if not hasattr(piece.GetItem(), "meta"):
                        piece.GetItem().meta = Meta.Meta()
                    piece.GetItem().meta.AddCredit(credit)
                else:
                    if data["handleType"] == "composer":
                        if not hasattr(piece.GetItem().meta, "composer"):
                            piece.GetItem().meta.composer = text
                        else:
                            if text.replace(
                                " ",
                                "") not in piece.GetItem().meta.composer.replace(
                                " ",
                                "") and piece.GetItem().meta.composer.replace(
                                " ",
                                "") not in text.replace(
                                " ",
                                    ""):
                                piece.GetItem().meta.composer += text
                    if data["handleType"] == "rights":
                        if not hasattr(piece.GetItem().meta, "copyright"):
                            piece.GetItem().meta.copyright = text
                        else:
                            if text.replace(
                                " ",
                                "") not in piece.GetItem().meta.copyright.replace(
                                " ",
                                "") and piece.GetItem().meta.copyright.replace(
                                " ",
                                "") not in text.replace(
                                " ",
                                    ""):
                                piece.GetItem().meta.copyright += text
                    if data["handleType"] == "title":
                        if not hasattr(piece.GetItem().meta, "title"):
                            piece.GetItem().meta.title = text
                        else:
                            if text.replace(
                                " ",
                                "") not in piece.GetItem().meta.title.replace(
                                " ",
                                "") and piece.GetItem().meta.title.replace(
                                " ",
                                "") not in text.replace(
                                " ",
                                    ""):
                                piece.GetItem().meta.title += text
                    if data["handleType"] == "page number":
                        if not hasattr(piece.GetItem().meta, "pageNum"):
                            piece.GetItem().meta.pageNum = True
                    data["handleType"] = ""
    return return_val


def UpdatePart(tag, attrib, content, piece, data):
    part_id = helpers.GetID(attrib, "part", "id")
    if part_id is None:
        part_id = helpers.GetID(attrib, "score-part", "id")
    return_val = None
    if len(tag) > 0:
        if "part-group" in tag:
            index = 0
            type = ""
            if "part-group" in attrib:
                if "number" in attrib["part-group"]:
                    index = int(attrib["part-group"]["number"])
                if "type" in attrib["part-group"]:
                    type = attrib["part-group"]["type"]
            if type != "":
                if type == "start":
                    piece.startGroup(index)
                if type == "stop":
                    piece.stopGroup(index)
        if "score-part" in tag:
            if part_id is None:
                raise(
                    Exceptions.NoScorePartException("ERROR IN UPDATEPART: no score-part id found"))
            elif piece.getPart(part_id) is None:
                piece.addPart(Part.Part(), index=part_id)
                return_val = 1
            if "part-name" in tag:
                if "part-name" in content and part_id is not None:
                    name_rplc = content["part-name"].replace("\r", "\n")
                    piece.getPart(part_id).GetItem().name = name_rplc
                    return_val = 1
            if "part-abbreviation" in tag:
                if "part-abbreviation" in content and part_id is not None:
                    piece.getPart(part_id).GetItem().shortname = content[
                        "part-abbreviation"]
    return return_val


def handleArticulation(tag, attrs, content, piece, data):
    if len(tag) > 0:
        if "articulations" in tag:
            if data["note"] is not None:
                accent = None
                if tag[-1] == "accent":
                    accent = Mark.Accent()
                if tag[-1] == "strong-accent":
                    s_type = ""
                    if "strong-accent" in attrs:
                        if "type" in attrs["strong-accent"]:
                            s_type = attrs["strong-accent"]["type"]
                    accent = Mark.StrongAccent(type=s_type)
                if tag[-1] == "staccato":
                    accent = Mark.Staccato()
                if tag[-1] == "staccatissimo":
                    accent = Mark.Staccatissimo()
                if tag[-1] == "detached-legato":
                    accent = Mark.DetachedLegato()
                if tag[-1] == "tenuto":
                    accent = Mark.Tenuto()
                if tag[-1] == "breath-mark":
                    accent = Mark.BreathMark()
                if tag[-1] == "caesura":
                    accent = Mark.Caesura()
                if accent is not None:
                    if data["note"].Search(type(accent)) is None:
                        data["note"].addNotation(accent)
                    accent = None
            return 1
    return None


def HandleMovementBetweenDurations(tags, attrs, chars, piece, data):
    global last_note
    measure_id = IdAsInt(helpers.GetID(attrs, "measure", "number"))

    part_id = helpers.GetID(attrs, "part", "id")
    if part_id is not None:
        if measure_id is not None:
            part = piece.getPart(part_id)
            if part.getMeasure(measure_id, data["staff_id"]) is None:
                part.addEmptyMeasure(measure_id, data["staff_id"])
            measure =  part.getMeasure(measure_id, data["staff_id"])
            if "backup" in tags and tags[-1] == "duration":
                part.CheckDivisions()
                part.Backup(measure_id, duration=float(chars["duration"]))
            if "forward" in tags and tags[-1] == "duration":
                part.CheckDivisions()
                part.Forward(measure_id, duration=float(chars["duration"]))


def HandleFermata(tags, attrs, chars, piece, data):
    if "fermata" in tags:
        type = None
        symbol = None
        if "fermata" in attrs:
            if "type" in attrs["fermata"]:
                type = attrs["fermata"]["type"]
        if "fermata" in chars:
            symbol = chars["fermata"]
        fermata = Mark.Fermata(type=type, symbol=symbol)
        data["note"].addNotation(fermata)
    return None


def handleOtherNotations(tag, attrs, content, piece, data):
    if len(tag) > 0:
        if "notations" in tag:
            if tag[-1] == "slur":

                notation = Directions.Slur()
                if "slur" in attrs and "placement" in attrs["slur"]:
                    notation.placement = attrs["slur"]["placement"]

                if "slur" in attrs and "type" in attrs["slur"]:
                    notation.type = attrs["slur"]["type"]
                data["note"].AddSlur(notation)
            if tag[-
                   2] == "technical" and tag[-
                                             1] != "bend" and tag[-
                                                                  1] != "hammer-on" and tag[-
                                                                                            1] != "pull-off":
                text = None
                if tag[-1] in content:
                    text = content[tag[-1]]
                data["note"].addNotation(
                    Mark.Technique(type=tag[-1], symbol=text))
            elif len(tag) >= 3 and tag[-3] == "technical" and tag[-2] == "bend":
                bend_val = 0
                if tag[-1] in content:
                    bend_val = content[tag[-1]]
                data["note"].addNotation(Mark.Bend(value=float(bend_val)))

            return 1
    return None


def HandleMeasures(tag, attrib, content, piece, data):
    part_id = helpers.GetID(attrib, "part", "id")
    measure_id = IdAsInt(helpers.GetID(attrib, "measure", "number"))
    part = None
    key = None
    return_val = None
    if len(tag) > 0 and "measure" in tag:

        if "staff" in tag:
            data["staff_id"] = int(content["staff"])
        if part_id is None:
            part_id = piece.root.GetChildrenIndexes()[-1]
        if part_id is not None:
            part = piece.getPart(part_id)
            if part is None:
                part_id = piece.root.GetChildrenIndexes()[-1]
                part = piece.getPart(part_id)
        measure = None

        if part is not None:
            if tag[-1] == "staves":
                staves = int(content["staves"])
                for staff in range(1, staves + 1):
                    if part.getMeasure(measure_id, staff) is None:
                        part.addEmptyMeasure(measure_id, staff)
            measure =  part.getMeasure(
                measure=measure_id, staff=data["staff_id"])
            if measure is None:
                part.addEmptyMeasure(measure_id, data["staff_id"])
                measure =  part.getMeasure(measure_id, data["staff_id"])
            if measure is not None:
                key = measure.GetLastKey(voice=data["voice"])
                if key is not None and type(key) is not Key.Key:
                    key = key.GetItem()
        implicit = helpers.GetID(attrib, "measure", "implicit")
        if implicit is not None:
            measure.partial = YesNoToBool(implicit)
        if tag[-1] == "divisions" and measure is not None:
            measure.divisions = int(content["divisions"])
        if tag[-1] == "key":
            if "key" in attrib:
                if "number" in attrib["key"]:
                    data["staff_id"] = int(attrib["key"]["number"])
                    measure = part.getMeasure(
                        measure=measure_id,
                        staff=data["staff_id"])
                    if measure is None:
                        part.addEmptyMeasure(measure_id, data["staff_id"])
                        measure = part.getMeasure(measure_id, data["staff_id"])
                    if measure is not None:
                        key = measure.GetLastKey(voice=data["voice"])
                        if key is not None and type(key) is not Key.Key:
                            key = key.GetItem()
                        measure.addKey(Key.Key(), data["voice"])
                else:
                    part.addKey(Key.Key(), measure_id)
            else:
                part.addKey(Key.Key(), measure_id)
        if tag[-1] == "mode" and "key" in tag and measure is not None:
            key = measure.GetLastKey(voice=data["voice"])
            if key is not None and type(key) is not Key.Key:
                key = key.GetItem()
            if key is not None:
                key.mode = content["mode"]
            return_val = 1
        if tag[-1] == "fifths" and "key" in tag:
            key = measure.GetLastKey(voice=data["voice"])
            if key is not None and type(key) is not Key.Key:
                key = key.GetItem()
            if key is not None:
                key.fifths = int(content["fifths"])
            return_val = 1

        if tag[-1] == "beats" and ("time" in tag or "meter" in tag):
            symbol = helpers.GetID(attrib, "time", "symbol")
            if hasattr(measure, "meter"):
                measure.meter.beats = int(content["beats"])
                if symbol is not None:
                    measure.meter.style = symbol
            else:
                measure.meter = Meter.Meter(
                    beats=int(
                        content["beats"]),
                    style=symbol)
            return_val = 1
        if tag[-1] == "beat-type" and ("time" in tag or "meter" in tag):
            symbol = helpers.GetID(attrib, "time", "symbol")
            if hasattr(measure, "meter"):
                measure.meter.type = int(content["beat-type"])
                if symbol is not None:
                    measure.meter.style = symbol
            else:
                measure.meter = Meter.Meter(
                    type=int(
                        content["beat-type"]),
                    style=symbol)
            return_val = 1
        if "clef" in tag:
            handleClef(tag, attrib, content, piece, data)
        if "transpose" in tag:
            if "diatonic" in tag:
                if hasattr(measure, "transpose"):
                    measure.transpose.diatonic = content["diatonic"]
                else:
                    measure.transpose = BarlinesAndMarkers.Transposition(
                        diatonic=content["diatonic"])
            if "chromatic" in tag:
                if hasattr(measure, "transpose"):
                    measure.transpose.chromatic = content["chromatic"]
                else:
                    measure.transpose = BarlinesAndMarkers.Transposition(
                        chromatic=content["chromatic"])
            if "octave-change" in tag:
                if hasattr(measure, "transpose"):
                    measure.transpose.octave = content["octave-change"]
                else:
                    measure.transpose = BarlinesAndMarkers.Transposition(
                        octave=content["octave-change"])
            return_val = 1
        if "print" in tag:
            part = piece.getPart(part_id)
            staves = piece.getPart(part_id).GetChildrenIndexes()
            if "print" in attrib:
                if "new-system" in attrib["print"]:
                    for staff in staves:
                        if part.getMeasure(measure_id, staff) is None:
                            part.addEmptyMeasure(measure_id, staff)
                        measure =  part.getMeasure(measure_id, staff)
                        measure.newSystem = YesNoToBool(
                            attrib["print"]["new-system"])
                if "new-page" in attrib["print"]:
                    for staff in staves:
                        if part.getMeasure(measure_id, staff) is None:
                            part.addEmptyMeasure(measure_id, staff)
                        measure =  part.getMeasure(measure_id, staff)
                        measure.newPage = YesNoToBool(
                            attrib["print"]["new-page"])
            return_val = 1

        if "harmony" in tag:
            root = None
            kind = None
            bass = None
            if data["direction"] is None:
                data["direction"] = Harmony.Harmony(kind=kind)
            else:
                data["direction"].kind = kind

            if "root" in tag:
                if not hasattr(data["direction"], "root"):
                    root = Harmony.harmonyPitch()
                    data["direction"].root = root
                else:
                    root = data["direction"].root
                if tag[-1] == "root-step":
                    if "root-step" in content:
                        root.step = content["root-step"]
                if tag[-1] == "root-alter":
                    if "root-alter" in content:
                        root.alter = content["root-alter"]

            if "kind" in tag:
                if not hasattr(data["direction"], "kind") or data["direction"].kind is None:
                    kind = Harmony.Kind()
                    data["direction"].kind = kind
                elif data["direction"].kind is not None:
                    kind = data["direction"].kind
                if "kind" in content:
                    kind.value = content["kind"]
                if "kind" in attrib:
                    if "text" in attrib["kind"]:
                        kind.text = attrib["kind"]["text"]
                    if "halign" in attrib["kind"]:
                        kind.halign = attrib["kind"]["halign"]
                    if "parenthesis-degrees" in attrib["kind"]:
                        kind.parenthesis = attrib[
                            "kind"]["parenthesis-degrees"]

            if tag[-1] == "voice":
                data["voice"] = int(content["voice"])

            if "bass" in tag:
                if not hasattr(data["direction"], "bass"):
                    data["direction"].bass = Harmony.harmonyPitch()
                if "bass-step" in tag and "bass-step" in content:
                    data["direction"].bass.step = content["bass-step"]
                if "bass-alter" in tag and "bass-alter" in content:
                    data["direction"].bass.alter = content["bass-alter"]
            frame = None

            if "degree" in tag:
                if data["degree"] is None:
                    data["degree"] = Harmony.Degree()
                    data["direction"].degrees.append(data["degree"])

                if "degree-value" in tag:
                    if "degree-value" in content:
                        data["degree"].value = content["degree-value"]
                if "degree-alter" in tag:
                    if "degree-alter" in content:
                        data["degree"].alter = content["degree-alter"]
                if "degree-type" in tag:
                    if "degree-type" in content:
                        data["degree"].type = content["degree-type"]
                    if "degree-type" in attrib:
                        if "text" in attrib["degree-type"]:
                            data["degree"].display = attrib[
                                "degree-type"]["text"]

            if "frame" in tag:
                if not hasattr(data["direction"], "frame"):
                    data["direction"].frame = Harmony.Frame()
                if "first-fret" in tag:
                    data["direction"].frame.firstFret = True
                    if "first-fret" in content:
                        if "first-fret" not in attrib:
                            data["direction"].frame.firstFret = [
                                content["first-fret"]]
                        else:
                            data["direction"].frame.firstFret = [
                                content["first-fret"]]
                            if "text" in attrib["first-fret"]:
                                data["direction"].frame.firstFret.append(
                                    attrib["first-fret"]["text"])
                if "frame-strings" in tag and "frame-strings" in content:
                    data["direction"].frame.strings = content["frame-strings"]
                if "frame-frets" in tag and "frame-frets" in content:
                    data["direction"].frame.frets = content["frame-frets"]
                if "frame-note" in tag:
                    if data["frame_note"] is None:
                        data["frame_note"] = Harmony.FrameNote()

                    if "string" in tag and "string" in content:
                        data["frame_note"].string = content["string"]
                        data["direction"].frame.notes[
                            int(content["string"])] = data["frame_note"]
                    if "fret" in tag and "fret" in content:
                        data["frame_note"].fret = content["fret"]
                    if "barre" in tag and "barre" in attrib:
                        data["frame_note"].barre = attrib["barre"]["type"]
                    if "fingering" in tag and "fingering" in content:
                        data["frame_note"].fingering = content["fingering"]
    handleBarline(tag, attrib, content, piece, data)
    HandleDirections(tag, attrib, content, piece, data)
    handleArticulation(tag, attrib, content, piece, data)

    return return_val


def handleClef(tag, attrib, content, piece, data):
    data["staff_id"] = IdAsInt(helpers.GetID(attrib, "clef", "number"))
    if data["staff_id"] is None:
        data["staff_id"] = 1
    measure_id = IdAsInt(helpers.GetID(attrib, "measure", "number"))
    part_id = helpers.GetID(attrib, "part", "id")
    part = piece.getPart(part_id)
    if part is not None:
        BarlinesAndMarkersNode = part.getMeasure(measure_id, data["staff_id"])
        if BarlinesAndMarkersNode is None:
            part.addEmptyMeasure(measure_id, data["staff_id"])
            BarlinesAndMarkersNode = part.getMeasure(measure_id, data["staff_id"])
        if tag[-1] == "clef":
            part.addClef(
                Clef.Clef(), measure_id, data["staff_id"], data["voice"])
        if BarlinesAndMarkersNode is not None:
            clef = BarlinesAndMarkersNode.GetLastClef(voice=data["voice"])
            if clef is not None and type(clef) is not Clef.Clef:
                clef = clef.GetItem()
            sign = None
            line = None
            octave = None
            if tag[-1] == "sign":
                sign = content["sign"]
                if sign == "percussion":
                    part.drum = True
                else:
                    part.drum = False
                if sign == "tab":
                    part.tab = True
                else:
                    part.tab = False
            if tag[-1] == "line":
                line = int(content["line"])
            if tag[-1] == "clef-octave-change":
                octave = int(content["clef-octave-change"])
            if clef is not None:
                if sign is not None:
                    clef.sign = sign
                if line is not None:
                    clef.line = int(line)
                if octave is not None:
                    clef.octave_change = octave
    data["staff_id"] = 1


def handleBarline(tag, attrib, content, piece, data):
    part_id = helpers.GetID(attrib, "part", "id")
    measure_id = IdAsInt(helpers.GetID(attrib, "measure", "number"))
    measure =  None
    times = 2
    if part_id is not None and measure_id is not None:
        part = piece.getPart(part_id)
        if part is None:
            part = piece.getLastPart()

        if part.getMeasure(measure_id, int(data["staff_id"])) is None:
            part.addEmptyMeasure(measure_id, int(data["staff_id"]))
        measure =  part.getMeasure(measure_id, int(data["staff_id"]))
    if "barline" in tag and measure is not None:
        location = helpers.GetID(attrib, "barline", "location")
        barline = None
        style = None
        repeat = None
        ending = None
        if tag[-1] == "ending":
            btype = None
            number = None
            if "ending" in attrib:
                if "number" in attrib["ending"]:
                    if measure.GetBarline(location) is None or not hasattr(
                            measure.GetBarline(location),
                            "ending"):
                        num_str = attrib["ending"]["number"]
                        split = num_str.split(",")
                        number = int(split[-1])
                    else:
                        measure.GetBarline(location).ending.number = int(
                            attrib["ending"]["number"])
                if "type" in attrib["ending"]:
                    if location not in measure.barlines or not hasattr(
                            measure.GetBarline(location),
                            "ending"):
                        btype = attrib["ending"]["type"]
                    else:
                        measure.GetBarline(
                            location).ending.type = attrib["ending"]["type"]

            ending = BarlinesAndMarkers.EndingMark(type=btype, number=number)

            if location in measure.barlines:
                measure.GetBarline(location).ending = ending

        if tag[-1] == "bar-style":
            if location not in measure.barlines:
                style = content["bar-style"]
            else:
                measure.GetBarline(location).style = style
        if tag[-1] == "repeat":
            if "repeat" in attrib:
                times = helpers.GetID(attrib, "repeat", "times")
                if times is not None:
                    times = int(times)
                if "direction" in attrib["repeat"]:
                    barline = measure.GetBarline(location)

                    repeat = attrib["repeat"]["direction"]
                    if hasattr(barline, "ending"):
                        position = -2
                        index = part.getMeasureIDAtPosition(
                            position,
                            staff=data["staff_id"])
                        if index is not None:
                            right_barline = part.getMeasure(
                                index,
                                data["staff_id"]).GetBarline("right")
                            if right_barline is not None and hasattr(
                                    right_barline,
                                    "ending"):
                                position -= 1
                                index = part.getMeasureIDAtPosition(
                                    position,
                                    staff=data["staff_id"])
                            part.AddBarline(
                                measure=index,
                                staff=data["staff_id"],
                                item=BarlinesAndMarkers.Barline(
                                    repeat="backward",
                                    repeatNum=times),
                                location="right")
                            #part.AddBarline(measure=index, staff=staff_id, item=BarlinesAndMarkers.Barline(repeat="forward"), location="left")
                            if barline.ending.number == 1:
                                barline.repeat = "backward-barline"

                    else:
                        if barline is not None:
                            barline.repeat = repeat
                            if times is not None:
                                barline.repeatNum = times
                            part.AddBarline(
                                item=barline,
                                measure=measure_id,
                                staff=data["staff_id"],
                                location=location)

        if location not in measure.barlines:
            if barline is None:
                barline = BarlinesAndMarkers.Barline(
                    style=style,
                    repeat=repeat,
                    ending=ending)
            part.AddBarline(
                measure=measure_id,
                staff=data["staff_id"],
                item=barline,
                location=location)


def CheckID(tag, attrs, string, id_name):
    if string in tag:
        return attrs[string][id_name]


def CreateNote(tag, attrs, content, piece, data):
    ret_value = None

    if len(tag) > 0 and "note" in tag:

        if tag[-1] == "staff":
            data["staff_id"] = int(content["staff"])
        if "note" in tag and data["note"] is None:
            data["note"] = Note.Note()
            ret_value = 1
        if "note" in attrs:
            if "print-object" in attrs["note"]:
                result = YesNoToBool(attrs["note"]["print-object"])
                data["note"].print = result
        if "rest" in tag:
            rest_measure =  helpers.GetID(attrs, "rest", "measure")
            if rest_measure is not None:
                value = YesNoToBool(rest_measure)
                data["note"].measureRest = value
            data["note"].rest = True
        if "cue" in tag:
            data["note"].cue = True

        if tag[-1] == "grace":
            slash = False
            if "grace" in attrs:
                if "slash" in attrs["grace"]:
                    slash = YesNoToBool(attrs["grace"]["slash"])
            data["note"].addNotation(Note.GraceNote(slash=slash, first=True))
        if tag[-1] == "duration" and "note" in tag:
            if not hasattr(data["note"], "duration"):
                data["note"].duration = float(content["duration"])

        if tag[-1] == "type":
            data["note"].SetType(content["type"])

        if tag[-1] == "dot":
            data["note"].addDot()
        if tag[-1] == "tie":
            data["note"].AddTie(attrs["tie"]["type"])
        if "chord" in tag:
            data["note"].chord = True
        if tag[-1] == "stem":
            data["note"].stem = Note.Stem(content["stem"])
        if tag[-1] == "voice":
            data["voice"] = int(content["voice"])

        if tag[-1] == "beam":
            type = ""
            if "beam" in content:
                type = content["beam"]
            if "beam" in attrs:
                id = int(attrs["beam"]["number"])
            else:
                id = len(data["note"].beams)
            part_id = helpers.GetID(attrs, "part", "id")
            part = piece.getPart(part_id)
            part.NewBeam(type, data["staff_id"])
            data["note"].addBeam(id, Note.Beam(type))

        if tag[-1] == "accidental":
            if not hasattr(data["note"], "pitch"):
                data["note"].pitch = Note.Pitch()
                if "accidental" in content:
                    data["note"].pitch.accidental = content["accidental"]

            else:
                if "accidental" in content:
                    data["note"].pitch.accidental = content["accidental"]
        if tag[-1] == "staff":
            data["staff_id"] = int(content["staff"])
    HandleNoteheads(tag, attrs, content, piece, data)
    HandleArpeggiates(tag, attrs, content, piece, data)
    HandleSlidesAndGliss(tag, attrs, content, piece, data)
    handleLyrics(tag, attrs, content, piece, data)
    handleOrnaments(tag, attrs, content, piece, data)
    handleOtherNotations(tag, attrs, content, piece, data)
    handleTimeMod(tag, attrs, content, piece, data)
    return ret_value


def HandleNoteheads(tags, attrs, content, piece, data):
    if "note" in tags:
        if tags[-1] == "notehead":
            data["note"].notehead = Note.Notehead()
            if "notehead" in attrs:
                if "filled" in attrs["notehead"]:
                    filled = YesNoToBool(attrs["notehead"]["filled"])
                    data["note"].notehead.filled = filled
            if "notehead" in content:
                data["note"].notehead.type = content["notehead"]


def HandleArpeggiates(tags, attrs, content, piece, data):
    if len(tags) > 0:
        if tags[-1] == "arpeggiate":
            data["direction"] = None
            if "arpeggiate" in attrs:
                if "direction" in attrs["arpeggiate"]:
                    data["direction"] = attrs["arpeggiate"]["direction"]
            arpegg = Note.Arpeggiate(direction=data["direction"])
            data["note"].addNotation(arpegg)
        if tags[-1] == "non-arpeggiate":
            type = None
            if "non-arpeggiate" in attrs:
                if "type" in attrs["non-arpeggiate"]:
                    type = attrs["non-arpeggiate"]["type"]
            narpegg = Note.NonArpeggiate(type=type)
            data["note"].addNotation(narpegg)


def HandleSlidesAndGliss(tags, attrs, content, piece, data):
    type = None
    number = None
    lineType = None
    if "slide" in tags or "glissando" in tags:
        if tags[-1] in attrs:
            if "type" in attrs[tags[-1]]:
                type = attrs[tags[-1]]["type"]
            if "line-type" in attrs[tags[-1]]:
                lineType = attrs[tags[-1]]["line-type"]
            if "number" in attrs[tags[-1]]:
                number = int(attrs[tags[-1]]["number"])
    if "slide" in tags:
        slide = Note.Slide(type=type, lineType=lineType, number=number)
        data["note"].addNotation(slide)
    if "glissando" in tags:
        gliss = Note.Glissando(type=type, lineType=lineType, number=number)
        data["note"].addNotation(gliss)


def handleOrnaments(tags, attrs, content, piece, data):
    if "ornaments" in tags:
        if tags[-1] == "inverted-mordent":
            data["note"].addNotation(Ornaments.InvertedMordent())
        if tags[-1] == "mordent":
            data["note"].addNotation(Ornaments.Mordent())
        if tags[-1] == "trill-mark":
            data["note"].addNotation(Ornaments.Trill())
        if tags[-1] == "wavy-line":
            type = ""
            if "wavy-line" in attrs:
                if "type" in attrs["wavy-line"]:
                    type = attrs["wavy-line"]["type"]
                else:
                    type = True
            data["note"].addNotation(Ornaments.TrillSpanner(line=type))
        if tags[-1] == "turn":
            data["note"].addNotation(Ornaments.Turn())
        if tags[-1] == "inverted-turn":
            data["note"].addNotation(Ornaments.InvertedTurn())
        if tags[-1] == "tremolo":
            type = None
            value = None
            if "tremolo" in attrs:
                if "type" in attrs["tremolo"]:
                    type = attrs["tremolo"]["type"]
            if "tremolo" in content:
                value = int(content["tremolo"])
            data["note"].addNotation(Ornaments.Tremolo(type=type, value=value))


def SetupFormat(tags, attrs, text, piece, data):
    return None


def HandlePitch(tags, attrs, text, piece, data):
    return_val = None
    if len(tags) > 0:
        if "pitch" or "unpitched" in tags:
            if not hasattr(data["note"], "pitch") and data["note"] is not None:
                data["note"].pitch = Note.Pitch()
            if "unpitched" in tags:
                data["note"].pitch.unpitched = True
            if "step" in tags[-1]:
                if "step" not in text:
                    data["note"].pitch.step = text["display-step"]
                else:
                    data["note"].pitch.step = text["step"]
                return_val = 1
            if tags[-1] == "alter":
                data["note"].pitch.alter = int(text["alter"])
                return_val = 1
            if "octave" in tags[-1]:
                if "octave" not in text:
                    data["note"].pitch.octave = text["display-octave"]
                else:
                    data["note"].pitch.octave = text["octave"]
                return_val = 1
    return return_val


def HandleDirections(tags, attrs, chars, piece, data):
    global expressions, items
    return_val = None
    if len(tags) == 0:
        return None

    if "direction" in tags:
        measure_id = IdAsInt(helpers.GetID(attrs, "measure", "number"))
        part_id = helpers.GetID(attrs, "part", "id")
        measure =  None
        if measure_id is not None and part_id is not None:
            measure =  piece.getPart(part_id).getMeasure(
                measure_id, data["staff_id"])
            if measure is None:
                piece.getPart(part_id).addEmptyMeasure(
                    measure_id, data["staff_id"])
                measure =  piece.getPart(part_id).getMeasure(
                    measure_id,
                    data["staff_id"])
        placement = None
        if measure is None:
            return None
        if tags[-1] == "staff":
            data["staff_id"] = int(chars["staff"])
        if "direction" in attrs:
            if "placement" in attrs["direction"]:
                placement = attrs["direction"]["placement"]

        if tags[-1] == "words":
            return_val = 1

            size = None
            font = None
            text = None
            if "words" in chars:
                text = chars["words"]

            if "words" in attrs:
                if "font-size" in attrs["words"]:
                    size = attrs["words"]["font-size"]
                if "font-family" in attrs["words"]:
                    font = attrs["words"]["font-family"]
            data["direction"] = Directions.Direction(
                font=font,
                text=text,
                size=size,
                placement=placement)
        if tags[-1] == "voice":
            data["voice"] = int(chars["voice"])
        if tags[-1] == "rehearsal":
            return_val = 1

            size = None
            font = None
            text = chars["rehearsal"]

            if "words" in attrs:
                if "font-size" in attrs["words"]:
                    size = attrs["words"]["font-size"]
                if "font-family" in attrs["words"]:
                    font = attrs["words"]["font-family"]
            data["direction"] = Directions.RehearsalMark(
                font=font,
                text=text,
                size=size,
                placement=placement)
        if tags[-1] == "metronome":
            if data["direction"] is not None:
                if type(data["direction"]) != Directions.Metronome:
                    new_obj = Directions.Metronome(
                        text=copy.deepcopy(data["direction"]))
                    data["direction"] = new_obj
        if "metronome" in tags:
            if tags[-1] == "beat-unit":
                return_val = 1
                unit = chars["beat-unit"]
                if data["direction"] is None:
                    data["direction"] = Directions.Metronome(
                        placement=placement,
                        beat=unit)
                else:
                    if not hasattr(
                            data["direction"],
                            "beat") or data["direction"].beat is None:
                        data["direction"].beat = unit
                    else:
                        data["direction"].secondBeat = unit
                    data["direction"].placement = placement
                if "metronome" in attrs:
                    if "font-family" in attrs["metronome"]:
                        data["direction"].font = attrs[
                            "metronome"]["font-family"]
                    if "font-size" in attrs["metronome"]:
                        data["direction"].size = attrs[
                            "metronome"]["font-size"]
                    if "parentheses" in attrs["metronome"]:
                        data["direction"].parentheses = YesNoToBool(
                            attrs["metronome"]["parentheses"])
            if tags[-1] == "per-minute":
                return_val = 1
                pm = chars["per-minute"]

                if data["direction"] is None:
                    data["direction"] = Directions.Metronome(min=pm)
                else:
                    data["direction"].min = pm
                if "metronome" in attrs:
                    if "font-family" in attrs["metronome"]:
                        data["direction"].font = attrs[
                            "metronome"]["font-family"]
                    if "font-size" in attrs["metronome"]:
                        data["direction"].size = float(
                            attrs["metronome"]["font-size"])
                    if "parentheses" in attrs["metronome"]:
                        data["direction"].parentheses = YesNoToBool(
                            attrs["metronome"]["parentheses"])
        if tags[-1] == "wedge":
            w_type = None
            if "wedge" in attrs:
                if "type" in attrs["wedge"]:
                    w_type = attrs["wedge"]["type"]
            data["expression"] = Directions.Wedge(
                placement=placement, type=w_type)

        if len(tags) > 1:
            if tags[-2] == "dynamics" and tags[-1] != "other-dynamics":
                data["expression"] = Directions.Dynamic(
                    placement=placement, mark=tags[-1])
            if tags[-2] == "dynamics" and tags[-1] == "other-dynamics":
                data["expression"] = Directions.Dynamic(
                    placement=placement,
                    text=chars["other-dynamics"])

        if "sound" in tags:
            return_val = 1
            if "sound" in attrs:
                if "dynamics" in attrs["sound"]:
                    measure.volume = attrs["sound"]["dynamics"]
                if "tempo" in attrs["sound"]:
                    measure.tempo = attrs["sound"]["tempo"]
        l_type = None
        if tags[-1] in ["wavy-line", "octave-shift", "pedal", "bracket"]:
            if tags[-1] in attrs:
                if "type" in attrs[tags[-1]]:
                    l_type = attrs[tags[-1]]["type"]
        if "octave-shift" in tags:
            amount = None
            font = None
            if "octave-shift" in attrs:
                if "size" in attrs["octave-shift"]:
                    amount = int(attrs["octave-shift"]["size"])
                if "font" in attrs["octave-shift"]:
                    font = attrs["octave-shift"]["font"]
            data["direction"] = Directions.OctaveShift(
                type=l_type,
                amount=amount,
                font=font)

        if tags[-1] == "wavy-line":
            data["direction"] = Directions.WavyLine(type=l_type)
        if tags[-1] == "pedal":
            line = None
            if "pedal" in attrs:
                if "line" in attrs["pedal"]:
                    line = YesNoToBool(attrs["pedal"]["line"])
            data["direction"] = Directions.Pedal(line=line, type=l_type)
        if tags[-1] == "bracket":
            num = None
            ltype = None
            elength = None
            lineend = None
            if "bracket" in attrs:
                if "number" in attrs["bracket"]:
                    num = int(attrs["bracket"]["number"])
                if "line-type" in attrs["bracket"]:
                    ltype = attrs["bracket"]["line-type"]
                if "end-length" in attrs["bracket"]:
                    elength = int(attrs["bracket"]["end-length"])
                if "line-end" in attrs["bracket"]:
                    lineend = attrs["bracket"]["line-end"]
            data["direction"] = Directions.Bracket(
                lineEnd=lineend,
                elength=elength,
                type=l_type,
                ltype=ltype,
                number=num)
    HandleRepeatMarking(tags, attrs, chars, piece, data)

    return return_val


def HandleRepeatMarking(tags, attrs, chars, piece, data):
    global last_note
    if "direction" in tags or "forward" in tags:
        if tags[-1] == "voice":
            data["voice"] = int(chars["voice"])
        measure =  None
        part_id = helpers.GetID(attrs, "part", "id")
        measure_id = IdAsInt(helpers.GetID(attrs, "measure", "number"))
        if part_id is not None:
            if measure_id is not None:
                measure =  piece.getPart(part_id).getMeasure(
                    measure_id,
                    data["staff_id"])

        if measure is not None:
            d_type = None

            if tags[-1] == "segno" or tags[-1] == "coda":
                d_type = tags[-1]
                data["direction"] = Directions.RepeatSign(type=d_type)

            if tags[-1] == "sound":
                if "sound" in attrs:
                    if "coda" in attrs["sound"]:
                        measure.coda = attrs["sound"]["coda"]
                    if "dacapo" in attrs["sound"]:
                        measure.dacapo = YesNoToBool(attrs["sound"]["dacapo"])
                    if "dalsegno" in attrs["sound"]:
                        measure.dalsegno = attrs["sound"]["dalsegno"]
                    if "fine" in attrs["sound"]:
                        measure.fine = YesNoToBool(attrs["sound"]["fine"])
                    if "segno" in attrs["sound"]:
                        measure.segno = attrs["sound"]["segno"]
                    if "tocoda" in attrs["sound"]:
                        measure.tocoda = attrs["sound"]["tocoda"]


def handleLyrics(tags, attrs, chars, piece, data):
    if "lyric" in tags:
        if not hasattr(data["note"], "lyrics"):
            data["note"].lyrics = {}
        number = len(data["note"].lyrics)
        if "lyric" in attrs:
            if "number" in attrs["lyric"]:
                number = int(attrs["lyric"]["number"])
        if number not in data["note"].lyrics:
            data["note"].lyrics[number] = Directions.Lyric()
        if tags[-1] == "text":
            data["note"].lyrics[number].text = chars["text"]
        if tags[-1] == "syllabic":
            data["note"].lyrics[number].syllabic = chars["syllabic"]


def handleTimeMod(tags, attrs, chars, piece, data):
    if "notations" in tags:
        if tags[-1] == "tuplet":
            type = None
            bracket = None

            if "tuplet" in attrs:
                if "type" in attrs["tuplet"]:
                    type = attrs["tuplet"]["type"]
                if "bracket" in attrs["tuplet"]:
                    bracket = YesNoToBool(attrs["tuplet"]["bracket"])
            tuplet = Note.Tuplet(bracket=bracket, type=type)
            data["note"].addNotation(tuplet)
    if "time-modification" in tags:
        if not hasattr(data["note"], "timeMod"):
            data["note"].timeMod = Note.TimeModifier()
        if tags[-1] == "actual-notes":
            data["note"].timeMod.actual = int(chars["actual-notes"])
        if tags[-1] == "normal-notes":
            data["note"].timeMod.normal = int(chars["normal-notes"])
    return None


def CheckDynamics(tag):
    return_val = False
    dmark = ["p", "f"]
    if len(tag) == 1 and tag in dmark:
        return_val = True
    elif len(tag) == 2:
        if tag[-1] in dmark:
            if tag[0] == tag[-1] or tag[0] == "m" or tag[0] == "s":
                return_val = True
    if len(tag) > 2:
        val = tag[0]
        if val in dmark:
            for char in tag:
                if char == val:
                    return_val = True
                else:
                    return_val = False
    return return_val
