# -*- coding: utf-8 -*-
"""
Copyright 2021, Nils Hilbricht, Germany ( https://www.hilbricht.net )

This file is part of Laborejo ( https://www.laborejo.org )

Laborejo 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 3 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, see <http://www.gnu.org/licenses/>.
"""
import logging; logger = logging.getLogger(__name__); logger.info("import")

#Standard Library Modules
from collections import deque
from fractions import Fraction
from weakref import WeakSet, WeakValueDictionary
from warnings import warn

#Third Party Modules
from calfbox import cbox

#Template Modules
import template.engine.pitch as pitchmath
import template.engine.duration as duration
from template.engine.duration import DM, DB, DL, D1, D2, D4, D8, D16, D32, D64, D128, D256, D512, D1024, D_DEFAULT, D_STACCATO, D_TENUTO, D_TIE
from template.helper import EndlessGenerator, flatList

##########################
## Part of other items ##
##########################

class Note(object):
    """A note cannot be inserted in a track directly. It relies on a Chord structure.

    A note is not the level of operation. All note operations go through the notes parent Chord
    item. This is not only a design but is actually needed for undo. Only the Chord methods return
    their reverse operation for the undo system to register.

    for example note.octaveUp just sets self.pitch an octave up and returns None."""

    allNotes = WeakValueDictionary() #key is the noteId, value is the weak reference to the Note. AND NOT CHORDS

    def __init__(self, parentChord, duration, pitch):
        self.pitch = pitch #a value from constants.py with octave modifier
        self.duration = duration  #a Duration instance
        self.dynamic = Dynamic()
        self._secondInit(parentChord)

    def _secondInit(self, parentChord):
        """see Item._secondInit"""
        self.parentChord = parentChord
        self.rememberNote()

    @property
    def pitch(self):
        if self._pitch < 20:
            warn("Pitch {} lower than the lowest note detected. Exported normalized non-destructively to lowest possible note. Please raise pitch again manually before further editing.".format(self._pitch))
            return 20
        elif self._pitch > 3140:
            warn("Pitch {} higher than the highest note detected. Exported normalized non-destructively to highest possible note. Please raise pitch again manually before further editing.".format(self._pitch))
            return 3140
        else:
            return self._pitch

    @pitch.setter
    def pitch(self, value):
        self._pitch = value

    @classmethod
    def instanceFromSerializedData(cls, serializedObject, parentObject):
        """see Score.instanceFromSerializedData"""
        assert cls.__name__ == serializedObject["class"]
        self = cls.__new__(cls)
        self._pitch = int(serializedObject["pitch"])
        self.duration = Duration.instanceFromSerializedData(serializedObject["duration"], parentObject = self)
        self.dynamic = Dynamic.instanceFromSerializedData(serializedObject["dynamic"], parentObject = self)
        self._secondInit(parentChord = parentObject)
        return self

    def serialize(self):
        result = {}
        result["class"] = "Note"
        result["duration"] = self.duration.serialize()
        result["pitch"] = self._pitch
        result["dynamic"] = self.dynamic.serialize()
        return result

    def rememberNote(self):
        oid = id(self)
        Note.allNotes[oid] = self
        #weakref_finalize(self, print, "deleted note "+str(oid))
        return oid

    def copy(self, newParentChord):
        new = Note(newParentChord, self.duration.copy(), self._pitch)
        new.dynamic = self.dynamic.copy()
        return new

    def __lt__(self, other): # self < other
        return self.pitch < other.pitch
    def __le__(self, other): # self <= other
        return self.pitch <= other.pitch
    def __eq__(self, other): # self == other
        return self.pitch == other.pitch
    def __ne__(self, other): # self != other
        return self.pitch != other.pitch
    def __gt__(self, other): # self > other
        return self.pitch > other.pitch
    def __ge__(self, other): # self >= other
        return self.pitch >= other.pitch

    def asDotOnLine(self, clef):
        return pitchmath.distanceInDiatonicStepsFrom1720[self.pitch] + clef.asDotOnLineOffset

    def accidental(self, keysig):
        """-20, -10, 0, +10, +20
        and just 1, which is Natural"""
        diff = pitchmath.diffToKey(self.pitch, keysig)

        #If there is a variation to the keysig. We need to find out if a white note has to get an accidental or an already altered noted (from the keysig) gets a natural sign.
        if diff and self.pitch == pitchmath.toWhite[self.pitch]:
            return 1 #natural
        elif diff == 0: #This just means it is the same as the keysig.
            return diff
        else: #it is different to the keysig. Traditionally the accidental shows the difference to white, not to the keysig. eis in C-minor is #e not Xe/##e
            return self.pitch - pitchmath.toWhite[self.pitch]

    #Inplace pitch modifications
    def sharpen(self):
        self.pitch = pitchmath.sharpen(self.pitch)

    def flatten(self):
        self.pitch = pitchmath.flatten(self.pitch)

    def intervalAutomatic(self, rootPitch, targetPitch):
        """Change the notepitch with the same interval that is between
        rootPitch and targetPitch"""
        self.pitch = pitchmath.intervalAutomatic(self.pitch, rootPitch, targetPitch)

    def mirrorAroundCursor(self, cursorPitch):
        self.pitch = pitchmath.mirror(self.pitch, axis=cursorPitch)

    #def toWhite(self):
    #    self.pitch = pitch.toWhite[self.pitch]

    #def mirror(self, keysig, axis):
    #    """Wants two pitches"""
    #    self.pitch = pitch.mirror(self.pitch, axis)
    #    self.toScale(keysig)

    def octaveUp(self):
        self.pitch = self.pitch + 350

    def octaveDown(self):
        self.pitch = self.pitch - 350

    def toScale(self, keysig):
        self.pitch = pitchmath.toScale(self.pitch, keysig)

    def stepUpInScale(self, keysig):
        self.pitch += 50
        self.toScale(keysig) #adjust to keysig.

    def stepDownInScale(self, keysig):
        self.pitch -= 50
        self.toScale(keysig) #adjust to keysig.

    def exportObject(self, trackState):
        dur = self.duration.completeDuration()
        on, off = self.duration.noteOnAndOff(trackState, dur)
        result = {
            "type" : "note",
            "id" : id(self),
            "accidental" : self.accidental(trackState.keySignature()),
            "dotOnLine" : self.asDotOnLine(trackState.clef()),
            "dots" : self.duration.dots,
            "tuplet" : self.duration.tuplet,
            "notehead" : self.duration.notehead,

            "completeDuration" : dur,
            #"leftModInTicks" : self.duration.startModInTicks(trackState), #only for gui. midi is in self.noteOnAndOff
            "leftModInTicks" : on,
            #"rightModInTicks" : self.duration.endModInTicks(trackState), #only for gui. midi is in self.noteOnAndOff
            "rightModInTicks" : off - dur,

            "tieDistanceInTicks" : dur, #because technically that is right. This note sounds until this value.
            "tie" : "", #empty, first, notFirst  . notFirst is the end of a tie-sequence as well as a middle tie:   c ~ [c] ~ c

            "manualOverride" : bool(self.duration.shiftStart or (self.duration.shiftEnd and not self.duration.durationKeyword == D_TIE)),
            "durationKeyword" : self.duration.durationKeyword, #an int. We internally use constants through module constants.D_KEYWORD
            "velocity" : self.dynamic.velocity(trackState), #int
            "velocityManualOverride" : bool(self.dynamic.velocityModification),
            "endTick" : trackState.tickindex, #for ties.
            "dynamicKeyword" : self.dynamic.dynamicKeyword, #int/"enum", defined in constants. One-time Dynamic Signature for sfz, fp and accents.
            }

        #Handle tied notes.
        if self.pitch in trackState.EXPORTtiedNoteExportObjectsWaitingForClosing:
            result["tie"] = "notFirst"
            if not self.duration.durationKeyword == D_TIE: #this was the last tied note, not a middle.
                trackState.EXPORTtiedNoteExportObjectsWaitingForClosing[self.pitch]["rightModInTicks"] = trackState.tickindex - trackState.EXPORTtiedNoteExportObjectsWaitingForClosing[self.pitch]["endTick"] + result["rightModInTicks"]
                trackState.EXPORTtiedNoteExportObjectsWaitingForClosing[self.pitch]["tieDistanceInTicks"] = trackState.tickindex - dur - trackState.EXPORTtiedNoteExportObjectsWaitingForClosing[self.pitch]["endTick"] + trackState.EXPORTtiedNoteExportObjectsWaitingForClosing[self.pitch]["completeDuration"]

                del trackState.EXPORTtiedNoteExportObjectsWaitingForClosing[self.pitch] #delete this pitch from the dict. Obviously this does not delete the exportObject.
        elif self.duration.durationKeyword == D_TIE: #since we already made sure this pitch is not waiting for a closing/middle tie but we are a tie we can be sure to be the first in a sequence.
            result["tie"] = "first"
            trackState.EXPORTtiedNoteExportObjectsWaitingForClosing[self.pitch] = result

        self._cachedExportObject = result

        return result

    def lilypond(self, carryLilypondRanges):
        """Called by chord.lilypond(carryLilypondRanges), See Item.lilypond for the general docstring.
        Returns two strings, pitch and duration.
        The duration is per-chord or needs special polyphony."""
        return pitchmath.pitch2ly[self.pitch], self.duration.lilypond(carryLilypondRanges)

class Dynamic(object):
    """dynamic means velocity midi terms.
    (but not volume and not expression).

    Absolute velocity can never go over 128 or below 0. 0 means mute.

    This is not a Dynamic Item which can be inserted into a track.

    Only notes have a working dynamic attribute.
    """
    def __init__(self):
        self.velocityModification = 0 #signed int which will simply get added to the current dynamic signatures value.
        self.dynamicKeyword = 0 #TODO One-time Dynamic Signature for sfz, fp and accents.

    def _secondInit(self, parentNote):
        """see Item._secondInit"""
        pass

    @classmethod
    def instanceFromSerializedData(cls, serializedObject, parentObject):
        """see Score.instanceFromSerializedData"""
        assert cls.__name__ == serializedObject["class"]
        self = cls.__new__(cls)
        self.velocityModification = int(serializedObject["velocityModification"])
        self.dynamicKeyword = int(serializedObject["dynamicKeyword"])
        self._secondInit(parentNote = parentObject)
        return self

    def serialize(self):
        result = {}
        result["class"] = "Dynamic"
        result["velocityModification"] = self.velocityModification
        result["dynamicKeyword"] = self.dynamicKeyword
        return result

    def copy(self):
        new = Dynamic()
        new.velocityModification = self.velocityModification #int, needs no special copy
        new.dynamicKeyword = self.dynamicKeyword  #int, needs no special copy
        return new

    def velocity(self, trackState):
        #todo: if keyword. see init
        base = trackState.dynamicSignature().baseVelocity(trackState)
        if trackState.dynamicRamp: #maybe override the baseVelocity.
            vel = trackState.dynamicRamp.getVelocity(trackState.tickindex - trackState.track.previousItem().logicalDuration() ) #TODO: The tickindex is already ahead of the current item. Somehow in the whole program that doesn't matter except for DynamicRamps. this is really strange. I don't know if this is a design error or one of the few exceptions. Actually, a rather prominent "exception" is the exportObject for every item which is state.tickindex - item.logicalDuration() and even has a comment. So I guess it is alright.
            if not vel is None: #0 is good. None means there is a DynamicRamp but not a target velocity. So we ignore it for now.
                base = vel

        value = int(base + self.velocityModification)
        if value < 0:
            return 0
        elif value > 127:
            return 127
        else:
            return value

class Duration(object):
    """Don't be confused by the amount of durations in the score.
    Each item has a duration. A clef has a duration, including the default one in TrackState.
    A chord has a null-duration itself, because it is an Item, and also each note in the chord
    has its own duration. These are the real durations.

    There are multiple concepts of "duration" in the program:

    baseDuration: The note without dots, tuplets or other modifications. This is the basic note
    on a sheet of paper.

    completeDuration: baseDuration with dots and tuplets. But no shifting(staccato, manual mod etc.)

    noteOnAndOff: the final tick value for midi export. complete duration with all
    expression modifications like staccato, slurs or tenuto and user fine control

    logicalDuration: Value used to calculate the order and spacing of items. aka. determines how
    many ticks the cursor advances when parsing the item or how many pixels space are needed in a
    GUI before the next item. For normal chords this is the same as completeDuration.
    But this also includes values for MultiMeasure Rests and other special cases.
    Note that this is not the sounding duration from noteOnAndOff but the "original" duration
    without any interpretation or expression based modifications.

    The duration of a chord is called "durationGroup", which is it's own class. Why the name
    mismatch? Because all the data that matters to the outside gets exported to a static dict.

    For chords the logicalDuration is the shortest logical note. Example: A chord consists of
    a half note with staccato and a quarter note which is extended by quite a bit through manual
    shifting. The sounding quarter note is longer than the sounding half note.
    noteOnAndOff(half note) < noteOnAndOff(quarter).
    But the logical duration is still the completeDuration where a half note is always longer than
    a quarter note.
    """

    def __init__(self, baseDuration):
        self._baseDuration = baseDuration #base value. Without dots, tuplets/times , overrides etc. this is the duration for all representations
        #DEPRECTATED. WILL BREAK SAVE FILES. self.tuplets = [] # a list of tuplets [(numerator, denominator), (,)...]. normal triplet is (2,3). Numerator is the level of the notehead. 2^n Each is one level of tuplets, arbitrary nesting depth. [2,3] for triplet # Tuplet multiplication is used for all representations and gets auto-merged on lilypond output to tuplet groups (if its in a chord).   This is a list and not tuple() because json load will make it a list anyway.
        self.tuplet = None #a single tuple (numerator, denominator). There is no nesting in Laborejo. Numerator is the level of the notehead. 2^n Each is one level of tuplets, arbitrary nesting depth. [2,3] for triplet # Tuplet multiplication is used for all representations and gets auto-merged on lilypond output to tuplet groups (if its in a chord).
        self.dots = 0 #number of dots.
        self.durationKeyword = D_DEFAULT
        #The offsets shift the start and ending to the left and right or rather: earlier and later. positive values mean later, negative values mean earlier.
        self.shiftStart = 0 # in ticks
        self.shiftEnd = 0 # in ticks
        self._secondInit(parentItem = None) #see Item._secondInit

    def _secondInit(self, parentItem):
        """see Score._secondInit"""
        #discard parentItem
        self.genericNumber = self.calcGenericNumber()
        self.notehead = self.calcNotehead() #depends on self.genericNumber
        #self.cachedCompleteDuration = None

    def __setattr__(self, name, value):
        #print ("change!", self, name, value)
        super().__setattr__(name, value)
        super().__setattr__("cachedCompleteDuration", None)


    @classmethod
    def createByGuessing(cls, completeDuration): #class method. no self.
        """Return a new Duration. A constructor class method.
        Take one absolute tick value and guess what is the best
        duration combination (base, tuplets, dots...) for it.

        createByGuessing exists to distinguish between dotted notes and fractions.
        This means that the duration must be somehow musical precise and without fuzzyness.
        You can't shove arbitrary live-recorded duration values in here. It is not a quantizer"""

        def _closest(num, datalist):
            """only for positive numbers"""
            if num in datalist:
                return num
            else:
                positions = len(datalist)
                tmplist = [0] + sorted(datalist)
                tmplist.reverse()
                #Loop over all positions except the last 0.

                for x in range(positions):
                    if tmplist[x+1] < num < tmplist[x]:
                        return tmplist[x]
                else: #the highest value in datalist was smaller
                    return None

        if completeDuration == int(completeDuration):
            completeDuration = int(completeDuration)

        durList = [D1024, D512, D256, D128, D64, D32, D16, D8, D4, D2, D1, DB, DL, DM]
        guessedBase = _closest(completeDuration, durList)

        if guessedBase:
            if completeDuration in durList:  #no need for guessing
                new = cls(completeDuration)
            elif completeDuration/1.5 in durList:  #single dot
                new = cls(completeDuration/1.5)
                new.dots = 1
            elif completeDuration/1.5/1.5 in durList:  #double dots
                new = cls(completeDuration/1.5/1.5)
                new.dots = 2
            elif completeDuration *3/2 in durList:  #triplets are so a common we take care of them instead of brute forcing with fractions in the else branch
                new = cls(guessedBase)
                new.tuplet = (2,3)
            else: #tuplet. That means the value is below a standard "notehead" duration. We need to find how much much lower.
                new = cls(guessedBase)
                #ratio = completeDuration / guessedBase #0.666~ for triplet
                newRatio = Fraction(int(completeDuration), guessedBase).limit_denominator(100000) #protects 6 or 7 decimal positions
                new.tuplet = (newRatio.numerator, newRatio.denominator)
        else:
            #the base could not be guessed. In this case we just create a non-standard note.
            warn("non-standard duration generated")
            new = cls(int(completeDuration)) #210, minimum base duration value.

        return new

    @classmethod
    def instanceFromSerializedData(cls, serializedObject, parentObject):
        """see Score.instanceFromSerializedData"""
        assert cls.__name__ == serializedObject["class"]
        self = cls.__new__(cls)
        self._baseDuration = int(serializedObject["baseDuration"])

        if "tuplets" in serializedObject and serializedObject["tuplets"]:
            #old file format, which allowed nested tuplets. Was never used by anyone.
            #convert to new single-tuplet format and this will be gone next save.
            self.tuplet = serializedObject["tuplets"][0] #just take the first element. There was never a nested tuple, the gui never supported it.
        elif "tuplet" in serializedObject and serializedObject["tuplet"]:
            #Current file format.
            self.tuplet = tuple(serializedObject["tuplet"])
        else:
            self.tuplet = None
        self.dots = int(serializedObject["dots"])
        self.durationKeyword = int(serializedObject["durationKeyword"])
        self.shiftStart = int(serializedObject["shiftStart"])
        self.shiftEnd = int(serializedObject["shiftEnd"])
        self._secondInit(parentItem = parentObject)
        return self

    def serialize(self):
        result = {}
        result["class"] = "Duration"
        result["baseDuration"] = self._baseDuration
        result["tuplet"] = self.tuplet
        result["dots"] = self.dots
        result["shiftStart"] = self.shiftStart
        result["shiftEnd"] = self.shiftEnd
        result["durationKeyword"] = self.durationKeyword
        return result

    @property
    def baseDuration(self):
        return self._baseDuration

    @baseDuration.setter
    def baseDuration(self, value):
        """implement limits"""
        if value < D1024:
            value = D1024
        elif value > DM:
            value = DM
        self._baseDuration = value

    def copy(self):
        new = Duration(self.baseDuration)
        new.tuplet = self.tuplet
        new.dots = self.dots
        new.genericNumber = new.calcGenericNumber()
        new.notehead = new.calcNotehead()
        new.durationKeyword = self.durationKeyword
        new.shiftStart = self.shiftStart
        new.shiftEnd = self.shiftEnd
        return new

    def augment(self):
        self.baseDuration *= 2
        self.genericNumber = self.calcGenericNumber()
        self.notehead = self.calcNotehead()

    def diminish(self):
        self.baseDuration /= 2
        self.genericNumber = self.calcGenericNumber()
        self.notehead = self.calcNotehead()

    def calcGenericNumber(self):
        """A number for humans and lilypond. 4 = quarter, 2 = half"""
        return duration.baseDurationToTraditionalNumber[self.baseDuration]

    def calcNotehead(self):
        """What classical notehead would be used
        to express baseDuration"""
        if self.genericNumber >= 4:
            return 4
        else:
            return self.genericNumber

    def completeDuration(self):
        """Ticks with dots and tuplets. But not leftMod rightMod
         x = basic note value , n = number of dots: 2x - x/2^n

        This function doesn't seem to be much. Why all the optimisation?
        Because it is called very very often. many times or so for a single note.
        """
        if not self.baseDuration:
            return 0

        if self.cachedCompleteDuration:
            return self.cachedCompleteDuration
        else:
            value = 2 * self.baseDuration - self.baseDuration / 2**self.dots
            if self.tuplet:
                numerator, denominator = self.tuplet
                value = value * numerator / denominator

            if not value == int(value):
                raise ValueError("Only integers are allowed as durations. {} is not {}.".format(value, int(value)))  #TODO: leave this in until real world testing. We *could* live with floats for very complex tuplets, but let's see where this leads us.

            value = int(value)
            super().__setattr__("cachedCompleteDuration", value)
            return value

    def noteOnAndOff(self, trackState, completeDuration):
        """Midi only.
        return a tick value, the offset from an imaginary
        starting point on the tick/time axis, which needs to be
        added later. Think t=0 during this function.

        We do not export the actual duration since this function
        is intended for midi which only needs start and end points
        and calculates the duration itself."""
        on = self.startModInTicks(trackState)(completeDuration)
        off = completeDuration + self.endModInTicks(trackState)(completeDuration)
        if on > off:
            warn("Your calculation resulted in a note off before note on. Please change it manually. Forced to standard note on/off.\n{}\n{}".format(self.startModInTicks(trackState), self.endModInTicks(trackState)))
            return 0, completeDuration
        else:
            return (on, off)

    def startModInTicks(self, trackState):
        """Modifications have different priority. A user created mod
        is always highest priority.
        Contexts like slurs or other instructions are next.
        Then follow individual duration keywords. There can only be one
        keyword at a time."""
        if self.shiftStart:
            return lambda x: self.shiftStart
        elif trackState.duringLegatoSlur:
            return trackState.track.durationSettingsSignature.legatoOn
        elif self.durationKeyword == D_DEFAULT or self.durationKeyword ==  D_TIE:
            return trackState.track.durationSettingsSignature.defaultOn
        elif self.durationKeyword ==  D_STACCATO:
            return trackState.track.durationSettingsSignature.staccatoOn
        elif self.durationKeyword == D_TENUTO:
            return trackState.track.durationSettingsSignature.tenutoOn
        else:
            raise ValueError("Duration Keyword {} unknown".format(self.durationKeyword))

    def endModInTicks(self, trackState):
        """see Duration.startMod"""
        if self.shiftEnd:
            return lambda x: self.shiftEnd
        elif trackState.duringLegatoSlur:
            if self.durationKeyword == D_STACCATO: #slur plus staccato cancels each other out
                return trackState.track.durationSettingsSignature.defaultOff
            else:
                return trackState.track.durationSettingsSignature.legatoOff
        elif self.durationKeyword == D_DEFAULT or self.durationKeyword == D_TIE:
            return trackState.track.durationSettingsSignature.defaultOff
        elif self.durationKeyword ==  D_STACCATO:
            return trackState.track.durationSettingsSignature.staccatoOff
        elif self.durationKeyword == D_TENUTO:
            return trackState.track.durationSettingsSignature.tenutoOff
        else:
            raise ValueError("Duration Keyword {} unknown".format(self.durationKeyword))

    def toggleDurationKeyword(self, keywordConstant):
        """Activate a keyword for a note. If the note already has one,
        overwrite it. Except it already has the same keyword, then
        remove it"""
        if self.durationKeyword == keywordConstant:
            self.durationKeyword = D_DEFAULT
        else:
            self.durationKeyword = keywordConstant

    def toggleDot(self):
        if self.dots == 1:
            self.dots = 2
        elif self.dots == 0:
            self.dots = 1
        else:
            self.dots = 0

    def toggleTriplet(self):
        if self.tuplet == (2,3):
            self.tuplet = None
        else:
            self.tuplet = (2,3)

    def lilypond(self, carryLilypondRanges):
        """Called by note.lilypond(carryLilypondRanges), See Item.lilypond for the general docstring.
        returns a number as string."""

        if self.durationKeyword == D_TIE:
            append = "~"
        else:
            append = ""

        n = self.genericNumber
        if n == 0:
            return "\\breve" + append
        elif n == -1:
            return "\\longa" + append
        elif n == -2:
            return "\\maxima" + append
        else:
            return str(n) + self.dots*"." + append

class DurationGroup(object):
    """Holds several durations and returns values meant for chords.
    Always up to date since notelist is a mutable data type.

    Is compatible to Duration() methods and parameters.

    Midi will send the actual note durations, per-note.
    For the representation (GUI) and logical calculations always the minimum
    note duration counts for the whole chord.

    """
    def __init__(self, chord):
        self.chord = chord
        self.group = True
        self.minimumNote = None
        self.cacheMinimumNote() #the shortest note. set inplace.

    def cacheMinimumNote(self):
        work = []
        for note in self.chord.notelist:
            work.append((note.duration.completeDuration(), note))
        self.minimumNote =  min(work)[1]  #a list of tuplets (duration, note). The minimum is the one with the lowest duration, [1] is the note

    def templateDuration(self):
        """for chord addNote we need some template"""
        return self.minimumNote.duration.copy()

    def completeDuration(self):
        """Complete Musical Duration of this item."""
        return self.minimumNote.duration.completeDuration()

    def hasTuplet(self):
        """Return true if there is any tuplet in any note"""
        return any(note.duration.tuplet for note in self.chord.notelist)

    @property
    def baseDuration(self):
        return self.minimumNote.duration.baseDuration

    @property
    def genericNumber(self):
        return duration.baseDurationToTraditionalNumber[self.baseDuration]

    @property
    def tuplet(self):
        """Return the tuplet of the shortest duration.
        Does *not* use self.hasTuplet() because that returns if any
        note has tuplet. We just want the logical shortest duration,
        not even the actual absolute shortes duration.
        In other words: the smallest notehead, does that have a tuplet?"""
        return self.minimumNote.duration.tuplet


##############################################
## Items ##
##############################################


def createChordOrRest(completeDuration, pitchlist, velocityModification):
    """Return a Chord or Rest instance.
    The complete duration is the absolute duration after
    dots, tuplets etc. e.g. generated by midi in.
    Pitchlist can also be empty then it generates rests. But you
    have to explicitely set it to empty.

    This is used by the pattern generator which supports both
    rests and chords and works by absolute durations.
    """

    durationObject = Duration.createByGuessing(completeDuration)
    dynamic = Dynamic()
    dynamic.velocityModification = velocityModification
    if pitchlist: #chord
        new = Chord(firstDuration=durationObject, firstPitch=pitchlist[0])
        for pitch in pitchlist[1:]:
            new.addNote(pitch)
        for note in new.notelist:
            note.dynamic = dynamic.copy()
    else:
        new = Rest(durationObject)

    return new

class Item(object):

    pseudoDuration = Duration(0) #any item has a duration. But we don't need a new pseudo duration instance for every clef and keysig.

    def __init__(self):
        self.duration = self.pseudoDuration
        self.notelist = [] #only in Chord. Here for compatibility reasons
        self.parentBlocks = WeakSet() #this is filled and manipulated by the Block Class through copy, split, contentLink etc. this takes also care of Item.copy(). Don't look in this file for anything parentBlocks related.
        #self._secondInit(parentBlock = None) #Items don't know their parent object. They are in a mutable list which can be in several containers.
        self.lilypondParameters = { # Only use basic data types! Not all lilypond parameters are used by every item. For clarity and editing  we keep them all in one place.
            "visible" : True,
            "override" : "",
            "explicit" : False,
            }

    def _secondInit(self, parentBlock):
        """see Score._secondInit"""

    def deserializeDurationAndNotelistInPlace(self, serializedObject):
        """Part of instanceFromSerializedData.
        Any Item has a notelist and a duration, even if they are
        empty or never read.

        Needs a self, so
        the instance needs to be created. Call after
        self = cls.__new__(cls)"""
        self.parentBlocks = WeakSet() #inserted by the parent load method
        self.duration = Duration.instanceFromSerializedData(serializedObject["duration"], parentObject = self) #needs to be implemented in every childclass because we do not call Item.instanceFromSerializedData
        self.notelist = [Note.instanceFromSerializedData(note, parentObject = self) for note in serializedObject["notelist"]]
        self.lilypondParameters = serializedObject["lilypondParameters"]

    @classmethod
    def instanceFromSerializedData(cls, serializedObject, parentObject):
        """see Score.instanceFromSerializedData.
        This does not get called by a child class so you need to
        reimplement self.duration and other common parameters in
        every child class!"""
        assert cls.__name__ == serializedObject["class"]
        self = cls.__new__(cls)
        self.deserializeDurationAndNotelistInPlace(serializedObject) #creates parentBlocks
        self._secondInit(parentTrack = parentObject)
        return self

    def serialize(self):
        """Return a serialized data from this instance. Used for save, load
        and undo/redo.
        Can be called in a chain by subclasses with super().serialize

        The main difference between serialize and exportObject is that
        serialize does not compute anything. I just saves the state
        without calulating note on and off or stem directions,
        for example.
        """
        #result = super().serialize() #call this in child classes
        result = {}
        result["class"] = self.__class__.__name__
        result["duration"] =  self.duration.serialize()
        result["notelist"] = [note.serialize() for note in self.notelist]
        result["lilypondParameters"] = self.lilypondParameters
        return result

    def _exportObject(self, trackState):
        """Implement this in every child class, return a dict.

        Base class function to return a dict which is a good layout
        export basis. For example for GUI frontends. They don't have to
        parse and calculate their own values in slow pure Python then

        midiBytes is a list with patternBlob/binary data generated by
        cbox.Pattern.serialize_event(position, midibyte1 (noteon), midibyte2(pitch), midibyte3(velocity))
        Items with duration, like chords, will most like need to create
        at least two patternBlobs, one for the start (e.g. note on) and one
        for the stop (e.g. note off)
        """
        raise NotImplementedError("Implement _exportObject() for this item type!")

        #And for documentation and education here a template exportObject
        duration = 0
        return {
            "type" : "",
            "completeDuration" : duration,
            "tickindex" : trackState.tickindex - duration, #we parse the tickindex after we stepped over the item.
            "midiBytes" : [],
            "UIstring" : "", #this is for a UI, possibly a text UI, maybe for simple items of a GUI. Make it as short and unambigious as possible.
        }

    def exportObject(self, trackState):
        exportDict = {
            "id" : id(self),
            "lilypondParameters" : self.lilypondParameters,
        }

        exportDict.update(self._exportObject(trackState))
        return exportDict


    @property
    def parentTracks(self):
        #return (block.parentTrack for block in self.parentBlocks[0])
        return (block.parentTrack for block in list(self.parentBlocks)[0].linkedContentBlocksInScore())

    @property
    def parentTrackIds(self):
        return (id(tr) for tr in self.parentTracks)

    def _copy(self):
        """return an independent copy of self"""
        raise NotImplementedError("Implement _copy() for this item type!")

    def copy(self):
        new = self._copy()
        new.lilypondParameters = self.lilypondParameters.copy()
        return new

    def copyParentBlocks(self, oldItem):
        """Move the oldItems parentBlocks to the new item"""
        #self.parentBlocks = oldItem.parentBlocks #TODO. We took that out when pasting after deleting a track and recreating failed. Why do we to copy the parentBlocks when a parentBlock is added during block.insert anyway? Wild guess: we don't.

    def logicalDuration(self):
        return 0

    def deserializeInplace(self, serializedObject):
        """Change this instance values to the values in the
         serialized object. Part of the undo/redo system"""
        return

    #Empty methods that allow for apply to selection and other iterators to work with items that cannot respond to a command. e.g. stepUp on a rest.
    #Api functions must react to these function returning None. For example Undo will not register anything when no undo-function is returned by an item-method.
    #We don't use functions(self, *args) and then a bunch of function1 = function2 = function3 because this way it is easier to see what methods are available (and what arguments they need) when creating new classes from Item.

    def sharpenNoteNearPitch(self, pitch): pass
    def flattenNoteNearPitch(self, pitch): pass
    def stepUpNoteNearPitch(self, pitch, keysig): pass
    def stepDownNoteNearPitch(self, pitch, keysig): pass
    def stepUpOctaveNoteNearPitch(self, pitch): pass
    def stepDownOctaveNoteNearPitch(self, pitch): pass
    def augmentNoteNearPitch(self, pitch): pass
    def diminishNoteNearPitch(self, pitch): pass
    def toggleDotNearPitch(self, pitch): pass
    def toggleTripletNearPitch(self, pitch): pass
    def setTupletNearPitch(self, pitch, tupletListForDuration): pass
    def toggleDurationKeywordNearPitch(self, pitch, keywordConstant): pass
    def moreVelocityNearPitch(self, pitch): pass
    def lessVelocityNearPitch(self, pitch): pass
    def moreDurationNearPitch(self, pitch): pass
    def lessDurationNearPitch(self, pitch): pass
    def resetVelocityAndDurationModsNearPitch(self, pitch): pass

    def sharpen(self): pass
    def flatten(self): pass
    def augment(self): pass
    def diminish(self): pass
    def toggleDot(self): pass
    def toggleTriplet(self): pass
    def setTuplet(self, durationTupletListForEachNote): pass
    def toggleDurationKeyword(self, listOfKeywordConstants): pass
    def stepUp(self, keysigList): pass
    def stepDown(self, keysigList): pass
    def stepUpOctave(self): pass
    def stepDownOctave(self): pass
    def intervalAutomatic(self, rootPitch, targetPitch): pass
    def split(self, newparts): pass
    def addNote(self, pitch): pass
    def deleteNote(self, note): pass
    def removeNoteNearPitch(self, pitch): pass
    def moreVelocity(self): pass
    def lessVelocity(self): pass
    def moreDuration(self): pass
    def lessDuration(self): pass
    def resetVelocityAndDurationMods(self): pass

    def toggleBeam(self): pass
    def removeBeam(self): pass

    def midiRelativeChannelPlus(self): pass
    def midiRelativeChannelMinus(self): pass
    def midiRelativeChannelReset(self): pass

    def mirrorAroundCursor(self, cursorPitch): pass

    def lilypond(self, carryLilypondRanges):
        if self.lilypondParameters["override"]:
            return self.lilypondParameters["override"]
        else:
            return self._lilypond(carryLilypondRanges)

    def _lilypond(self, carryLilypondRanges):
        """called by block.lilypond(carryLilypondRanges), returns a string.
        Don't create white-spaces yourself, this is done by the structures.
        When in doubt prefer functionality and robustness over 'beautiful' lilypond syntax."""
        return ""

class TemplateItem(Item):
    """Use this with copy and paste to create a new item class"""

    def __init__(self, itemData):
        """Either init gets called, by creation during runtime, or instanceFromSerializedData.
        That means everything in init must be matched by a loading call in instanceFromSerializedData."""
        super().__init__()
        raise NotImplementedError("Implement __init__() for this item type!")
        self.itemData = itemData #matches instanceFromSerializedData and serialize
        self._secondInit(parentBlock = None) #see Item._secondInit.

    def _secondInit(self, parentBlock):
        """After creating a new item during runtime or after loading from a file this gets called.
        Put every calcuation that depends on values that could be changed by the user.
        see Item._secondInit"""
        super()._secondInit(parentBlock) #Item._secondInit

    @classmethod
    def instanceFromSerializedData(cls, serializedObject, parentObject):
        """see Score.instanceFromSerializedData"""
        raise NotImplementedError("Implement instanceFromSerializedData() for this item type!")
        assert cls.__name__ == serializedObject["class"]
        self = cls.__new__(cls)
        self.itemData = serializedObject["itemData"]
        self.deserializeDurationAndNotelistInPlace(serializedObject)
        self._secondInit(parentBlock = parentObject)
        return self

    def serialize(self):
        raise NotImplementedError("Implement serialize() for this item type!")
        result = super().serialize() #call this in child classes
        result["itemData"] = self.itemData
        return result

    def _copy(self):
        """return an independent copy of self"""
        raise NotImplementedError("Implement _copy() for this item type!")
        new = TemplateItem(self.itemData)
        return new


    def _exportObject(self, trackState):
        """Implement this in every child class, return a dict.

        Base class function to return a dict which is a good layout
        export basis. For example for GUI frontends. They don't have to
        parse and calculate their own values in slow pure Python then

        midiBytes is a list with patternBlob/binary data generated by
        cbox.Pattern.serialize_event(position, midibyte1 (noteon), midibyte2(pitch), midibyte3(velocity))
        Items with duration, like chords, will most like need to create
        at least two patternBlobs, one for the start (e.g. note on) and one
        for the stop (e.g. note off)
        """
        raise NotImplementedError("Implement _exportObject() for this item type!")

        #And for documentation and education here a template exportObject
        duration = 0
        return {
            "type" : "",
            "completeDuration" : duration,
            "tickindex" : trackState.tickindex - duration, #we parse the tickindex after we stepped over the item.
            "midiBytes" : [],
            "UIstring" : "", #this is for a UI, possibly a text UI, maybe for simple items of a GUI. Make it as short and unambigious as possible.
        }

    def _lilypond(self, carryLilypondRanges):
        """called by block.lilypond(carryLilypondRanges), returns a string.
        Don't create white-spaces yourself, this is done by the structures.
        When in doubt prefer functionality and robustness over 'beautiful' lilypond syntax."""
        return ""

class Chord(Item):
    """Duration and firstPitch are for the first note.
    You must only modify the notelist through Chord methods. The chord methods keep
    the list sorted and also keep track what is the shortest and longest note, and other meta-data.
    Replacing .notelist or using list methods like .append directly will break the whole program.
    Especially the lowest and shortest notes are used multiple times on every item export!
    """

    def __init__(self, firstDuration, firstPitch):
        super().__init__()
        firstNote = Note(self, firstDuration, firstPitch)
        self.notelist = [firstNote] #from lowest to highest
        self._beamGroup = False # Works a bit like legato slurs, only this is a property of a note, not an item.   self.beamGroup is a @property that self-corrects against errors.
        self._secondInit(parentBlock = None) #see Item._secondInit.
        self.midiChannelOffset = 0 #from -15 to 15. In reality much less. Expected values are -3 to +3

    def _secondInit(self, parentBlock):
        """see Item._secondInit"""
        super()._secondInit(parentBlock) #Item._secondInit
        self.durationGroup = DurationGroup(self) #durationGroup is a dynamic data type and therefore does not need serialize. The serialized durations are in the notelist.
        self._cachedClefForLedgerLines = None

    @classmethod
    def instanceFromSerializedData(cls, serializedObject, parentObject):
        """see Score.instanceFromSerializedData"""
        assert cls.__name__ == serializedObject["class"]
        self = cls.__new__(cls)
        self.deserializeDurationAndNotelistInPlace(serializedObject)
        self._beamGroup = bool(serializedObject["beamGroup"])
        self.midiChannelOffset = serializedObject["midiChannelOffset"]
        self._secondInit(parentBlock = parentObject)
        return self

    def serialize(self):
        result = super().serialize() #call this in child classes
        result["beamGroup"] = self._beamGroup
        result["midiChannelOffset"] = self.midiChannelOffset
        #durationGroup is a dynamic data type and gets its values from its parent chord: self.
        return result

    def logicalDuration(self):
        """Return the logical duration that is used to calculate
        the space and subsequent position of items."""
        #TODO: this gets called pretty often. 6 times per note insert on a previously empty track. Why?!
        return self.durationGroup.completeDuration()

    def _copy(self):
        new = Chord(Duration(0), 0)

        new.copyParentBlocks(self)
        new._beamGroup = self._beamGroup
        new.notelist = []
        for note in self.notelist:
            new.notelist.append(note.copy(new))
        new.durationGroup.cacheMinimumNote()
        return new

    def pitchlist(self):
        return [note.pitch for note in self.notelist] #needs to be subscriptable

    def whitePitchlist(self):
        result = []
        for note in self.notelist:
            result.append(pitchmath.toWhite[note.pitch])
        return result

    def _setNotelist(self, notelist):
        oldNotelist = self.notelist.copy()
        self.notelist = notelist
        self.durationGroup.cacheMinimumNote()
        return lambda: self._setNotelist(oldNotelist)

    def replaceNoteList(self, notelist):
        """Wants an actual notelist, not pitches.
        This is its own function because the private _setNotelist was created years before this here
        """
        return self._setNotelist(notelist)

    def addNote(self, pitch):
        """The notelist gets sorted after insert.
        Each white note is only allowed once in each chord.
        That means gis and ges are not allowed in the same chord.
        And not two times the same note.

        We use the template duration from the group. That means the new
        note has the same main-properties as the existing notes.
        """
        note = Note(self, self.durationGroup.templateDuration(), pitch)
        if pitchmath.toWhite[pitch] in self.whitePitchlist():
            return False #only one of each base note in a chord. no cis, ces and c in the same chord.
        else:
            oldNotelist = self.notelist.copy() #only copies the order, not the items itself. Which is what we need.
            self.notelist.append(note)
            self.notelist.sort()
            self.durationGroup.cacheMinimumNote()
            return lambda: self._setNotelist(oldNotelist)

    def deleteNote(self, note):
        """Delete the exact note, an instance.
        No search for the pitch or crap like "highest first".
        Notelist remains sorted after delete."""
        if len(self.notelist) > 1:
            oldNotelist = self.notelist.copy()
            self.notelist.remove(note)
            self.durationGroup.cacheMinimumNote()
            #No need for sorting.
            return lambda: self._setNotelist(oldNotelist)

    def removeNoteNearPitch(self, pitch):
        note = self.getNearestNote(pitch)
        return self.deleteNote(note)

    def getNearestNote(self, pitch):
        """Return the note which is nearest.
        The behaviour if two notes of the same pitch are in this chord
        is undefined. Whatever cython/python finds first will get
        returned. It really shouldn't matter."""
        listOfTuples = []
        for note in self.notelist:
            listOfTuples.append((abs(pitch - note.pitch), note))
        note = min(listOfTuples)[1] #[1] of the tuple (pitch,noteinstance)
        return note

    #def Note getHighestNote(self):
    #def Note getLowestNotes(self):


    #Direct functions. Do not need a context, don't need a state.
    #Meant for undo.

    def _setNotePitch(self, note, targetPitch):
        """A direct setter for an exact note,
        not just near a pitch.
        Mostly for undo"""
        oldValueForUndo = note.pitch
        note.pitch = targetPitch
        self._cachedClefForLedgerLines = None
        return lambda: self._setNotePitch(note, oldValueForUndo)

    def _setNoteDuration(self, note, durationInstance):
        """see setNotePitch."""
        assert not note.duration is durationInstance
        oldValueForUndo = note.duration.copy()
        note.duration = durationInstance
        self.durationGroup.cacheMinimumNote()
        return lambda: self._setNoteDuration(note, oldValueForUndo)

    def _setNoteDynamic(self, note, dynamicInstance):
        """see _setNoteDuration."""
        assert not note.dynamic is dynamicInstance
        oldValueForUndo = note.dynamic.copy()
        note.dynamic = dynamicInstance
        return lambda: self._setNoteDynamic(note, oldValueForUndo)

    def _setNoteDynamicAndDuration(self, note, durationInstance, dynamicInstance):
        assert not note.duration is durationInstance
        oldDurationValueForUndo = note.duration.copy()
        note.duration = durationInstance
        self._cachedClefForLedgerLines = None

        assert not note.dynamic is dynamicInstance
        oldDynamicValueForUndo = note.dynamic.copy()
        note.dynamic = dynamicInstance

        return lambda: self._setNoteDynamicAndDuration(note, oldDurationValueForUndo, oldDynamicValueForUndo)


    #Nearest Note:
    #Pitch modifications
    def sharpenNoteNearPitch(self, pitch):
        note = self.getNearestNote(pitch)
        oldValueForUndo = note.pitch
        self.notelist.sort()
        note.sharpen()
        return lambda: self._setNotePitch(note, oldValueForUndo)

    def flattenNoteNearPitch(self, pitch):
        note = self.getNearestNote(pitch)
        oldValueForUndo = note.pitch
        self.notelist.sort()
        note.flatten()
        return lambda: self._setNotePitch(note, oldValueForUndo)

    def stepUpNoteNearPitch(self, pitch, keysig):
        note = self.getNearestNote(pitch)
        oldValueForUndo = note.pitch
        note.stepUpInScale(keysig)
        self.notelist.sort()
        self._cachedClefForLedgerLines = None
        return lambda: self._setNotePitch(note, oldValueForUndo)

    def stepDownNoteNearPitch(self, pitch, keysig):
        note = self.getNearestNote(pitch)
        oldValueForUndo = note.pitch
        note.stepDownInScale(keysig)
        self.notelist.sort()
        self._cachedClefForLedgerLines = None
        return lambda: self._setNotePitch(note, oldValueForUndo)

    def stepUpOctaveNoteNearPitch(self, pitch):
        note = self.getNearestNote(pitch)
        oldValueForUndo = note.pitch
        note.octaveUp()
        self.notelist.sort()
        self._cachedClefForLedgerLines = None
        return lambda: self._setNotePitch(note, oldValueForUndo)

    def stepDownOctaveNoteNearPitch(self, pitch):
        note = self.getNearestNote(pitch)
        oldValueForUndo = note.pitch
        note.octaveDown()
        self.notelist.sort()
        self._cachedClefForLedgerLines = None
        return lambda: self._setNotePitch(note, oldValueForUndo)

    #Duration modifications
    def augmentNoteNearPitch(self, pitch):
        note = self.getNearestNote(pitch)
        oldValueForUndo = note.duration.copy()
        note.duration.augment()
        self.durationGroup.cacheMinimumNote()
        return lambda: self._setNoteDuration(note, oldValueForUndo)

    def diminishNoteNearPitch(self, pitch):
        note = self.getNearestNote(pitch)
        oldValueForUndo = note.duration.copy()
        note.duration.diminish()
        self.durationGroup.cacheMinimumNote()
        return lambda: self._setNoteDuration(note, oldValueForUndo)

    def toggleDotNearPitch(self, pitch):
        note = self.getNearestNote(pitch)
        oldValueForUndo = note.duration.copy()
        note.duration.toggleDot()
        self.durationGroup.cacheMinimumNote()
        return lambda: self._setNoteDuration(note, oldValueForUndo)

    def toggleTripletNearPitch(self, pitch):
        note = self.getNearestNote(pitch)
        oldValueForUndo = note.duration.copy()
        note.duration.toggleTriplet()
        self.durationGroup.cacheMinimumNote()
        return lambda: self._setNoteDuration(note, oldValueForUndo)

    def setTupletNearPitch(self, pitch, tupletForDuration):
        note = self.getNearestNote(pitch)
        oldValueForUndo = note.duration.copy()
        note.duration.tuplet = tupletForDuration
        self.durationGroup.cacheMinimumNote()
        return lambda: self._setNoteDuration(note, oldValueForUndo)

    def toggleDurationKeywordNearPitch(self, pitch, keywordConstant):
        note = self.getNearestNote(pitch)
        oldValueForUndo = note.duration.copy()
        note.duration.toggleDurationKeyword(keywordConstant)
        return lambda: self._setNoteDuration(note, oldValueForUndo)

    def moreDurationNearPitch(self, pitch):
        """If you change the function don't forget the chord version"""
        note = self.getNearestNote(pitch)
        oldValueForUndo = note.duration.copy()
        note.duration.shiftEnd += D64
        if not note.duration.shiftEnd: #to avoid weird behaviour where you add duration and the note off actually jumps back because shiftEnd got 0 and the default note off kicked in (which is less than 0). You can only reset to truly "0" by using the reset command.
            note.duration.shiftEnd += D64
        return lambda: self._setNoteDuration(note, oldValueForUndo)

    def lessDurationNearPitch(self, pitch):
        note = self.getNearestNote(pitch)
        oldValueForUndo = note.duration.copy()

        if not note.duration.shiftEnd: #only happens when never touched by human hands. prevents a jump in the wrong direction because the user expects a change from the current value, not from 0.
            note.duration.shiftEnd = note._cachedExportObject["rightModInTicks"]

        note.duration.shiftEnd -= D64
        if not note.duration.shiftEnd: #see moreDurationNearPitch. never return to 0 to let the default keyword kick in again.
            note.duration.shiftEnd -= D64
        return lambda: self._setNoteDuration(note, oldValueForUndo)

    def moreVelocityNearPitch(self, pitch):
        note = self.getNearestNote(pitch)
        oldValueForUndo = note.dynamic.copy()
        note.dynamic.velocityModification += 1
        return lambda: self._setNoteDynamic(note, oldValueForUndo)

    def lessVelocityNearPitch(self, pitch):
        note = self.getNearestNote(pitch)
        oldValueForUndo = note.dynamic.copy()
        note.dynamic.velocityModification -= 1
        return lambda: self._setNoteDynamic(note, oldValueForUndo)

    def resetVelocityAndDurationModsNearPitch(self, pitch):
        note = self.getNearestNote(pitch)
        if note.dynamic.velocityModification == 0 and  note.duration.shiftStart == 0 and note.duration.shiftEnd == 0:
            return None
        oldDurationValueForUndo = note.duration.copy()
        oldDynamicValueForUndo = note.dynamic.copy()
        note.dynamic.velocityModification = 0
        note.duration.shiftStart = 0
        note.duration.shiftEnd = 0
        return lambda: self._setNoteDynamicAndDuration(note, oldDurationValueForUndo, oldDynamicValueForUndo)

    #Whole Chord/All Notes. Typically used with selections
    #REMEMBER that function-parameters are lists because it is possible
    #to give one parameter for each note in the chord.

    def _createParameterGenerator(self, notelist, parameterForEachNote):
        """Check and choose if there is a correct parameter list with
        enough values for each note in the notelist or choose the only
        given parameter for each note.

        Most of the time we want all parameters set to the same value
        so we receive only one argument in the listForEachNote.
        However, Undo from the api really sends us the original values,
        which could be a different one for each note.

        So we expect a list with len()==1 or equal length to notelist.

        If we only have one note it does not matter at all."""

        if len(parameterForEachNote) == 1:
            gen = EndlessGenerator(parameterForEachNote[0])
        else:
            assert len(self.notelist) == len(parameterForEachNote)
            gen = (param for param in parameterForEachNote)
        return gen

    def _setDurationlist(self, durationlist):
        """see _setPitchlist. Only for durations.
        The durations are standalone versions, most likely created
        through duration.copy() (in case of undo).

        Since this is never called twice (score.selectedItems() filters
        out content-links) on content-linked data no links
        get broken."""
        assert len(self.notelist) == len(durationlist)
        oldValues = []
        for note, newDuration in zip(self.notelist, durationlist):
            oldValues.append(note.duration.copy()) #no content-links get broken this way because score.selectedItems() only gets one instance of every item, no matter how many links exist.
            note.duration = newDuration
        self.durationGroup.cacheMinimumNote()
        return lambda: self._setDurationlist(oldValues)

    def _setDynamiclist(self, dynamiclist):
        """see _setDurationslist. Only for dynamics."""
        assert len(self.notelist) == len(dynamiclist)
        oldValues = []
        for note, newDynamic in zip(self.notelist, dynamiclist):
            oldValues.append(note.dynamic.copy()) #no content-links get broken this way because score.selectedItems() only gets one instance of every item, no matter how many links exist.
            note.dynamic = newDynamic
        return lambda: self._setDynamiclist(oldValues)

    def _setDynamicAndDuration(self, omnilist):
        """omnilist is a list of tuples (duration, dynamic)
        Function written for clearVelocityAndDurationMods"""
        assert len(self.notelist) == len(omnilist)
        oldValues = []
        for note, (newDuration, newDynamic) in zip(self.notelist, omnilist):
            oldValues.append((note.duration.copy(), note.dynamic.copy())) #no content-links get broken this way because score.selectedItems() only gets one instance of every item, no matter how many links exist.
            note.dynamic = newDynamic
            note.duration = newDuration
        return lambda: self._setDynamicAndDuration(oldValues)

    def _setPitchlist(self, pitchlist):
        """This is for undo. We rely on the fact that undo is linear
        and the pitches we set match exactly the order of notes we
        already have.
        We don't need tests for valid values or a matching number of
        notes"""
        assert len(self.notelist) == len(pitchlist)
        oldValues = []
        for note, newPitch in zip(self.notelist, pitchlist):
            oldValues.append(note.pitch)
            note.pitch = newPitch
        self._cachedClefForLedgerLines = None
        return lambda: self._setPitchlist(oldValues)


    def sharpen(self):
        oldValues = []
        for note in self.notelist:
            oldValues.append(note.pitch)
            note.sharpen()
        return lambda: self._setPitchlist(oldValues)

    def flatten(self):
        oldValues = []
        for note in self.notelist:
            oldValues.append(note.pitch)
            note.flatten()
        return lambda: self._setPitchlist(oldValues)

    def split(self, newparts, _existingCopies = []):
        """
        This is only for chords.
        Technically this works with rests as well.
        However, there is little musical meaning in splitted rests.
        And it does not make sense for further editing.

        Undo works by remembering which original note spawned which
        child notes.

        There is an optional parameter _existingCopies. This is used
        by the internal undo/redo system. The api does not need to call
        it. It will be used instead of making new copies.
        Reason:
        If you split a note and split one of the newly spawned notes
        again it works fine. Two times Undo will delete those notes and
        keep only the original note with its original duration.
        If you now do redo with a normal split it will work the first
        time and split the original note again. But the resulting notes
        are NOT the ones from the first round. So we can't find use
        them in our undo philosophy, which works with specific instances.

        In other words: only redo needs _existingCopies.
        """

        assert newparts >= 2

        #First change the current duration in place so we only have to make copies later.
        oldValues = []
        for note in self.notelist:
            oldValues.append(note.duration.copy()) #we don't actually need a copy since we create a new one. But better safe than sorry. This matches also the behaviour of the other duration change functions.
            complDur =  note.duration.completeDuration()

            #newTicks = complDur / newparts
            note.duration = Duration.createByGuessing(Fraction(complDur, newparts))

        #Now we have one note with the duration the user wants to have. Make n-1 copies-
        spawnedNotes = []
        for block in self.parentBlocks: #weakrefs are not iterators and not iterable. So next() does not work and [x] does not work. for loop has to do.
            currentIndexInBlock = block.data.index(self)
            for i in range(newparts-1):
                if _existingCopies: #this is redo.
                    c = _existingCopies[i]
                else:
                    c = self.copy()

                spawnedNotes.append(c)
                block.data.insert(currentIndexInBlock+1, c) #add each new item after the original one.
            break #we only need one block since the data is the same in all blocks.

        def _undoSplit():
            #Reverse order. First remove the spawned notes
            for spawn in spawnedNotes:
                block.data.remove(spawn)

            #Then restore the original note durations
            self._setDurationlist(oldValues)

            return lambda: self.split(newparts, _existingCopies = spawnedNotes)

        self.durationGroup.cacheMinimumNote()
        return _undoSplit

    def augment(self):
        oldValues = []
        for note in self.notelist:
            oldValues.append(note.duration.copy()) #see _setDurationlist
            note.duration.augment()
        self.durationGroup.cacheMinimumNote()
        return lambda: self._setDurationlist(oldValues)

    def diminish(self):
        oldValues = []
        for note in self.notelist:
            oldValues.append(note.duration.copy()) #see _setDurationlist
            note.duration.diminish()
        self.durationGroup.cacheMinimumNote()
        return lambda: self._setDurationlist(oldValues)

    def _setRelativeMidiChannel(self, value):
        oldValue = self.midiChannelOffset
        self.midiChannelOffset = value
        return lambda: self._setRelativeMidiChannel(oldValue)

    def midiRelativeChannelPlus(self):
        return self._setRelativeMidiChannel(self.midiChannelOffset+1)

    def midiRelativeChannelMinus(self):
        return self._setRelativeMidiChannel(self.midiChannelOffset-1)

    def midiRelativeChannelReset(self):
        return self._setRelativeMidiChannel(0)

    def toggleDot(self):
        oldValues = []
        for note in self.notelist:
            oldValues.append(note.duration.copy()) #see _setDurationlist
            note.duration.toggleDot()
        self.durationGroup.cacheMinimumNote()
        return lambda: self._setDurationlist(oldValues)

    def toggleTriplet(self):
        oldValues = []
        for note in self.notelist:
            oldValues.append(note.duration.copy()) #see _setDurationlist
            note.duration.toggleTriplet()
        self.durationGroup.cacheMinimumNote()
        return lambda: self._setDurationlist(oldValues)

    def setTuplet(self, durationTupletForEachNote:list):
        """
        parameter format:
        [
            (2,3),        #note 1
            (4,5),        #note 2
            (3,4),        #note 3
        ]
        """
        gen = self._createParameterGenerator(self.notelist, durationTupletForEachNote)
        oldValues = []
        for note in self.notelist:
            oldValues.append(note.duration.copy()) #see _setDurationlist
            note.duration.tuplet = next(gen)
        self.durationGroup.cacheMinimumNote()
        return lambda: self._setDurationlist(oldValues)

    def toggleDurationKeyword(self, listOfKeywordConstants):
        gen = self._createParameterGenerator(self.notelist, listOfKeywordConstants)
        oldValues = []
        for note in self.notelist:
            oldValues.append(note.duration.copy()) #see _setDurationlist
            note.duration.toggleDurationKeyword(next(gen))
        return lambda: self._setDurationlist(oldValues)

    def stepUp(self, keysigList):
        """We get a list as parameter but we already know it is not
        possible to get more than one keysig for a chord. We play along
        for compatibility"""
        gen = self._createParameterGenerator(self.notelist, keysigList)
        oldValues = []
        for note in self.notelist:
            oldValues.append(note.pitch)
            note.stepUpInScale(next(gen))
        self._cachedClefForLedgerLines = None
        return lambda: self._setPitchlist(oldValues)

    def stepDown(self, keysigList):
        gen = self._createParameterGenerator(self.notelist, keysigList)
        oldValues = []
        for note in self.notelist:
            oldValues.append(note.pitch)
            note.stepDownInScale(next(gen))
        self._cachedClefForLedgerLines = None
        return lambda: self._setPitchlist(oldValues)

    def stepUpOctave(self):
        oldValues = []
        for note in self.notelist:
            oldValues.append(note.pitch)
            note.octaveUp()
        self._cachedClefForLedgerLines = None
        return lambda: self._setPitchlist(oldValues)

    def stepDownOctave(self):
        oldValues = []
        for note in self.notelist:
            oldValues.append(note.pitch)
            note.octaveDown()
        self._cachedClefForLedgerLines = None
        return lambda: self._setPitchlist(oldValues)

    def intervalAutomatic(self, rootPitch, targetPitch):
        oldValues = []
        for note in self.notelist:
            oldValues.append(note.pitch)
            note.intervalAutomatic(rootPitch, targetPitch) #TODO: possible optimisation is to calculate the interval once instead of every time.
        self._cachedClefForLedgerLines = None
        return lambda: self._setPitchlist(oldValues)

    def mirrorAroundCursor(self, cursorPitch):
        oldValues = []
        for note in self.notelist:
            oldValues.append(note.pitch)
            note.mirrorAroundCursor(cursorPitch)
        self.notelist.sort()
        self._cachedClefForLedgerLines = None
        return lambda: self._setPitchlist(oldValues)


    def moreDuration(self):
        oldValues = []
        for note in self.notelist:
            oldValues.append(note.duration.copy()) #see _setDurationlist
            note.duration.shiftEnd += D64
            if not note.duration.shiftEnd: #see moreDurationNearPitch
                note.duration.shiftEnd += D64
        return lambda: self._setDurationlist(oldValues)

    def lessDuration(self):
        oldValues = []
        for note in self.notelist:
            oldValues.append(note.duration.copy()) #see _setDurationlist
            if not note.duration.shiftEnd: #only happens when never touched by human hands. prevents a jump in the wrong direction because the user expects a change from the current value, not from 0.
                note.duration.shiftEnd = note._cachedExportObject["rightModInTicks"]
            note.duration.shiftEnd -= D64
            if not note.duration.shiftEnd: #see moreDurationNearPitch
                note.duration.shiftEnd -= D64
        return lambda: self._setDurationlist(oldValues)

    def moreVelocity(self):
        oldValues = []
        for note in self.notelist:
            oldValues.append(note.dynamic.copy())
            note.dynamic.velocityModification += 1
        return lambda: self._setDynamiclist(oldValues)

    def lessVelocity(self):
        oldValues = []
        for note in self.notelist:
            oldValues.append(note.dynamic.copy())
            note.dynamic.velocityModification -= 1
        return lambda: self._setDynamiclist(oldValues)

    def resetVelocityAndDurationMods(self):
        oldValues = []
        unmodified = []
        for note in self.notelist:
            oldValues.append((note.duration.copy(), note.dynamic.copy())) #no content-links get broken this way because score.selectedItems() only gets one instance of every item, no matter how many links exist.
            unmodified.append(note.dynamic.velocityModification == 0 and  note.duration.shiftStart == 0 and note.duration.shiftEnd == 0)
            note.dynamic.velocityModification = 0
            note.duration.shiftStart = 0
            note.duration.shiftEnd = 0
        if all(unmodified):
            return None
        else:
            return lambda: self._setDynamicAndDuration(oldValues)

    @property
    def beamGroup(self):
        """toggleBeam only works on D8 or smaller. But a D8 can get augmented and then still
        have the beam flag. So every time the beamStatus gets accessed we take this as a chance
        to test if this Chord still has the rights for a beam instruction. This happens at least
        every Gui or Midi static export. So very very often and always when it matters.
        TODO: test performance and maybe put this test in the duration itself"""
        if self.durationGroup.baseDuration > D8:
            self._beamGroup = False
        return self._beamGroup

    def _setBeam(self, beamBool):
        """for undo"""
        oldValue = self._beamGroup #it is not guaranteed that this is the opposite of beamBool. It doesn't need do be.
        self._beamGroup = beamBool
        return lambda: self._setBeam(oldValue)

    def toggleBeam(self):
        if self.durationGroup.baseDuration > D8:
            assert self._beamGroup == False
            return None
        else:
            undoFunction = self._setBeam(not self._beamGroup)
            return undoFunction

    def removeBeam(self):
        return self._setBeam(False) #return undo function

    #Export functions

    def calcFlag(self):
        """What classical flag would be used
        to express baseDuration"""
        if self.durationGroup.genericNumber <= 4: #no flag. Quarter, Half or bigger duration.
            return 0
        else:
            return self.durationGroup.genericNumber

    def calculateLedgerLinesForLayoutExport(self, clef):
        if self._cachedClefForLedgerLines and clef == self._cachedClefForLedgerLines:
            return self._cachedLedgerLines
        else:
            self._cachedClefForLedgerLines = clef
        lowestNote = self.notelist[0]
        highestNote = self.notelist[-1]

        below = lowestNote.asDotOnLine(clef)
        above = highestNote.asDotOnLine(clef)

        if below >= 6:
            below = int(below/2) - 2
        else:
            below = 0
        if above <= -6:
            above = int(abs(above)/2) - 2
        else:
            above = 0

        self._cachedLedgerLines = (below, above)
        return (below, above)

    def _exportObject(self, trackState):
        clef = trackState.clef()
        dotOnLineList = []
        for note in self.notelist:
            dotOnLineList.append(note.asDotOnLine(clef))

        highestPitchAsDotOnLine = min(dotOnLineList) #it is min because the higher pitch go in the minus direction. Qt Convention. Think Rows/Columns of a table/pixels on screen
        lowestPitchAsDotOnLine = max(dotOnLineList) #it is max because the higher pitch go in the minus direction. Qt Convention. Think Rows/Columns of a table/pixels on screen

        baseDur = self.durationGroup.baseDuration #duration is a DurationGroup so we get the minimmal note value from the notelist
        dur = self.logicalDuration() #duration is a DurationGroup so we get the minimmal note value from the notelist

        #Stems. in staffline coordinates (dots on lines): [starting point, length, 1|-1] 1 stem is on the right or -1 left side of the note.
        if baseDur < D1:
            if trackState.track.double and len(self.notelist) == 1 and 7 <= highestPitchAsDotOnLine <= 11 : #The break is between the h and c' (treble-clef)
                forceDownStem = True
            else:
                forceDownStem = False

            x = abs(highestPitchAsDotOnLine - lowestPitchAsDotOnLine) #difference between the two numbers
            if (not forceDownStem) and sum(dotOnLineList) >= 0: #stem goes up.
                stem = (lowestPitchAsDotOnLine - 1, -1 * (x+5), 1)   #-1 excludes the note height/gap itself
            else: #stem goes down
                stem = (highestPitchAsDotOnLine + 1, x+5, -1)  #+1 excludes the note height/gap itself
        else:
            stem = tuple()

        flagResult = self.calcFlag()
        if flagResult:
            assert stem
            flag = flagResult * stem[2] #1 or -1 for up or down.
        else:
            flag = 0

        #Calfbox
        midiBytesList = []
        exportNoteList = []
        logicalStartingTick = trackState.tickindex - dur #minus duration because we are RIGHT of an item after parsing it in structures.staticRepresentation
        for note in self.notelist:
            velocity = note.dynamic.velocity(trackState)
            midipitch = pitchmath.toMidi[note.pitch]
            onOffset, offOffset = note.duration.noteOnAndOff(trackState, note.duration.completeDuration())

            if note.pitch in trackState.EXPORTtiedNoteExportObjectsWaitingForClosing: #this is the last or the middle in a tied note sequence (but not the first).
                exportNoteList.append(note.exportObject(trackState))
                if not note.pitch in trackState.EXPORTtiedNoteExportObjectsWaitingForClosing:  #AGAIN! if this changed after exportObject it means this was the last note in a tied sequence
                    #Export the missing note off (see below in this loop)
                    midiBytesList.append(cbox.Pattern.serialize_event(logicalStartingTick + offOffset - 1, 0x80+pitchmath.midiChannelLimiter(trackState.midiChannel()+self.midiChannelOffset), pitchmath.midiPitchLimiter(midipitch, trackState.midiTranspose), velocity)) #-1 to create a small logical gap. This is nothing compared to our tick value dimensions, but it is enough for the midi protocol to treat two notes as separate ones. Imporant to say that this does NOT affect the next note on. This will be mathematically correct anyway.
                continue

            else: #this is a real note with its own note-on. Set it.
                midiBytesList.append(cbox.Pattern.serialize_event(logicalStartingTick + onOffset, 0x90+pitchmath.midiChannelLimiter(trackState.midiChannel()+self.midiChannelOffset), pitchmath.midiPitchLimiter(midipitch, trackState.midiTranspose), velocity))
                if not note.duration.durationKeyword == D_TIE: #First tied note in a row: no note off.  these are set by the last in a sequence tied note (see above)
                    midiBytesList.append(cbox.Pattern.serialize_event(logicalStartingTick + offOffset - 1, 0x80+pitchmath.midiChannelLimiter(trackState.midiChannel()+self.midiChannelOffset), pitchmath.midiPitchLimiter(midipitch, trackState.midiTranspose), velocity)) #-1 to create a small logical gap. This is nothing compared to our tick value dimensions, but it is enough for the midi protocol to treat two notes as separate ones. Imporant to say that this does NOT affect the next note on. This will be mathematically correct anyway.
                #Export Dict needs to be the last step because ties depend on the first/notFirst flags not set.
                exportNoteList.append(note.exportObject(trackState))

        #Ledger Lines
        ledgerLines = self.calculateLedgerLinesForLayoutExport(clef)
        if trackState.track.double and not 6 in dotOnLineList and ledgerLines[0] <= 6 : #The "middle" Ledger line in a double stuff is not needed if there is no note on it.
            ledgerLines = (ledgerLines[0] -6, ledgerLines[1])


        assert dur == int(dur)
        assert trackState.tickindex == int(trackState.tickindex)
        return {
            "type" : "Chord",
            "notelist" : exportNoteList,  #a list of dicts with notehead paramters like accidentals, noteheads and dots on lines.
            "dotOnLineList" : dotOnLineList,
            "highestPitchAsDotOnLine" : highestPitchAsDotOnLine,
            "lowestPitchAsDotOnLine" : lowestPitchAsDotOnLine,
            "completeDuration" : dur,
            "baseDuration" : baseDur, #this is from a durationGroup, so we get the minimal one. Duplicated in NoteList but handy for calculating beams.
            "ledgerLines" : ledgerLines,
            "tickindex" : trackState.tickindex - dur, #because we parse the tickindex after we stepped over the item.
            "stem" : stem, #stem means in staffline coordinates (dots on lines): [starting point, length, 1|-1] 1 stem is on the right or -1 left side of the note.
            "flag" : flag, #0 os no flag, 8 is eighth flag upwards, -8 is downwards. 16 is 16th note etc.
            "beamGroup" : self.beamGroup, #bool
            "midiBytes" : midiBytesList,
            "midiChannelOffset" : self.midiChannelOffset,
            "beam" : tuple(), #decided later in track export. Has the same structure as a stem.
            }

    def _lilypond(self, carryLilypondRanges):
        """Called by block.lilypond(carryLilypondRanges), returns a string.
        See Item.lilypond for the general docstring."""

        def _createLilypondTuplet(duration, carryLilypondRanges:dict)->str:
            pre = ""
            # \tuplet 3/2 {   }
            if duration.tuplet:  #item has a tuplet...
                if "tuplet" in carryLilypondRanges: #... and is part of a group (2nd or more)
                    if duration.tuplet ==  carryLilypondRanges["tuplet"]:
                        pass #this is nothing and the entire reason why we check for group membership. Tuplet-members in ly are just inside a {...} music section.
                    else: #it just happened that two different tuplet types followed next to each other. end one, start a new tuplet group.
                        carryLilypondRanges["tuplet"] = duration.tuplet
                        pre = f" }}  \\tuplet {duration.tuplet[1]}/{duration.tuplet[0]} {{ "
                else: # ... and is the first of a group
                    carryLilypondRanges["tuplet"] = duration.tuplet
                    pre = f"\\tuplet {duration.tuplet[1]}/{duration.tuplet[0]} {{ "
            else:
                if "tuplet" in carryLilypondRanges: #... and the previous item was a tuplet. Group has ended.
                    pre = " } " #hopefully this just ends the tuplet :) . Pre is correct, not post. Because we end the tuplet of the previous item.
                    del carryLilypondRanges["tuplet"]
                else: #... and the previous item was also not a tuplet. Most common case.
                    pass
            return pre

        #Create lilypond durations
        pitches, durations = zip(*(note.lilypond(carryLilypondRanges) for note in self.notelist))
        onlyOneDuration = durations.count(durations[0]) == len(durations)

        if onlyOneDuration:
            tupletSubstring = _createLilypondTuplet(self.notelist[0].duration, carryLilypondRanges)
            if len(pitches) == 1:
                # The most common case:  Just a single note
                return tupletSubstring +  pitches[0] + durations[0]   #this is a string.
            else:
                # A simple chord with mulitple pitches but just one duration
                return tupletSubstring +  "<" + " ".join(pitches) + ">" + durations[0]
        else:
            #TODO: Cache this

            # < \\ { <g''>4 } \\ { <e'' a'>2 }  >>
            #Here is how it's done: We group the same duration together and create normal chords which are placed each in its own automatic voice through the lilypond syntax << { <chord> } \\ { <chord> } \\ etc. >>
            #Lilypond default is to take the longest note as overall duration. However, Laborejo uses the shortest duration as logical duration. So we scale all other to logical shortest duration
            #See http://lilypond.org/doc/v2.18/Documentation/notation/writing-rhythms#scaling-durations
            table = {}
            for note in self.notelist:
                pitch, duration = note.lilypond(carryLilypondRanges)
                ticks = note.duration.completeDuration()
                if not (ticks, duration) in table:
                    table[(ticks, duration)] = list()
                table[(ticks, duration)].append(pitch)

            substrings = []
            minimumTicksInChord = self.durationGroup.completeDuration()
            for (ticks, lilydur), lst in table.items():
                #factor = Fraction(ticks, minimumDurInChord).limit_denominator(100000) #protects 6 or 7 decimal positions
                #dur = lilydur + " * {}/{}".format(factor.denominator, factor.numerator)  #lilypond requires the 1/x syntax
                assert int(ticks) == ticks, ticks
                assert int(minimumTicksInChord) == minimumTicksInChord, minimumTicksInChord
                assert minimumTicksInChord <= ticks
                if lilydur.endswith("~"):
                    lilydur = lilydur[:-1] #without ~ #TODO: not supported yet.
                dur = lilydur + " * {}/{}".format(int(minimumTicksInChord), int(ticks))  #lilypond requires the x/y syntax, even if it is 1/y #TODO: this is very ugly
                substrings.append("\\\\ { <" + " ".join(lst) + ">" + dur + "  }" )

            tupletSubstring = _createLilypondTuplet(self.durationGroup, carryLilypondRanges)
            result = tupletSubstring + "<<" + " ".join(substrings) + ">>"
            return result

class Rest(Item):
    def __init__(self, duration):
        super().__init__()
        self.duration = duration
        self._secondInit(parentBlock = None) #see Item._secondInit.

    def _secondInit(self, parentBlock):
        """see Item._secondInit"""
        super()._secondInit(parentBlock) #Item._secondInit

    @property
    def durationGroup(self):
        """For compatibility with Chords"""
        return self.duration


    @classmethod
    def instanceFromSerializedData(cls, serializedObject, parentObject):
        """see Score.instanceFromSerializedData"""
        assert cls.__name__ == serializedObject["class"]
        self = cls.__new__(cls)
        self.deserializeDurationAndNotelistInPlace(serializedObject)
        self.lilypondParameters = serializedObject["lilypondParameters"]
        self._secondInit(parentBlock = parentObject)
        return self

    def _copy(self):
        new = Rest(self.duration.copy())
        new.copyParentBlocks(self)
        new.lilypondParameters = self.lilypondParameters.copy()
        return new

    def logicalDuration(self):
        """Return the logical duration that is used to calculate
        the space and subsequent position of items.
        Does include the base, dots and tuplets but not modifications for finetuning or phrasing."""
        return self.duration.completeDuration()

    # Commands issued by the user. Support undo.
    # They get the same parameters, like pitch, as a Chord. This is only for compatibility

    def _setDuration(self, newDuration):
        """A setter which supports undo"""
        assert type(newDuration) is Duration
        oldValue = self.duration.copy()
        self.duration = newDuration
        return lambda: self._setDuration(oldValue)

    def augmentNoteNearPitch(self, pitch = None):
        oldValue = self.duration.copy()
        self.duration.augment()
        return lambda: self._setDuration(oldValue)

    def diminishNoteNearPitch(self, pitch = None):
        oldValue = self.duration.copy()
        self.duration.diminish()
        return lambda:  self._setDuration(oldValue)

    def toggleDotNearPitch(self, pitch = None):
        oldValue = self.duration.copy()
        self.duration.toggleDot()
        return lambda:  self._setDuration(oldValue)

    def toggleTripletNearPitch(self, pitch = None):
        oldValue = self.duration.copy()
        self.duration.toggleTriplet()
        return lambda:  self._setDuration(oldValue)

    def setTupletNearPitch(self, pitch, tuplet):
        oldValue = self.duration.copy()
        self.duration.tuplet = tuplet
        return lambda:  self._setDuration(oldValue)

    #Apply to selection gets called by different methods. We can just create aliases for Rest
    augment = augmentNoteNearPitch
    diminish = diminishNoteNearPitch
    toggleDot = toggleDotNearPitch
    toggleTriplet = toggleTripletNearPitch

    def setTuplet(self, durationTupletForEachNote:list):
        """
        parameter format compatible with Chord
        [
            (2,3),        #note 1
            (4,5),        #note 2
            (3,4),        #note 3
        ]
        """
        oldValue = self.duration.copy()
        self.duration.tuplet = durationTupletForEachNote[0] #TODO: ? reasoning to only take the first? I think this is right, I just forgot why.
        return lambda: self._setDuration(oldValue)

    def calcFlag(self):
        """What classical flag would be used
        to express baseDuration, even if this only a rest"""
        if self.duration.genericNumber <= 4: #no flag. Quarter, Half or bigger duration.
            return 0
        else:
            return self.duration.genericNumber

    def _exportObject(self, trackState):
        dur = self.logicalDuration()
        return {
            "type": "Rest",
            "rest": self.duration.genericNumber,
            "baseDuration": self.duration.baseDuration,
            "completeDuration" : dur,
            "dots" : self.duration.dots,
            "tuplet" : self.duration.tuplet,
            "tickindex" : trackState.tickindex - dur, #because we parse the tickindex after we stepped over the item.
            "midiBytes" : [],
            #for compatibility with beaming groups:
            "flag" : self.calcFlag(),
            "stem" : (0,0,0),
            "lowestPitchAsDotOnLine" : 0,
            "highestPitchAsDotOnLine" : 0,

            }

    def _lilypond(self, carryLilypondRanges):
        """Called by block.lilypond(carryLilypondRanges), returns a string.
        See Item.lilypond for the general docstring."""
        if self.lilypondParameters["visible"]:
            return "r{}".format(self.duration.lilypond(carryLilypondRanges))
        else:
            return "s{}".format(self.duration.lilypond(carryLilypondRanges))

class MultiMeasureRest(Item):
    def __init__(self, numberOfMeasures):
        super().__init__()
        self.numberOfMeasures = numberOfMeasures
        self._secondInit(parentBlock = None) #None is filled in later by item insert. #see Item._secondInit.

    def _secondInit(self, parentBlock):
        """see Item._secondInit"""
        super()._secondInit(parentBlock) #Item._secondInit
        self._cachedOneMeasureInTicks = 0

    @property
    def numberOfMeasures(self):
        return self._numberOfMeasures

    @numberOfMeasures.setter
    def numberOfMeasures(self, newValue):
        if newValue <= 0: #this allows 0.5 and 0.25
            newValue = 1
        self._numberOfMeasures = newValue

    @classmethod
    def instanceFromSerializedData(cls, serializedObject, parentObject):
        """see Score.instanceFromSerializedData"""
        assert cls.__name__ == serializedObject["class"]
        self = cls.__new__(cls)
        self.deserializeDurationAndNotelistInPlace(serializedObject) #creates parent blocks.
        self._secondInit(parentBlock = parentObject)
        self.numberOfMeasures = int(serializedObject["numberOfMeasures"])
        self.lilypondParameters = serializedObject["lilypondParameters"]
        return self

    def serialize(self):
        result = super().serialize() #call this in child classes
        result["numberOfMeasures"] = self.numberOfMeasures
        return result

    def _copy(self):
        new = MultiMeasureRest(self.numberOfMeasures)
        new.lilypondParameters = self.lilypondParameters.copy()
        new.copyParentBlocks(self)
        return new

    def logicalDuration(self):
        """Return the logical duration that is used to calculate
        the space and subsequent position of items.
        Cursor movement uses this.

        This gets called before exportObject gets called.
        """
        if not self._cachedOneMeasureInTicks:
            for block in self.parentBlocks:
                self._cachedOneMeasureInTicks = block.parentTrack.state.metricalInstruction().oneMeasureInTicks
                break

        return self._cachedOneMeasureInTicks * self.numberOfMeasures

    def _exportObject(self, trackState):
        self._cachedOneMeasureInTicks = trackState.metricalInstruction().oneMeasureInTicks
        self._cachedMetricalInstruction = trackState.metricalInstruction() #for lilypond export
        dur = self.logicalDuration()
        return {
            "type": "MultiMeasureRest",
            "numberOfMeasures": self.numberOfMeasures,
            "completeDuration" : dur,
            "tickindex" : trackState.tickindex - dur, #because we parse the tickindex after we stepped over this item.
            "midiBytes" : [],
            "lilypondParameters" : self.lilypondParameters,
            }

    def _setNumberOfMeasures(self, numberOfMeasures):
        """For undo and redo"""
        oldValue = self.numberOfMeasures
        self.numberOfMeasures = numberOfMeasures
        return lambda: self._setNumberOfMeasures(oldValue)

    def augmentNoteNearPitch(self, pitch):
        """Don't double the duration but simply add one more measure"""
        oldValue = self.numberOfMeasures
        self.numberOfMeasures += 1
        return lambda: self._setNumberOfMeasures(oldValue)

    def diminishNoteNearPitch(self, pitch):
        """Don't half the duration but simply add one more measure"""
        oldValue = self.numberOfMeasures
        self.numberOfMeasures -= 1
        return lambda: self._setNumberOfMeasures(oldValue)

    #Selected MM rests do *2 and /2 like other durations.
    def augment(self):
        oldValue = self.numberOfMeasures
        self.numberOfMeasures = self.numberOfMeasures * 2
        return lambda: self._setNumberOfMeasures(oldValue)

    def diminish(self):
        oldValue = self.numberOfMeasures
        self.numberOfMeasures = self.numberOfMeasures / 2
        return lambda: self._setNumberOfMeasures(oldValue)

    def _lilypond(self, carryLilypondRanges):
        """Called by block.lilypond(carryLilypondRanges), returns a string.
        See Item.lilypond for the general docstring."""
        lyduration = "{}/{}".format(self._cachedMetricalInstruction.nominator, duration.baseDurationToTraditionalNumber[self._cachedMetricalInstruction.denominator])

        if self.lilypondParameters["visible"]:
            return "R1*{}*{}".format(self.numberOfMeasures, lyduration)
        else:
            return "\\skilp1*{}*{}".format(self.numberOfMeasures, lyduration)

class KeySignature(Item):
    """Examples:
    C Major: KeySignature(20, [0, 0, 0, 0, 0, 0, 0]) #c, d, e, f, g, a, b/h
    F Major: KeySignature(170, [0, 0, 0, 0, 0, 0, -10])
    G Major: KeySignature(220, [0, 0, 0, 10, 0, 0, 0])
    Indian Scale ?: KeySignature(220, [0, -10, -20, 0, 0, -10, -20, 0]) #c, des, eeses, f, g, aes, beses, c

    The input deviationFromMajorScale is transformed to an absolute
    keysig, compatible with Lilypond, in init.

    The result is saved permanently in self.keysigList in the following
    form:
        [(0, c), (1, d), (2, e), (3, f), (4, g), (5, a), (6, b)])
    where each note can be , -20, -10, 0, +10, +20
    for double flat to double sharp.

    Attention! the order of items is important.

    Example 2. G Major with correct order of accidentals:
        [(3,10), (0,0), (4,0), (1,0), (5,0), (2,0), (6,0)])

    Example 3. F Major with correct order.
        [(6,-10), (2,0), (5,0), (1,0), (4,0), (0,0), (3,0)])

    So it is possible to rewrite the keysignature manually. It is a
    public parameter.


    Eventhough a dict might seem convenient for this kind of data
    structure the order is important.
    """

    def __init__(self, root, deviationFromMajorScale):
        super().__init__()
        assert root < 350 #really a plain note?
        assert pitchmath.plain(root) == root
        self.root = root
        self.deviationFromMajorScale = deviationFromMajorScale
        self._secondInit(parentBlock = None) #see Item._secondInit.
        self.lilypondParameters["explicit"] = False


    def _secondInit(self, parentBlock):
        """see Item._secondInit"""
        super()._secondInit(parentBlock) #Item._secondInit
        rootIndex = pitchmath.pillarOfFifth.index(self.root) #which position in the pillar has the root note?
        distance = rootIndex - 15 #distance from C. This is not the same as pitchmatch.tonalDistanceFromC! It is the index distance and not the tonal distance as interval.

        transposedMajorScale = self.transposeMajorScale([(3,0), (0,0), (4,0), (1,0), (5,0), (2,0), (6,0)], distance) #the number list is the major scale.
        transposedMajorScale = self.rotateToRoot(sorted(transposedMajorScale), pitchmath.tonalDistanceFromC[self.root]) #Ordered by pitch, rotated to have the root note in front

        #Modify the transposed major scale with our modificator pattern.
        self.keysigList = []
        for maj, mod in zip(transposedMajorScale, self.deviationFromMajorScale):
            self.keysigList.append((maj[0], maj[1] + mod))

        self.keysigList = self.sortKeysig(self.keysigList)

        #Now fill a list with offsets from the middle line in treble clef h', which is 0. c'' is -1, a' is +1
        self.keysigListAsDistanceInDiatonicStepsFrom1720 = [] #the basis for exportObject for GUIs. Need to be modified dynamically with a clef, see exportObject()
        for index, accidentalMod in self.keysigList:
            if accidentalMod:
                if index == 0: #c
                    index = -1
                elif index == 1: #d
                    index = -2
                elif index == 2: #e
                    index = -3
                elif index == 3: #f
                    index = -4
                elif index == 4: #g
                    index = -5
                elif index == 5: #a
                    index = 1
                elif index == 6: #h/b
                    index = 0
                else:
                    raise ValueError("Accidental indices for KeySignatures must be 0-6")
                self.keysigListAsDistanceInDiatonicStepsFrom1720.append((index, accidentalMod))

        self.keysigList = tuple(self.keysigList) #from list to tuple for hashing and caching

    def hash(self):
        return self.keysigList

    def _set(self, root, deviationFromMajorScale):
        """Circular for undo"""
        oldValues = (self.root, self.deviationFromMajorScale)
        self.root = root
        self.deviationFromMajorScale = deviationFromMajorScale
        self._secondInit(parentBlock = None) #see Item._secondInit.
        return lambda ov=oldValues: self._set(*ov)

    @classmethod
    def instanceFromSerializedData(cls, serializedObject, parentObject):
        """see Score.instanceFromSerializedData"""
        assert cls.__name__ == serializedObject["class"]
        self = cls.__new__(cls)
        self.root = int(serializedObject["root"])
        self.deviationFromMajorScale = serializedObject["deviationFromMajorScale"]
        self.deserializeDurationAndNotelistInPlace(serializedObject)
        self._secondInit(parentBlock = parentObject)
        return self

    def serialize(self):
        result = super().serialize() #call this in child classes
        result["root"] = self.root
        result["deviationFromMajorScale"] = self.deviationFromMajorScale
        #Only the two init values are saved. That means that any manual changes to the keysig cannot be saved. Keysigs are damned to get deleted and recreated. It is the same for copy.
        return result

    def _copy(self):
        new = KeySignature(root = self.root, deviationFromMajorScale = self.deviationFromMajorScale)

        return new

    def intervalAutomatic(self, rootPitch, targetPitch):
        undo = self._set(root = pitchmath.intervalAutomatic(self.root, rootPitch, targetPitch), deviationFromMajorScale = self.deviationFromMajorScale)
        return undo

    def transposeMajorScale(self, cMajorScale, stepsInPillar):
        """Transpose a list of positions/modifications like a keysig
        Pillar Of Fifth Steps.
        Sadly the method only works for the Major Keysig"""
        counter = 0
        if stepsInPillar > 0:
            modificator = 10
        else:
            modificator = -10
            cMajorScale.reverse()

        transpose =  []
        for pos, accidental in cMajorScale:
            if counter == abs(stepsInPillar):
                transpose.extend(cMajorScale[counter:7])  #append the missing notes to the return list.
                break
            else:
                transpose.append((pos, accidental + modificator))
                counter += 1
        if stepsInPillar <= 0:  #Return as it came in. Most likely the return list will be sorted anyway later, but this here is more correct.
            transpose.reverse()
        return transpose

    def sortKeysig(self, keysig):
        wip = sorted(keysig)
        new = [wip[3], wip[0], wip[4], wip[1], wip[5], wip[2], wip[6],]
        if wip[6][1] == -10: #Bes included? Then use flat order.
            new.reverse()
        return new

    def rotateToRoot(self, transposedMajorScale, pos):
        """pos is the first number of a keysig tupet.
        Use to rotate a root note to the front"""
        r = deque(transposedMajorScale)
        while not r[0][0] == pos:
            r.rotate(1)
        return list(r)  #this is not simply creating a list but converting to a list. don't replace with [r]

    def asAccidentalsOnLines(self, clef):
        result = []
        for onLineIndex, accidentalMod in self.keysigListAsDistanceInDiatonicStepsFrom1720:
            result.append((onLineIndex + clef.inComparisonToTrebleClefOffset, accidentalMod))
        return result

    def _exportObject(self, trackState):
        """Usual dotsOnLine syntax. 0 for middle line,
        -n to go higher, +n to go lower.

        accidentalsOnLines is a dict with key = linePosition and value
        can be filled with
            bb, b, natural, #, ##
        which is:
            -20, -10, 0, 10, 20
        """
        return {
            "type": "KeySignature",
            "completeDuration" : 0,
            "tickindex" : trackState.tickindex,
            "root" : self.root,
            "accidentalsOnLines" : self.asAccidentalsOnLines(trackState.clef()),
            "midiBytes" : [],

            }

    def _lilypond(self, carryLilypondRanges):
        def build(step, alteration): #generate a Scheme pair for Lilyponds GUILE interpreter
            if alteration == -10:
                return "(" + str(step) + " . ," + "FLAT)"
            elif alteration == 10:
                return "(" + str(step) + " . ," + "SHARP)"
            elif alteration == 20:
                return "(" + str(step) + " . ," + "DOUBLE-SHARP)"
            elif alteration == -20:
                return "(" + str(step) + " . ," + "DOUBLE-FLAT)"
            elif self.lilypondParameters["explicit"] and alteration == 0:
                return "(" + str(step) + " . ," + "NATURAL)"
            else:
                return ""

        #G Major ((3, 10), (0, 0), (4, 0), (1, 0), (5, 0), (2, 0), (6, 0))
        #`((0 . ,NATURAL) (1 . ,NATURAL) (2 . ,NATURAL) (3 . ,SHARP) (4 . ,NATURAL) (5 . ,NATURAL) (6 . ,NATURAL))
        schemepairs = " ".join(build(x[0], x[1]) for x in self.keysigList)
        schemepairs = " ".join(schemepairs.split()) # split and join again to reduce all whitespaces to single ones.
        subtextRoot = "\\once \\override Staff.KeySignature #'stencil = #(lambda (grob) (ly:stencil-combine-at-edge (ly:key-signature-interface::print grob) Y DOWN (grob-interpret-markup grob (markup #:small \"" + pitchmath.pitch2ly[self.root].strip(",").title() + "\")) 3)) " #3 is the space between keysig and text
        lyKeysignature = subtextRoot + "\\set Staff.keyAlterations = #`( {} )".format(schemepairs)
        return lyKeysignature

        #Alternative
        schemepairs = " ".join(build(num, alt) for num, alt in enumerate(self.deviationFromMajorScale))
        schemepairs = " ".join(schemepairs.split()) # split and join again to reduce all whitespaces to single ones.
        subtextRoot = "\\once \\override Staff.KeySignature #'stencil = #(lambda (grob) (ly:stencil-combine-at-edge (ly:key-signature-interface::print grob) Y DOWN (grob-interpret-markup grob (markup #:small \"" + pitchmath.pitch2ly[self.root].strip(",").title() + "\")) 3)) " #3 is the space between keysig and text
        if schemepairs:
            lyKeysignature = subtextRoot + f"\\key {pitchmath.pitch2ly[self.root].strip(',')}  #`( {schemepairs} )"
        else: #no explicit accidental
            lyKeysignature = subtextRoot +  "\\set Staff.keyAlterations = #`(( 0 . ,NATURAL))" #TODO: subtextRoot position is broken
        return lyKeysignature

class Clef(Item):
    """A clef has no direct musical logical meaning. But it gives some hints:
    Voice range. A typical musical voice in a polyphonic setup has between one and two octaves range
    so seeing the clef hints at what voice it is. Therefore we can use a clef a basis for random
    note generation or even range checking and warnings. """

    offset = {
        #the offset of the middle line. Basically: After the clef, how many diatonic steps (gaps/line) is the middle line away from b'/h' (1720)?
        #7 steps is one octave
        #negative goes up, positive goes down.
        #5 lines as basis for the comments.
        "treble" : 0, #Standard G Clef
        "treble^8" : 7,
        "treble_8" : -7,
        "bass" : -12,      #F Clef on the second highest line
        "bass^8" : -5,
        "bass_8" : -19,
        "french" : 2, #G Clef on the lowest line
        "baritone" : -10, #C Clef on the highest line
        "tenor" : -8, #C Clef on the second highest line
        "alto" : -6, #C Clef on the middle line
        "mezzosoprano" : -4, #C clef on the second lowest line
        "soprano" : -2, #C clef on the lowest line
        "varbaritone" :-10, #F Clef on the middle line
        "subbass" : -14, #F Clef on the highest line
        #"midiDrum" : -12, #Percussion clef in the middle for layout, but bass clef in reality. Used for Midi.
        "percussion" : -6,  #Percussion clef in the middle, for lilypond. Needs midi transposition, a special case checked for when doing midi export.
        }

    #regardles of the actual octave, what is the easiest way to display a pitch in another clef. for example F in treble is two lines down in bass.
    #This offset method is used for keysignature
    inComparisonToTrebleClef = {
        "treble" : 0, #Standard G Clef
        "treble^8" : 0,
        "treble_8" : 0,
        "alto" : 1,  #C on the middle line.
        "bass" : 2,      #F Clef on the second highest line
        "bass^8" : 2,
        "bass_8" : 2,
        #"midiDrum" : 2,  #Same as Bass
        "percussion" : 1,  #Same as Alto
        }


    centerPitch = { clefString:offset*50+1720 for clefString, offset in offset.items() }

    def __init__(self, clefString):
        super().__init__()
        self.clefString = clefString
        self._secondInit(parentBlock = None) #see Item._secondInit.

    def _secondInit(self, parentBlock):
        """see Item._secondInit"""
        super()._secondInit(parentBlock) #Item._secondInit
        self.asDotOnLineOffset = Clef.offset[self.clefString]
        self.inComparisonToTrebleClefOffset = Clef.inComparisonToTrebleClef[self.clefString]
        self.rangeCenter = Clef.centerPitch[self.clefString]
        self.rangeLowest = self.rangeCenter - 6*50 #the center is always the pitch on "middle line"
        self.rangeHighest = self.rangeCenter + 5*50

    def __hash__(self):
        """Since we implemented __eq__ we need to make Clef hashable again"""
        return id(self)
    def __eq__(self, other): # self == other
        if not type(other) is Clef:
            return False
        return self.clefString == other.clefString
    def __ne__(self, other): # self != other
        if not type(other) is Clef:
            return True
        return self.clefString != other.clefString


    @classmethod
    def instanceFromSerializedData(cls, serializedObject, parentObject):
        """see Score.instanceFromSerializedData"""
        assert cls.__name__ == serializedObject["class"]
        self = cls.__new__(cls)
        self.clefString = serializedObject["clefString"]
        self.deserializeDurationAndNotelistInPlace(serializedObject)
        self._secondInit(parentBlock = parentObject)
        return self

    def serialize(self):
        result = super().serialize() #call this in child classes
        result["clefString"] = self.clefString
        #The other values are generated from clefString
        return result

    def _copy(self):
        new = Clef(self.clefString)

        return new

    def _exportObject(self, trackState):
        return {
            "type": "Clef",
            "completeDuration" : 0,
            "tickindex" : trackState.tickindex,
            "clef" : self.clefString,
            "midiBytes" : [],

            #Thats it. A GUI does not need to know anything about the clef except its look because we already deliever note pitches as dots on lines, calculated with the clef.
            }

    def _lilypond(self, carryLilypondRanges):
        return "\\clef \"{}\"".format(self.clefString)

class TimeSignature(Item): #Deprecated since 1750
    """Meter information. Nominator is how many notes in one measure and
    denominator is notes of which type in ticks.
    Eventhough Time Signatures are logically only valid at the beginning
    of a measure it is still possible to add them anywhere because
    Lilypond allows it as well. And it is much easier to program this
    way.
    Dumb users will always find a way to put stuff in the wrong place.
    If they want timesigs in the middle of a bar, let them have it... idiots.

    Do not edit a timesig. Delete and recreate."""

    def __init__(self, nominator, denominator):
        raise RuntimeError("Use items.MetricalInstruction")
        super().__init__()
                                       #examples for 4/4 or 4/(210*2**8) measures
        self.nominator = nominator     #example: 4. Upper number
        self.denominator = denominator #example: 210*2**8. Lower number

        self._secondInit(parentBlock = None) #see Item._secondInit.

    def _secondInit(self, parentBlock):
        """see Item._secondInit"""
        super()._secondInit(parentBlock) #Item._secondInit
        self.oneMeasureInTicks = self.nominator * self.denominator


    @classmethod
    def instanceFromSerializedData(cls, serializedObject, parentObject):
        """see Score.instanceFromSerializedData"""
        assert cls.__name__ == serializedObject["class"]
        self = cls.__new__(cls)
        self.nominator = serializedObject["nominator"]
        self.denominator = serializedObject["denominator"]
        self.deserializeDurationAndNotelistInPlace(serializedObject)
        self._secondInit(parentBlock = parentObject)
        return self

    def serialize(self):
        result = super().serialize() #call this in child classes
        result["nominator"] = self.nominator
        result["denominator"] = self.denominator
        return result

    def _copy(self):
        new = TimeSignature(self.nominator, self.denominator)
        return new

    def _exportObject(self, trackState):
        return {
            "type": "TimeSignature",
            "completeDuration" : 0,
            "tickindex" : trackState.tickindex,
            "nominator" : self.nominator,
            "denominator" : duration.baseDurationToTraditionalNumber[self.denominator],
            "midiBytes" : [],

            }

class MetricalInstruction(Item):
    def __init__(self, treeOfInstructions, isMetrical = True):
        """Don't edit. Delete and create a new one"""
        super().__init__()
        for value in flatList(treeOfInstructions):
            if not type(value) is int:
                raise ValueError("Only integers are allowed in a metrical instruction. You used {}. Cast to int if you are sure your float is x.0, e.g. in a dotted quarter note created by D4*1.5".format(value))

        self.treeOfInstructions = treeOfInstructions #if you skipped the docstring. this is a 4/4: ((D4, D4), (D4, D4)) . If empty no barlines will get drawn
        self.isMetrical = isMetrical #If False all dynamic playback modifications based on the metre will be deactivated but barlines will still be created. This is forced to become false when the treeOfInstructions is 0 ticks.
        self._secondInit(parentBlock = None) #see Item._secondInit.


    def _secondInit(self, parentBlock):
        """see Item._secondInit"""
        super()._secondInit(parentBlock) #Item._secondInit
        i = sum(flatList(self.treeOfInstructions))
        assert i >= 0 #stop your shennenigans!
        self.oneMeasureInTicks = i #even if i is 0.

        if i == 0:
            self.isMetrical = False #protection against a wrong combination
            self.nominator = None
            self.denominator = None
        else:
            self.nominator = len(list(flatList(self.treeOfInstructions))) #for compatibility with lilypond etc.
            self.denominator = self.oneMeasureInTicks / self.nominator #for compatibility with lilypond etc.

    @classmethod
    def instanceFromSerializedData(cls, serializedObject, parentObject):
        """see Score.instanceFromSerializedData"""
        assert cls.__name__ == serializedObject["class"]
        self = cls.__new__(cls)
        self.treeOfInstructions = eval(serializedObject["treeOfInstructions"]) #we really want to keep the tuples and not mutable lists.
        self.isMetrical = serializedObject["isMetrical"]
        self.deserializeDurationAndNotelistInPlace(serializedObject)
        self._secondInit(parentBlock = parentObject)
        return self

    def serialize(self):
        result = super().serialize() #call this in child classes
        result["treeOfInstructions"] = self.treeOfInstructions.__repr__() #we really want to keep the tuples and not mutable lists.
        result["isMetrical"] = self.isMetrical
        return result

    def _copy(self):
        new = MetricalInstruction(self.treeOfInstructions, self.isMetrical)
        return new

    def _exportObject(self, trackState):
        return {
            "type": "MetricalInstruction",
            "completeDuration" : 0, #it has no duration of its own.
            "tickindex" : trackState.tickindex,
            #"instruction" : #TODO,
            "isMetrical" : self.isMetrical,
            "oneMeasureInTicks" : self.oneMeasureInTicks,
            "midiBytes" : [],
            "treeOfInstructions" : self.treeOfInstructions.__repr__(),
            }

    def _lilypond(self, carryLilypondRanges):
        """Since metrical instruction can get very complex we rely entirely on the lilypond override
        functionality. The common api signatures already include these."""
        raise RuntimeError("This metrical instruction should have a lilypond-override")

class BlockEndMarker(Item):
    """Cannot be inserted into the real blocks. It is added
    dynamically and automatically as exportObject
    only during track. static representation"""

    def __init__(self):
        super(BlockEndMarker, self).__init__()

    #does not need a serialize or copy method since it gets dynamically created only for export

    def _exportObject(self, trackState):
        return {
            "type": "BlockEndMarker",
            "completeDuration" : 0,
            "tickindex" : trackState.tickindex,
            "midiBytes" : [],

            }

class DynamicSignature(Item):
    """The class for "piano", "forte" and so on"""
    def __init__(self, keyword):
        assert keyword in ["ppppp", "pppp", "ppp", "pp", "p", "mp", "mf", "f", "ff", "fff", "ffff", "tacet", "custom"]
        super().__init__()
        self.keyword = keyword
        self._secondInit(parentBlock = None) #see Item._secondInit.

    def _secondInit(self, parentBlock):
        """see Item._secondInit"""
        super()._secondInit(parentBlock) #Item._secondInit

    @classmethod
    def instanceFromSerializedData(cls, serializedObject, parentObject):
        """see Score.instanceFromSerializedData"""
        assert cls.__name__ == serializedObject["class"]
        self = cls.__new__(cls)
        self.keyword = serializedObject["keyword"]
        self.deserializeDurationAndNotelistInPlace(serializedObject)
        self._secondInit(parentBlock = parentObject)
        return self

    def serialize(self):
        result = super().serialize() #call this in child classes
        result["keyword"] = self.keyword
        return result

    def _copy(self):
        new = DynamicSignature(keyword = self.keyword)
        return new

    def baseVelocity(self, trackState):
        return trackState.track.dynamicSettingsSignature.dynamics[self.keyword]

    def _exportObject(self, trackState):
        return {
            "type": "DynamicSignature",
            "completeDuration" : 0,
            "tickindex" : trackState.tickindex,
            "keyword" : self.keyword,
            "midiBytes" : [],
            }

class DynamicRamp(Item):
    """The class for "cresc", "decresc" and so on.
    It takes the current trackState as starting point and
    interpolates to the next DynamicSignature.
    If there is no DynamicSignature right of this item
    then the ramp goes to 127 or 0. The latter is probably useful for
    a fadeout.

    Setting the interpolation type to "standalone" effectively switches
    the interpolation off for this item. This is useful if you want
    the score marker (e.g. for Lilypond) but not the midi, for example
    because you do dynamics with the expression CC.

    Cresc or Decresc is set automatically.

    A note gets its velocity through

    note.dynamic.velocity(trackState)
    """

    def __init__(self):
        super().__init__()
        self.graphType = "linear" #options: linear, standalone
        self._secondInit(parentBlock = None) #see Item._secondInit.

    def _secondInit(self, parentBlock):
        """see Item._secondInit"""
        super()._secondInit(parentBlock) #Item._secondInit
        #The cached valueas are set during export.
        self._cachedTargetVelocity = -1 #0 is valid as well. Test for >0
        self._cachedTargetTick = -1 #0 is valid as well.  Test for >0
        self._cachedTickIndex = -1 #0 is valid as well.  Test for >0
        self._cachedVelocity = -1 #0 is valid as well.  Test for >0

    @classmethod
    def instanceFromSerializedData(cls, serializedObject, parentObject):
        """see Score.instanceFromSerializedData"""
        assert cls.__name__ == serializedObject["class"]
        self = cls.__new__(cls)
        self.graphType = serializedObject["graphType"]
        self.deserializeDurationAndNotelistInPlace(serializedObject)
        self._secondInit(parentBlock = parentObject)
        return self

    def serialize(self):
        result = super().serialize() #call this in child classes
        result["graphType"] = self.graphType
        return result

    def _copy(self):
        new = DynamicRamp()
        new.graphType = self.graphType

        return new

    def _linearVelocityValue(self, tickindex):
        """
        the variable is taken from the standard formula: f(x) = m*x + n

        We want to know the velocity value for a tickindex x.
        f(x) is that velocity.
        m is the gradient (steigung)
        """
        x = tickindex - self._cachedTickIndex
        n = self._cachedVelocity
        divider = (self._cachedTargetTick - self._cachedTickIndex)
        if not divider: #there is a chance  that this gets 0 but only while editing is in an intermediate sate.
            divider = 1
        m = (self._cachedTargetVelocity - self._cachedVelocity) / divider
        return int(m*x+n)

    def getVelocity(self, tickindex):
        """Get the complete velocity for a tickindex, without the base
        velocity from the dynamic Signature"""
        if self._cachedVelocity >= 0 and self._cachedTargetVelocity >= 0 and self._cachedTargetTick >= 0 and self._cachedTickIndex >= 0:
            assert tickindex <= self._cachedTargetTick
            #assert tickindex >= self._cachedTickIndex  #this creates problems with patterns which modify the tickindex while exporting.

            if self.graphType == "linear":
                return self._linearVelocityValue(tickindex)
            elif self.graphType == "standalone":
                return self._cachedVelocity
            else:
                raise ValueError("GraphType unknown:", self.graphType)

        else: #this is the case when a dynamic ramp has no final dynamic signature. note.dynamic.velocity checks for None and chooses the normal velocity instead.
            return None

    def _exportObject(self, trackState):
        """The keyword is dyn-ramp if there is either no follow-up
        Dynamic Signature or if the same dynsig follows again"""

        keyword = "dyn-ramp"
        if self._cachedVelocity >= 0 and self._cachedTargetVelocity >= 0 and self._cachedTargetTick >= 0 and self._cachedTickIndex >= 0:
            #print ("from {} to {} in {} ticks".format(self._cachedVelocity, self._cachedTargetVelocity, self._cachedTargetTick - self._cachedTickIndex ))
            if self._cachedVelocity > self._cachedTargetVelocity:
                keyword = "decresc"
            elif self._cachedVelocity < self._cachedTargetVelocity:
                keyword = "cresc"
            #else it stays dyn-ramp.
        return {
            "type": "DynamicSignature",
            "graphType" : self.graphType,
            "keyword" : keyword,
            "completeDuration" : 0,
            "tickindex" : trackState.tickindex,
            "midiBytes" : [],

            }

class LegatoSlur(Item):
    def __init__(self):
        super().__init__()
        self._secondInit(parentBlock = None) #see Item._secondInit.

    def _secondInit(self, parentBlock):
        """see Item._secondInit"""
        super()._secondInit(parentBlock) #Item._secondInit

    @classmethod
    def instanceFromSerializedData(cls, serializedObject, parentObject):
        """see Score.instanceFromSerializedData"""
        assert cls.__name__ == serializedObject["class"]
        self = cls.__new__(cls)
        self.deserializeDurationAndNotelistInPlace(serializedObject)
        self._secondInit(parentBlock = parentObject)
        return self


    def _copy(self):
        new = LegatoSlur()
        return new

    def openOrClose(self, trackState):
        """This function gets called by exportObject where
        the trackState is already after the current item, which is
        LegatoSlur.
        This means if trackState.duringLegatoSlur is False we just
        closed a slur.
        """
        if trackState.duringLegatoSlur:
            return "open"
        else:
            return "close"

    def _exportObject(self, trackState):
        return {
            "type" : "LegatoSlur",
            "slur" : self.openOrClose(trackState),
            "completeDuration" : 0,
            "tickindex" : trackState.tickindex,
            "midiBytes" : [],
        }

class InstrumentChange(Item):
    """Includes program change, both bank changes and the lilypond short name change.
    Lilypond does not support a full name change, so we don't offer that here."""
    def __init__(self, program, msb, lsb, shortInstrumentName):
        """Either init gets called, by creation during runtime, or instanceFromSerializedData.
        That means everything in init must be matched by a loading call in instanceFromSerializedData."""
        super().__init__()
        assert -1 <= program <= 127, program  #-1 means Laborejo will not send the program change, if this is the initial change.
        assert 0 <= msb <= 127, msb #a CC value
        assert 0 <= lsb <= 127, lsb #a CC value
        self.program = program
        self.msb = msb
        self.lsb = lsb
        self.shortInstrumentName = shortInstrumentName
        self._secondInit(parentBlock = None) #see Item._secondInit.

    def _secondInit(self, parentBlock):
        """After creating a new item during runtime or after loading from a file this gets called.
        Put every calcuation that depends on values that could be changed by the user.
        see Item._secondInit"""
        super()._secondInit(parentBlock) #Item._secondInit

    @classmethod
    def instanceFromSerializedData(cls, serializedObject, parentObject):
        """see Score.instanceFromSerializedData"""
        assert cls.__name__ == serializedObject["class"]
        self = cls.__new__(cls)
        self.program = serializedObject["program"]
        self.msb = serializedObject["msb"]
        self.lsb = serializedObject["lsb"]
        self.shortInstrumentName = serializedObject["shortInstrumentName"]
        self.deserializeDurationAndNotelistInPlace(serializedObject)
        self._secondInit(parentBlock = parentObject)
        return self

    def serialize(self):
        result = super().serialize() #call this in child classes
        result["program"] = self.program
        result["msb"] = self.msb
        result["lsb"] = self.lsb
        result["shortInstrumentName"] = self.shortInstrumentName
        return result

    def _copy(self):
        """return an independent copy of self"""
        new = type(self)(self.program, self.msb, self.lsb, self.shortInstrumentName) #all init parameters are immutable types and copied implicitly
        return new

    def _exportObject(self, trackState):
        duration = 0
        tickPosition = trackState.tickindex - duration #minus duration because we are RIGHT of an item after parsing it in structures.staticRepresentation
        #change this trackState.midiProgram = self.value
        assert tickPosition == 0 or self.program >= 0

        _msbStr = " msb" + str(self.msb) if self.msb > 0 else ""
        _lsbStr = " lsb" + str(self.lsb) if self.lsb > 0 else ""
        _nameStr = self.shortInstrumentName
        _nameStr = _nameStr + " " if self.shortInstrumentName else ""
        return {
            "type" : "InstrumentChange",
            "completeDuration" : duration,
            "tickindex" : trackState.tickindex - duration, #we parse the tickindex after we stepped over the item.
            "midiBytes" : [cbox.Pattern.serialize_event(tickPosition, 0xC0 + trackState.midiChannel(), self.program, 0),
                           cbox.Pattern.serialize_event(tickPosition, 0xB0 + trackState.midiChannel(), 0, self.msb), #position, status byte+channel, controller number, controller value
                           cbox.Pattern.serialize_event(tickPosition, 0xB0 + trackState.midiChannel(), 32, self.lsb), #position, status byte+channel, controller number, controller value
                           ],
            "shortInstrumentName" : self.shortInstrumentName,
            "UIstring" : "{}[pr{}{}{}]".format(_nameStr, self.program, _msbStr, _lsbStr), #this is for a UI, possibly a text UI, maybe for simple items of a GUI. Make it as short and unambigious as possible.
        }

    def _lilypond(self, carryLilypondRanges):
        """called by block.lilypond(carryLilypondRanges), returns a string.
        Don't create white-spaces yourself, this is done by the structures.
        When in doubt prefer functionality and robustness over 'beautiful' lilypond syntax."""
        if self.shortInstrumentName:
            return f'\\mark \\markup {{\\small "{self.shortInstrumentName}"}}' + "\n" + f'\\set Staff.shortInstrumentName = #"{self.shortInstrumentName}"'
        else:
            return ""

class ChannelChange(Item):
    def __init__(self, value, text):
        """Either init gets called, by creation during runtime, or instanceFromSerializedData.
        That means everything in init must be matched by a loading call in instanceFromSerializedData."""
        super().__init__()
        #self.itemData #matches instanceFromSerializedData and serialize
        assert 0 <= value <= 16, value
        self.value = value
        self.text = text
        self._secondInit(parentBlock = None) #see Item._secondInit.

    def _secondInit(self, parentBlock):
        """After creating a new item during runtime or after loading from a file this gets called.
        Put every calcuation that depends on values that could be changed by the user.
        see Item._secondInit"""
        super()._secondInit(parentBlock) #Item._secondInit

    @classmethod
    def instanceFromSerializedData(cls, serializedObject, parentObject):
        """see Score.instanceFromSerializedData"""
        assert cls.__name__ == serializedObject["class"]
        self = cls.__new__(cls)
        self.value = serializedObject["value"]
        self.text = serializedObject["text"]
        self.deserializeDurationAndNotelistInPlace(serializedObject)
        self._secondInit(parentBlock = parentObject)
        return self

    def serialize(self):
        result = super().serialize() #call this in child classes
        result["value"] = self.value
        result["text"] = self.text
        return result

    def _copy(self):
        """return an independent copy of self"""
        new = type(self)(self.value, self.text) #both init parameters are immutable types and copied implicitly
        return new

    def _exportObject(self, trackState):
        duration = 0
        tickPosition = trackState.tickindex - duration #minus duration because we are RIGHT of an item after parsing it in structures.staticRepresentation
        #the new midi channel for the cursor, and all following export items, is handled by the track state and left/right parser
        return {
            "type" : "ChannelChange",
            "completeDuration" : duration,
            "tickindex" : trackState.tickindex - duration, #we parse the tickindex after we stepped over the item.
             "midiBytes" : [],
            "text" : self.text,
            #TODO: UIs work with 1-16. The rest of the laborejo engine works with 0-15 but this is dedicated for our own UI, read-only. Export the GUI value directly, even if it is a responsibility mismatch.
            "UIstring" : f"{self.text}(ch{self.value+1})" if self.text else f"ch{self.value+1}", #this is for a UI, possibly a text UI, maybe for simple items of a GUI. Make it as short and unambigious as possible.
        }

    def _lilypond(self, carryLilypondRanges):
        """called by block.lilypond(carryLilypondRanges), returns a string.
        Don't create white-spaces yourself, this is done by the structures.
        When in doubt prefer functionality and robustness over 'beautiful' lilypond syntax."""
        if self.text:
            return f'\\mark \\markup {{\\small "{self.text}"}}'
        else:
            return ""


#Note in use.
class RecordedNote(Item):
    """An intermediate item that gets created by the live recording process.
    It has no Duration instance but a simple tick duration instead.
    Also it has a fixed position in tick-time and cannot be edited with the cursor and the other
    methods. It lives in a special per-track buffer.

    Is is saved and gets loaded. """

    def __init__(self, itemData):
        """Either init gets called, by creation during runtime, or instanceFromSerializedData.
        That means everything in init must be matched by a loading call in instanceFromSerializedData."""
        super().__init__(positionInTicks, durationInTicks ) #TODO: that is not super._init of item. Item has no parameters!
        self.positionInTicks = positionInTicks
        self.durationInTicks = durationInTicks

        self._secondInit(parentBlock = None) #see Item._secondInit.

    def _secondInit(self, parentBlock):
        """After creating a new item during runtime or after loading from a file this gets called.
        Put every calcuation that depends on values that could be changed by the user.
        see Item._secondInit"""
        super()._secondInit(parentBlock) #Item._secondInit

    @classmethod
    def instanceFromSerializedData(cls, serializedObject, parentObject):
        """see Score.instanceFromSerializedData"""
        raise NotImplementedError("Implement instanceFromSerializedData() for this item type!")
        assert cls.__name__ == serializedObject["class"]
        self = cls.__new__(cls)
        self.itemData = serializedObject["itemData"]
        self.deserializeDurationAndNotelistInPlace(serializedObject)
        self._secondInit(parentBlock = parentObject)
        return self

    def serialize(self):
        raise NotImplementedError("Implement serialize() for this item type!")
        result = super().serialize() #call this in child classes
        result["itemData"] = self.itemData
        return result

    def _copy(self):
        """return an independent copy of self"""
        raise NotImplementedError("Implement _copy() for this item type!")
        new = TemplateItem(self.itemData)
        return new

    def _exportObject(self, trackState):
        """Implement this in every child class, return a dict.

        Base class function to return a dict which is a good layout
        export basis. For example for GUI frontends. They don't have to
        parse and calculate their own values in slow pure Python then

        midiBytes is a list with patternBlob/binary data generated by
        cbox.Pattern.serialize_event(position, midibyte1 (noteon), midibyte2(pitch), midibyte3(velocity))
        Items with duration, like chords, will most like need to create
        at least two patternBlobs, one for the start (e.g. note on) and one
        for the stop (e.g. note off)
        """
        raise NotImplementedError("Implement _exportObject() for this item type!")

        #And for documentation and education here a template exportObject
        duration = 0
        return {
            "type" : "",
            "completeDuration" : duration,
            "tickindex" : trackState.tickindex - duration, #we parse the tickindex after we stepped over the item.
            "midiBytes" : [],
            "UIstring" : "", #this is for a UI, possibly a text UI, maybe for simple items of a GUI. Make it as short and unambigious as possible.
        }

    def _lilypond(self, carryLilypondRanges):
        """absolutely not"""
        return ""

class LilypondText(Item):
    """lilypond text as a last resort"""

    def __init__(self, text):
        super().__init__()
        self.text = text
        self._secondInit(parentBlock = None) #On item creation there is no parentBlock. The block adds itself to the item during insert.

    def _secondInit(self, parentBlock):
        """see Item._secondInit"""
        super()._secondInit(parentBlock) #Item._secondInit

    def _lilypond(self, carryLilypondRanges):
        """called by block.lilypond(carryLilypondRanges), returns a string.
        Don't create white-spaces yourself, this is done by the structures.
        When in doubt prefer functionality and robustness over 'beautiful' lilypond syntax."""
        return self.text


    @classmethod
    def instanceFromSerializedData(cls, serializedObject, parentObject):
        """see Score.instanceFromSerializedData"""
        assert cls.__name__ == serializedObject["class"]
        self = cls.__new__(cls)
        self.text = serializedObject["text"]
        self.deserializeDurationAndNotelistInPlace(serializedObject) #creates parentBlocks
        self._secondInit(parentBlock = parentObject)
        return self

    def serialize(self):
        result = super().serialize() #call this in child classes
        result["text"] = self.text
        return result

    def _copy(self):
        """return an independent copy of self"""
        new = LilypondText(self.text)
        return new


    def _exportObject(self, trackState):
        return {
            "type" : "LilypondText",
            "completeDuration" : 0,
            "tickindex" : trackState.tickindex, #we parse the tickindex after we stepped over the item.
             "midiBytes" : [],
            "text" : self.text,
             "UIstring" : self.text, #this is for a UI, possibly a text UI, maybe for simple items of a GUI. Make it as short and unambigious as possible.
        }
