/*
    Rosegarden
    A sequencer and musical notation editor.

    This program is Copyright 2000-2008
        Guillaume Laurent   <glaurent@telegraph-road.org>,
        Chris Cannam        <cannam@all-day-breakfast.com>,
        Richard Bown        <bownie@bownie.com>

    The moral right of the authors to claim authorship of this work
    has been asserted.

    This program is free software; you can redistribute it and/or
    modify it under the terms of the GNU General Public License as
    published by the Free Software Foundation; either version 2 of the
    License, or (at your option) any later version.  See the file
    COPYING included with this distribution for more information.
*/

#include "SegmentNotationHelper.h"
#include "NotationTypes.h"
#include "Quantizer.h"
#include "BasicQuantizer.h"
#include "NotationQuantizer.h"
#include "BaseProperties.h"
#include "Composition.h"

#include <iostream>
#include <algorithm>
#include <iterator>
#include <list>

namespace Rosegarden 
{
using std::cerr;
using std::endl;
using std::string;
using std::list;

using namespace BaseProperties;


SegmentNotationHelper::~SegmentNotationHelper() { }


const Quantizer &
SegmentNotationHelper::basicQuantizer() {
    return *(segment().getComposition()->getBasicQuantizer());
}

const Quantizer &
SegmentNotationHelper::notationQuantizer() {
    return *(segment().getComposition()->getNotationQuantizer());
}


//!!! we need to go very carefully through this file and check calls
//to getAbsoluteTime/getDuration -- the vast majority should almost
//certainly now be using getNotationAbsoluteTime/getNotationDuration

Segment::iterator
SegmentNotationHelper::findNotationAbsoluteTime(timeT t)
{
    iterator i(segment().findTime(t));

    // We don't know whether the notation absolute time t will appear
    // before or after the real absolute time t.  First scan backwards
    // until we find a notation absolute time prior to (or equal to)
    // t, and then scan forwards until we find the first one that
    // isn't prior to t

    while (i != begin() &&
	   ((i == end() ? t + 1 : (*i)->getNotationAbsoluteTime()) > t))
	--i;

    while (i != end() &&
	   ((*i)->getNotationAbsoluteTime() < t))
	++i;

    return i;
}

Segment::iterator
SegmentNotationHelper::findNearestNotationAbsoluteTime(timeT t)
{
    iterator i(segment().findTime(t));

    // Exactly findNotationAbsoluteTime, only with the two scan loops
    // in the other order

    while (i != end() &&
	   ((*i)->getNotationAbsoluteTime() < t))
	++i;

    while (i != begin() &&
	   ((i == end() ? t + 1 : (*i)->getNotationAbsoluteTime()) > t))
	--i;

    return i;
}

void
SegmentNotationHelper::setNotationProperties(timeT startTime, timeT endTime)
{
    Segment::iterator from = begin();
    Segment::iterator to = end();

    if (startTime != endTime) {
	from = segment().findTime(startTime);
	to   = segment().findTime(endTime);
    }
/*!!!
    bool justSeenGraceNote = false;
    timeT graceNoteStart = 0;
*/
    for (Segment::iterator i = from;
	 i != to && segment().isBeforeEndMarker(i); ++i) {

	if ((*i)->has(NOTE_TYPE) /*!!! && !(*i)->has(IS_GRACE_NOTE) */) continue;

	timeT duration = (*i)->getNotationDuration();

	if ((*i)->has(BEAMED_GROUP_TUPLET_BASE)) {
	    int tcount = (*i)->get<Int>(BEAMED_GROUP_TUPLED_COUNT);
	    int ucount = (*i)->get<Int>(BEAMED_GROUP_UNTUPLED_COUNT);

	    if (tcount == 0) {
		std::cerr << "WARNING: SegmentNotationHelper::setNotationProperties: zero tuplet count:" << std::endl;
		(*i)->dump(std::cerr);
	    } else {
		// nominal duration is longer than actual (sounding) duration
		duration = (duration / tcount) * ucount;
	    }
	}

	if ((*i)->isa(Note::EventType) || (*i)->isa(Note::EventRestType)) {

	    if ((*i)->isa(Note::EventType)) {
/*!!!		
		if ((*i)->has(IS_GRACE_NOTE) &&
		    (*i)->get<Bool>(IS_GRACE_NOTE)) {

		    if (!justSeenGraceNote) {
			graceNoteStart = (*i)->getNotationAbsoluteTime();
			justSeenGraceNote = true;
		    }

		} else if (justSeenGraceNote) {

		    duration += (*i)->getNotationAbsoluteTime() - graceNoteStart;
		    justSeenGraceNote = false;
		}
*/
	    }

	    Note n(Note::getNearestNote(duration));

	    (*i)->setMaybe<Int>(NOTE_TYPE, n.getNoteType());
	    (*i)->setMaybe<Int>(NOTE_DOTS, n.getDots());
	}
    }
}

timeT
SegmentNotationHelper::getNotationEndTime(Event *e)
{
    return e->getNotationAbsoluteTime() + e->getNotationDuration();
}


Segment::iterator
SegmentNotationHelper::getNextAdjacentNote(iterator i,
					   bool matchPitch,
					   bool allowOverlap)
{
    iterator j(i);
    if (!isBeforeEndMarker(i)) return i;
    if (!(*i)->isa(Note::EventType)) return end();

    timeT iEnd = getNotationEndTime(*i);
    long ip = 0, jp = 0;
    if (!(*i)->get<Int>(PITCH, ip) && matchPitch) return end();

    while (true) {
	if (!isBeforeEndMarker(j) || !isBeforeEndMarker(++j)) return end();
	if (!(*j)->isa(Note::EventType)) continue;

	timeT jStart = (*j)->getNotationAbsoluteTime();
	if (jStart > iEnd) return end();

	if (matchPitch) {
	    if (!(*j)->get<Int>(PITCH, jp) || (jp != ip)) continue;
	}

	if (allowOverlap || (jStart == iEnd)) return j;
    }
}

   
Segment::iterator
SegmentNotationHelper::getPreviousAdjacentNote(iterator i,
					       timeT rangeStart,
					       bool matchPitch,
					       bool allowOverlap)
{ 
    iterator j(i);
    if (!isBeforeEndMarker(i)) return i;
    if (!(*i)->isa(Note::EventType)) return end();

    timeT iStart = (*i)->getNotationAbsoluteTime();
    timeT iEnd   = getNotationEndTime(*i);
    long ip = 0, jp = 0;
    if (!(*i)->get<Int>(PITCH, ip) && matchPitch) return end();

    while (true) {
	if (j == begin()) return end(); else --j;
	if (!(*j)->isa(Note::EventType)) continue;
	if ((*j)->getAbsoluteTime() < rangeStart) return end();

	timeT jEnd = getNotationEndTime(*j);

	// don't consider notes that end after i ends or before i begins

	if (jEnd > iEnd || jEnd < iStart) continue;

	if (matchPitch) {
	    if (!(*j)->get<Int>(PITCH, jp) || (jp != ip)) continue;
	}

	if (allowOverlap || (jEnd == iStart)) return j;
    }
}


Segment::iterator
SegmentNotationHelper::findContiguousNext(iterator el) 
{
    std::string elType = (*el)->getType(),
        reject, accept;
     
    if (elType == Note::EventType) {
        accept = Note::EventType;
        reject = Note::EventRestType;
    } else if (elType == Note::EventRestType) {
        accept = Note::EventRestType;
        reject = Note::EventType;
    } else {
        accept = elType;
        reject = "";
    }

    bool success = false;

    iterator i = ++el;
    
    for(; isBeforeEndMarker(i); ++i) {
        std::string iType = (*i)->getType();

        if (iType == reject) {
            success = false;
            break;
        }
        if (iType == accept) {
            success = true;
            break;
        }
    }

    if (success) return i;
    else return end();
    
}

Segment::iterator
SegmentNotationHelper::findContiguousPrevious(iterator el)
{
    if (el == begin()) return end();

    std::string elType = (*el)->getType(),
        reject, accept;
     
    if (elType == Note::EventType) {
        accept = Note::EventType;
        reject = Note::EventRestType;
    } else if (elType == Note::EventRestType) {
        accept = Note::EventRestType;
        reject = Note::EventType;
    } else {
        accept = elType;
        reject = "";
    }

    bool success = false;

    iterator i = --el;

    while (true) {
        std::string iType = (*i)->getType();

        if (iType == reject) {
            success = false;
            break;
        }
        if (iType == accept) {
            success = true;
            break;
        }
	if (i == begin()) break;
	--i;
    }

    if (success) return i;
    else return end();
}


bool
SegmentNotationHelper::noteIsInChord(Event *note)
{
    iterator i = segment().findSingle(note);
    timeT t = note->getNotationAbsoluteTime();
    
    for (iterator j = i; j != end(); ++j) { // not isBeforeEndMarker, unnecessary here
	if (j == i) continue;
	if ((*j)->isa(Note::EventType)) {
	    timeT tj = (*j)->getNotationAbsoluteTime();
	    if (tj == t) return true;
	    else if (tj > t) break;
	}
    }

    for (iterator j = i; ; ) {
	if (j == begin()) break;
	--j;
	if ((*j)->isa(Note::EventType)) {
	    timeT tj = (*j)->getNotationAbsoluteTime();
	    if (tj == t) return true;
	    else if (tj < t) break;
	}
    }

    return false;

/*!!!
    iterator first, second;
    segment().getTimeSlice(note->getAbsoluteTime(), first, second);

    int noteCount = 0;
    for (iterator i = first; i != second; ++i) {
	if ((*i)->isa(Note::EventType)) ++noteCount;
    }

    return noteCount > 1;
*/
}


//!!! This doesn't appear to be used any more and may well not work.
// Ties are calculated in several different places, and it's odd that
// we don't have a decent API for them 
Segment::iterator
SegmentNotationHelper::getNoteTiedWith(Event *note, bool forwards)
{
    bool tied = false;

    if (!note->get<Bool>(forwards ?
			 BaseProperties::TIED_FORWARD :
                         BaseProperties::TIED_BACKWARD, tied) || !tied) {
        return end();
    }

    timeT myTime = note->getAbsoluteTime();
    timeT myDuration = note->getDuration();
    int myPitch = note->get<Int>(BaseProperties::PITCH);

    iterator i = segment().findSingle(note);
    if (!isBeforeEndMarker(i)) return end();

    for (;;) {
        i = forwards ? findContiguousNext(i) : findContiguousPrevious(i);

        if (!isBeforeEndMarker(i)) return end();
        if ((*i)->getAbsoluteTime() == myTime) continue;

        if (forwards && ((*i)->getAbsoluteTime() != myTime + myDuration)) {
            return end();
        }
        if (!forwards &&
            (((*i)->getAbsoluteTime() + (*i)->getDuration()) != myTime)) {
            return end();
        }
        
        if (!(*i)->get<Bool>(forwards ?
                             BaseProperties::TIED_BACKWARD :
                             BaseProperties::TIED_FORWARD, tied) || !tied) {
            continue;
        }

        if ((*i)->get<Int>(BaseProperties::PITCH) == myPitch) return i;
    }

    return end();
}


bool
SegmentNotationHelper::collapseRestsIfValid(Event* e, bool& collapseForward)
{
    iterator elPos = segment().findSingle(e);
    if (elPos == end()) return false;

    timeT myDuration = (*elPos)->getNotationDuration();

    // findContiguousNext won't return an iterator beyond the end marker
    iterator nextEvent = findContiguousNext(elPos),
	 previousEvent = findContiguousPrevious(elPos);

    // Remark: findContiguousXXX is inadequate for notes, we would
    // need to check adjacency using e.g. getNextAdjacentNote if this
    // method were to work for notes as well as rests.

    // collapse to right if (a) not at end...
    if (nextEvent != end() &&
	// ...(b) rests can be merged to a single, valid unit
 	isCollapseValid((*nextEvent)->getNotationDuration(), myDuration) &&
	// ...(c) event is in same bar (no cross-bar collapsing)
	(*nextEvent)->getAbsoluteTime() <
	    segment().getBarEndForTime(e->getAbsoluteTime())) {

        // collapse right is OK; collapse e with nextEvent
	Event *e1(new Event(*e, e->getAbsoluteTime(),
			    e->getDuration() + (*nextEvent)->getDuration()));

        collapseForward = true;
	erase(elPos);
        erase(nextEvent);
	insert(e1);
	return true;
    }

    // logic is exactly backwards from collapse to right logic above
    if (previousEvent != end() &&
	isCollapseValid((*previousEvent)->getNotationDuration(), myDuration) &&
	(*previousEvent)->getAbsoluteTime() >
	    segment().getBarStartForTime(e->getAbsoluteTime())) {
			    
        // collapse left is OK; collapse e with previousEvent
	Event *e1(new Event(**previousEvent,
			    (*previousEvent)->getAbsoluteTime(),
			    e->getDuration() +
			    (*previousEvent)->getDuration()));

        collapseForward = false;
        erase(elPos);
	erase(previousEvent);
	insert(e1);
	return true;
    }
    
    return false;
}


bool
SegmentNotationHelper::isCollapseValid(timeT a, timeT b)
{
    return (isViable(a + b));
}


bool
SegmentNotationHelper::isSplitValid(timeT a, timeT b)
{
    return (isViable(a) && isViable(b));
}

Segment::iterator
SegmentNotationHelper::splitIntoTie(iterator &i, timeT baseDuration)
{
    if (i == end()) return end();
    iterator i2;
    segment().getTimeSlice((*i)->getAbsoluteTime(), i, i2);
    return splitIntoTie(i, i2, baseDuration);
}

Segment::iterator
SegmentNotationHelper::splitIntoTie(iterator &from, iterator to,
				    timeT baseDuration)
{
    // so long as we do the quantization checks for validity before
    // calling this method, we should be fine splitting precise times
    // in this method. only problem is deciding not to split something
    // if its duration is very close to requested duration, but that's
    // probably not a task for this function

    timeT eventDuration = (*from)->getDuration();
    timeT baseTime = (*from)->getAbsoluteTime();

    long firstGroupId = -1;
    (*from)->get<Int>(BEAMED_GROUP_ID, firstGroupId);

    long nextGroupId = -1;
    iterator ni(to);

    if (segment().isBeforeEndMarker(ni) && segment().isBeforeEndMarker(++ni)) {
	(*ni)->get<Int>(BEAMED_GROUP_ID, nextGroupId);
    }

    list<Event *> toInsert;
    list<iterator> toErase;
          
    // Split all the note and rest events in range [from, to[
    //
    for (iterator i = from; i != to; ++i) {

	if (!(*i)->isa(Note::EventType) &&
	    !(*i)->isa(Note::EventRestType)) continue;

	if ((*i)->getAbsoluteTime() != baseTime) {
	    // no way to really cope with an error, because at this
	    // point we may already have splut some events. Best to
	    // skip this event
	    cerr << "WARNING: SegmentNotationHelper::splitIntoTie(): (*i)->getAbsoluteTime() != baseTime (" << (*i)->getAbsoluteTime() << " vs " << baseTime << "), ignoring this event\n";
	    continue;
	}

        if ((*i)->getDuration() != eventDuration) {
	    if ((*i)->getDuration() == 0) continue;
	    cerr << "WARNING: SegmentNotationHelper::splitIntoTie(): (*i)->getDuration() != eventDuration (" << (*i)->getDuration() << " vs " << eventDuration << "), changing eventDuration to match\n";
            eventDuration = (*i)->getDuration();
        }

        if (baseDuration >= eventDuration) {
//            cerr << "SegmentNotationHelper::splitIntoTie() : baseDuration >= eventDuration, ignoring event\n";
            continue;
        }

	std::pair<Event *, Event *> split =
	    splitPreservingPerformanceTimes(*i, baseDuration);

	Event *eva = split.first;
	Event *evb = split.second;

	if (!eva || !evb) {
	    cerr << "WARNING: SegmentNotationHelper::splitIntoTie(): No valid split for event of duration " << eventDuration << " at " << baseTime << " (baseDuration " << baseDuration << "), ignoring this event\n";
	    continue;
	}

	// we only want to tie Note events:

	if (eva->isa(Note::EventType)) {

	    // if the first event was already tied forward, the
	    // second one will now be marked as tied forward
	    // (which is good).  set up the relationship between
	    // the original (now shorter) event and the new one.

	    evb->set<Bool>(TIED_BACKWARD, true);
	    eva->set<Bool>(TIED_FORWARD, true);
	}

	// we may also need to change some group information: if
	// the first event is in a beamed group but the event
	// following the insertion is not or is in a different
	// group, then the new second event should not be in a
	// group.  otherwise, it should inherit the grouping info
	// from the first event (as it already does, because it
	// was created using the copy constructor).

	// (this doesn't apply to tupled groups, which we want
	// to persist wherever possible.)

	if (firstGroupId != -1 &&
	    nextGroupId != firstGroupId &&
	    !evb->has(BEAMED_GROUP_TUPLET_BASE)) {
	    evb->unset(BEAMED_GROUP_ID);
	    evb->unset(BEAMED_GROUP_TYPE);
	}

	toInsert.push_back(eva);
        toInsert.push_back(evb);
	toErase.push_back(i);
    }

    // erase the old events
    for (list<iterator>::iterator i = toErase.begin();
	 i != toErase.end(); ++i) {
	segment().erase(*i);
    }

    from = end();
    iterator last = end();

    // now insert the new events
    for (list<Event *>::iterator i = toInsert.begin();
         i != toInsert.end(); ++i) {
        last = insert(*i);
	if (from == end()) from = last;
    }

    return last;
}

bool
SegmentNotationHelper::isViable(timeT duration, int dots)
{
    bool viable;

/*!!!
    duration = basicQuantizer().quantizeDuration(duration);

    if (dots >= 0) {
        viable = (duration == Quantizer(Quantizer::RawEventData,
					Quantizer::DefaultTarget,
					Quantizer::NoteQuantize, 1, dots).
		  quantizeDuration(duration));
    } else {
        viable = (duration == notationQuantizer().quantizeDuration(duration));
    }
*/
    
    //!!! what to do about this?

    timeT nearestDuration =
	Note::getNearestNote(duration, dots >= 0 ? dots : 2).getDuration();

//    std::cerr << "SegmentNotationHelper::isViable: nearestDuration is " << nearestDuration << ", duration is " << duration << std::endl;
    viable = (nearestDuration == duration);

    return viable;
}


void
SegmentNotationHelper::makeRestViable(iterator i)
{
    timeT absTime = (*i)->getAbsoluteTime(); 
    timeT duration = (*i)->getDuration();
    erase(i);
    segment().fillWithRests(absTime, absTime + duration);
}


void
SegmentNotationHelper::makeNotesViable(iterator from, iterator to,
				       bool splitAtBars)
{
    // We don't use quantized values here; we want a precise division.
    // Even if it doesn't look precise on the score (because the score
    // is quantized), we want any playback to produce exactly the same
    // duration of note as was originally recorded

    std::vector<Event *> toInsert;

    for (Segment::iterator i = from, j = i;
	 segment().isBeforeEndMarker(i) && i != to; i = j) {

	++j;

	if (!(*i)->isa(Note::EventType) && !(*i)->isa(Note::EventRestType)) {
	    continue;
	}

	if ((*i)->has(BEAMED_GROUP_TUPLET_BASE)) {
	    continue;
	}

	DurationList dl;

	// Behaviour differs from TimeSignature::getDurationListForInterval

	timeT acc = 0;
	timeT required = (*i)->getNotationDuration();
	
	while (acc < required) {
	    timeT remaining = required - acc;
	    if (splitAtBars) {
		timeT thisNoteStart = (*i)->getNotationAbsoluteTime() + acc;
		timeT toNextBar =
		    segment().getBarEndForTime(thisNoteStart) - thisNoteStart;
		if (toNextBar > 0 && remaining > toNextBar) remaining = toNextBar;
	    }
	    timeT component = Note::getNearestNote(remaining).getDuration();
	    if (component > (required - acc)) dl.push_back(required - acc);
	    else dl.push_back(component);
	    acc += component;
	}

	if (dl.size() < 2) {
	    // event is already of the correct duration
	    continue;
	}
    
	acc = (*i)->getNotationAbsoluteTime();
	Event *e = new Event(*(*i));

	bool lastTiedForward = false;
	e->get<Bool>(TIED_FORWARD, lastTiedForward);
	
	e->set<Bool>(TIED_FORWARD, true);
	erase(i);

	for (DurationList::iterator dli = dl.begin(); dli != dl.end(); ++dli) {

	    DurationList::iterator dlj(dli);
	    if (++dlj == dl.end()) {
		// end of duration list
		if (!lastTiedForward) e->unset(TIED_FORWARD);
		toInsert.push_back(e);
		e = 0;
		break;
	    }
	    
	    std::pair<Event *, Event *> splits =
		splitPreservingPerformanceTimes(e, *dli);
	    
	    if (!splits.first || !splits.second) {
		cerr << "WARNING: SegmentNotationHelper::makeNoteViable(): No valid split for event of duration " << e->getDuration() << " at " << e->getAbsoluteTime() << " (split duration " << *dli << "), ignoring remainder\n";
		cerr << "WARNING: This is probably a bug; fix required" << std::endl;
		toInsert.push_back(e);
		e = 0;
		break;
	    }
	    
	    toInsert.push_back(splits.first);
	    delete e;
	    e = splits.second;
	    
	    acc += *dli;
	    
	    e->set<Bool>(TIED_BACKWARD, true);
	}
	
	delete e;
    }

    for (std::vector<Event *>::iterator ei = toInsert.begin();
	 ei != toInsert.end(); ++ei) {
	insert(*ei);
    }
}

void
SegmentNotationHelper::makeNotesViable(timeT startTime, timeT endTime,
				       bool splitAtBars)
{
    Segment::iterator from = segment().findTime(startTime);
    Segment::iterator to = segment().findTime(endTime);

    makeNotesViable(from, to, splitAtBars);
}


Segment::iterator
SegmentNotationHelper::insertNote(timeT absoluteTime, Note note, int pitch,
				  Accidental explicitAccidental)
{
    Event *e = new Event(Note::EventType, absoluteTime, note.getDuration());
    e->set<Int>(PITCH, pitch);
    e->set<String>(ACCIDENTAL, explicitAccidental);
    iterator i = insertNote(e);
    delete e;
    return i;
}

Segment::iterator
SegmentNotationHelper::insertNote(Event *modelEvent)
{
    timeT absoluteTime = modelEvent->getAbsoluteTime();
    iterator i = segment().findNearestTime(absoluteTime);

    // If our insertion time doesn't match up precisely with any
    // existing event, and if we're inserting over a rest, split the
    // rest at the insertion time first.

    if (i != end() &&
	(*i)->getAbsoluteTime() < absoluteTime &&
	(*i)->getAbsoluteTime() + (*i)->getDuration() > absoluteTime &&
	(*i)->isa(Note::EventRestType)) {
	i = splitIntoTie(i, absoluteTime - (*i)->getAbsoluteTime());
    }

    timeT duration = modelEvent->getDuration();

    if (i != end() && (*i)->has(BEAMED_GROUP_TUPLET_BASE)) {
	duration = duration * (*i)->get<Int>(BEAMED_GROUP_TUPLED_COUNT) /
	    (*i)->get<Int>(BEAMED_GROUP_UNTUPLED_COUNT);
    }

    //!!! Deal with end-of-bar issues!

    return insertSomething(i, duration, modelEvent, false);
}


Segment::iterator
SegmentNotationHelper::insertRest(timeT absoluteTime, Note note)
{
    iterator i, j;
    segment().getTimeSlice(absoluteTime, i, j);

    //!!! Deal with end-of-bar issues!

    timeT duration(note.getDuration());

    if (i != end() && (*i)->has(BEAMED_GROUP_TUPLET_BASE)) {
	duration = duration * (*i)->get<Int>(BEAMED_GROUP_TUPLED_COUNT) /
	    (*i)->get<Int>(BEAMED_GROUP_UNTUPLED_COUNT);
    }

    Event *modelEvent = new Event(Note::EventRestType, absoluteTime,
				  note.getDuration(),
				  Note::EventRestSubOrdering);

    i = insertSomething(i, duration, modelEvent, false);
    delete modelEvent;
    return i;
}


// return an iterator pointing to the "same" event as the original
// iterator (which will have been replaced)

Segment::iterator
SegmentNotationHelper::collapseRestsForInsert(iterator i,
					      timeT desiredDuration)
{
    // collapse at most once, then recurse

    if (!segment().isBeforeEndMarker(i) ||
	!(*i)->isa(Note::EventRestType)) return i;

    timeT d = (*i)->getDuration();
    iterator j = findContiguousNext(i); // won't return itr after end marker
    if (d >= desiredDuration || j == end()) return i;

    Event *e(new Event(**i, (*i)->getAbsoluteTime(), d + (*j)->getDuration()));
    iterator ii(insert(e));
    erase(i);
    erase(j);

    return collapseRestsForInsert(ii, desiredDuration);
}


Segment::iterator
SegmentNotationHelper::insertSomething(iterator i, int duration,
				       Event *modelEvent, bool tiedBack)
{
    // Rules:
    // 
    // 1. If we hit a bar line in the course of the intended inserted
    // note, we should split the note rather than make the bar the
    // wrong length.  (Not implemented yet)
    //
    // 2. If there's nothing at the insertion point but rests (and
    // enough of them to cover the entire duration of the new note),
    // then we should insert the new note/rest literally and remove
    // rests as appropriate.  Rests should never prevent us from
    // inserting what the user asked for.
    // 
    // 3. If there are notes in the way of an inserted note, however,
    // we split whenever "reasonable" and truncate our user's note if
    // not reasonable to split.  We can't always give users the Right
    // Thing here, so to hell with them.

    while (i != end() &&
	   ((*i)->getDuration() == 0 ||
	    !((*i)->isa(Note::EventType) || (*i)->isa(Note::EventRestType))))
	++i;

    if (i == end()) {
	return insertSingleSomething(i, duration, modelEvent, tiedBack);
    }

    // If there's a rest at the insertion position, merge it with any
    // following rests, if available, until we have at least the
    // duration of the new note.
    i = collapseRestsForInsert(i, duration);

    timeT existingDuration = (*i)->getNotationDuration();

//    cerr << "SegmentNotationHelper::insertSomething: asked to insert duration " << duration
//	 << " over event of duration " << existingDuration << ":" << endl;
    (*i)->dump(cerr);

    if (duration == existingDuration) {

        // 1. If the new note or rest is the same length as an
        // existing note or rest at that position, chord the existing
        // note or delete the existing rest and insert.

//	cerr << "Durations match; doing simple insert" << endl;

	return insertSingleSomething(i, duration, modelEvent, tiedBack);

    } else if (duration < existingDuration) {

        // 2. If the new note or rest is shorter than an existing one,
        // split the existing one and chord or replace the first part.

	if ((*i)->isa(Note::EventType)) {

	    if (!isSplitValid(duration, existingDuration - duration)) {

//		cerr << "Bad split, coercing new note" << endl;

		// not reasonable to split existing note, so force new one
		// to same duration instead
		duration = (*i)->getNotationDuration();

	    } else {
//		cerr << "Good split, splitting old event" << endl;
		splitIntoTie(i, duration);
	    }
	} else if ((*i)->isa(Note::EventRestType)) {

//	    cerr << "Found rest, splitting" << endl;
	    iterator last = splitIntoTie(i, duration);

            // Recover viability for the second half of any split rest
	    // (we duck out of this if we find we're in a tupleted zone)

	    if (last != end() && !(*last)->has(BEAMED_GROUP_TUPLET_BASE)) {
		makeRestViable(last);
	    }
	}

	return insertSingleSomething(i, duration, modelEvent, tiedBack);

    } else { // duration > existingDuration

        // 3. If the new note is longer, split the new note so that
        // the first part is the same duration as the existing note or
        // rest, and recurse to step 1 with both the first and the
        // second part in turn.

	bool needToSplit = true;

	// special case: existing event is a rest, and it's at the end
	// of the segment

	if ((*i)->isa(Note::EventRestType)) {
	    iterator j;
	    for (j = i; j != end(); ++j) {
		if ((*j)->isa(Note::EventType)) break;
	    }
	    if (j == end()) needToSplit = false;
	}
	
	if (needToSplit) {

	    //!!! This is not quite right for rests.  Because they
	    //replace (rather than chording with) any events already
	    //present, they don't need to be split in the case where
	    //their duration spans several note-events.  Worry about
	    //that later, I guess.  We're actually getting enough
	    //is-note/is-rest decisions here to make it possibly worth
	    //splitting this method into note and rest versions again

//	    cerr << "Need to split new note" << endl;

	    i = insertSingleSomething
		(i, existingDuration, modelEvent, tiedBack);

	    if (modelEvent->isa(Note::EventType))
		(*i)->set<Bool>(TIED_FORWARD, true);
	    
	    timeT insertedTime = (*i)->getAbsoluteTime();
	    while (i != end() &&
		   ((*i)->getNotationAbsoluteTime() <
		    (insertedTime + existingDuration))) ++i;

	    return insertSomething
		(i, duration - existingDuration, modelEvent, true);

	} else {
//	    cerr << "No need to split new note" << endl;
	    return insertSingleSomething(i, duration, modelEvent, tiedBack);
	}
    }
}

Segment::iterator
SegmentNotationHelper::insertSingleSomething(iterator i, int duration,
					     Event *modelEvent, bool tiedBack)
{
    timeT time;
    timeT notationTime;
    bool eraseI = false;
    timeT effectiveDuration(duration);

    if (i == end()) {
	time = segment().getEndTime();
	notationTime = time;
    } else {
	time = (*i)->getAbsoluteTime();
	notationTime = (*i)->getNotationAbsoluteTime();
	if (modelEvent->isa(Note::EventRestType) ||
	    (*i)->isa(Note::EventRestType)) eraseI = true;
    }

    Event *e = new Event(*modelEvent, time, effectiveDuration,
			 modelEvent->getSubOrdering(), notationTime);

    // If the model event already has group info, I guess we'd better use it!
    if (!e->has(BEAMED_GROUP_ID)) {
	setInsertedNoteGroup(e, i);
    }

    if (tiedBack && e->isa(Note::EventType)) {
        e->set<Bool>(TIED_BACKWARD, true);
    }

    if (eraseI) {
	// erase i and all subsequent events with the same type and
	// absolute time
	timeT time((*i)->getAbsoluteTime());
	std::string type((*i)->getType());
	iterator j(i);
	while (j != end() && (*j)->getAbsoluteTime() == time) {
	    ++j;
	    if ((*i)->isa(type)) erase(i);
	    i = j;
	}
    }

    return insert(e);
}

void
SegmentNotationHelper::setInsertedNoteGroup(Event *e, iterator i)
{
    // Formerly this was posited on the note being inserted between
    // two notes in the same group, but that's quite wrong-headed: we
    // want to place it in the same group as any existing note at the
    // same time, and otherwise leave it alone.

    e->unset(BEAMED_GROUP_ID);
    e->unset(BEAMED_GROUP_TYPE);

    while (isBeforeEndMarker(i) &&
	   (!((*i)->isa(Note::EventRestType)) ||
	    (*i)->has(BEAMED_GROUP_TUPLET_BASE)) &&
	   (*i)->getNotationAbsoluteTime() == e->getAbsoluteTime()) {

	if ((*i)->has(BEAMED_GROUP_ID)) {

	    string type = (*i)->get<String>(BEAMED_GROUP_TYPE);
	    if (type != GROUP_TYPE_TUPLED && !(*i)->isa(Note::EventType)) {
		if ((*i)->isa(Note::EventRestType)) return;
		else {
		    ++i;
		    continue;
		}
	    }

	    e->set<Int>(BEAMED_GROUP_ID, (*i)->get<Int>(BEAMED_GROUP_ID));
	    e->set<String>(BEAMED_GROUP_TYPE, type);

	    if ((*i)->has(BEAMED_GROUP_TUPLET_BASE)) {

		e->set<Int>(BEAMED_GROUP_TUPLET_BASE,
			    (*i)->get<Int>(BEAMED_GROUP_TUPLET_BASE));
		e->set<Int>(BEAMED_GROUP_TUPLED_COUNT,
			    (*i)->get<Int>(BEAMED_GROUP_TUPLED_COUNT));
		e->set<Int>(BEAMED_GROUP_UNTUPLED_COUNT,
			    (*i)->get<Int>(BEAMED_GROUP_UNTUPLED_COUNT));
	    }

	    return;
	}

	++i;
    }
}


Segment::iterator
SegmentNotationHelper::insertClef(timeT absoluteTime, Clef clef)
{
    return insert(clef.getAsEvent(absoluteTime));
}


Segment::iterator
SegmentNotationHelper::insertKey(timeT absoluteTime, Key key)
{
    return insert(key.getAsEvent(absoluteTime));
}


Segment::iterator
SegmentNotationHelper::insertText(timeT absoluteTime, Text text)
{
    return insert(text.getAsEvent(absoluteTime));
}


void
SegmentNotationHelper::deleteNote(Event *e, bool collapseRest)
{
    iterator i = segment().findSingle(e);

    if (i == end()) return;

    if ((*i)->has(TIED_BACKWARD) && (*i)->get<Bool>(TIED_BACKWARD)) {
	iterator j = getPreviousAdjacentNote(i, segment().getStartTime(),
					     true, false);
	if (j != end()) {
	    (*j)->unset(TIED_FORWARD); // don't even check if it has it set
	}
    }	

    if ((*i)->has(TIED_FORWARD) && (*i)->get<Bool>(TIED_FORWARD)) {
	iterator j = getNextAdjacentNote(i, true, false);
	if (j != end()) {
	    (*j)->unset(TIED_BACKWARD); // don't even check if it has it set
	}
    }	

    // If any notes start at the same time as this one but end first,
    // or start after this one starts but before it ends, then we go
    // for the delete-event-and-normalize-rests option.  Otherwise
    // (the notationally simpler case) we go for the
    // replace-note-by-rest option.  We still lose in the case where
    // another note starts before this one, overlaps it, but then also
    // ends before it does -- but I think we can live with that.
    
    iterator j = i;
    timeT endTime = (*i)->getAbsoluteTime() + (*i)->getDuration();

    while (j != end() && (*j)->getAbsoluteTime() < endTime) {
	
	bool complicatedOverlap = false;

	if ((*j)->getAbsoluteTime() != (*i)->getAbsoluteTime()) {
	    complicatedOverlap = true;
	} else if (((*j)->getAbsoluteTime() + (*j)->getDuration()) < endTime) {
	    complicatedOverlap = true;
	}

	if (complicatedOverlap) {
	    timeT startTime = (*i)->getAbsoluteTime();
	    segment().erase(i);
	    segment().normalizeRests(startTime, endTime);
	    return;
	}

	++j;
    }	

    if (noteIsInChord(e)) {

	erase(i);

    } else {

	// replace with a rest
	Event *newRest = new Event(Note::EventRestType,
				   e->getAbsoluteTime(), e->getDuration(),
				   Note::EventRestSubOrdering);
	insert(newRest);
	erase(i);

	// collapse the new rest
        if (collapseRest) {
            bool dummy;
            collapseRestsIfValid(newRest, dummy);
        }

    }
}

bool
SegmentNotationHelper::deleteRest(Event *e)
{
    bool collapseForward;
    return collapseRestsIfValid(e, collapseForward);
}

bool
SegmentNotationHelper::deleteEvent(Event *e, bool collapseRest)
{
    bool res = true;

    if (e->isa(Note::EventType)) deleteNote(e, collapseRest);
    else if (e->isa(Note::EventRestType)) res = deleteRest(e);
    else {
        // just plain delete
        iterator i = segment().findSingle(e);
	if (i != end()) erase(i);
    }

    return res;    
}


bool
SegmentNotationHelper::hasEffectiveDuration(iterator i)
{
    bool hasDuration = ((*i)->getDuration() > 0);

    if ((*i)->isa(Note::EventType)) {
	iterator i0(i);
	if (++i0 != end() &&
	    (*i0)->isa(Note::EventType) &&
	    (*i0)->getNotationAbsoluteTime() == 
	     (*i)->getNotationAbsoluteTime()) {
	    // we're in a chord or something
	    hasDuration = false;
	}
    }
    
    return hasDuration;
}


void
SegmentNotationHelper::makeBeamedGroup(timeT from, timeT to, string type)
{
    makeBeamedGroupAux(segment().findTime(from), segment().findTime(to),
		       type, false);
}

void
SegmentNotationHelper::makeBeamedGroup(iterator from, iterator to, string type)
{
    makeBeamedGroupAux
      ((from == end()) ? from : segment().findTime((*from)->getAbsoluteTime()),
	 (to == end()) ? to   : segment().findTime((*to  )->getAbsoluteTime()),
       type, false);
}

void
SegmentNotationHelper::makeBeamedGroupExact(iterator from, iterator to, string type)
{
    makeBeamedGroupAux(from, to, type, true);
}

void
SegmentNotationHelper::makeBeamedGroupAux(iterator from, iterator to,
					  string type, bool groupGraces)
{
//    cerr << "SegmentNotationHelper::makeBeamedGroupAux: type " << type << endl;
//    if (from == to) cerr << "from == to" <<endl;

    int groupId = segment().getNextId();
    bool beamedSomething = false;

    for (iterator i = from; i != to; ++i) {
//	std::cerr << "looking at " << (*i)->getType() << " at " << (*i)->getAbsoluteTime() << std::endl;

	// don't permit ourselves to change the type of an
	// already-grouped event here
	if ((*i)->has(BEAMED_GROUP_TYPE) &&
	    (*i)->get<String>(BEAMED_GROUP_TYPE) != GROUP_TYPE_BEAMED) {
	    continue;
	}

	if (!groupGraces) {
	    if ((*i)->has(IS_GRACE_NOTE) &&
		(*i)->get<Bool>(IS_GRACE_NOTE)) {
		continue;
	    }
	}

	// don't beam anything longer than a quaver unless it's
	// between beamed quavers -- in which case marking it as
	// beamed will ensure that it gets re-stemmed appropriately

	if ((*i)->isa(Note::EventType) &&
	    (*i)->getNotationDuration() >= Note(Note::Crotchet).getDuration()) {
//	    std::cerr << "too long" <<std::endl;
	    if (!beamedSomething) continue;
	    iterator j = i;
	    bool somethingLeft = false;
	    while (++j != to) {
		if ((*j)->getType() == Note::EventType &&
		    (*j)->getNotationAbsoluteTime() > (*i)->getNotationAbsoluteTime() &&
		    (*j)->getNotationDuration() < Note(Note::Crotchet).getDuration()) {
		    somethingLeft = true;
		    break;
		}
	    }
	    if (!somethingLeft) continue;
	}

//	std::cerr << "beaming it" <<std::endl;
        (*i)->set<Int>(BEAMED_GROUP_ID, groupId);
        (*i)->set<String>(BEAMED_GROUP_TYPE, type);
    }
}

void
SegmentNotationHelper::makeTupletGroup(timeT t, int untupled, int tupled,
				       timeT unit)
{
    int groupId = segment().getNextId();

    cerr << "SegmentNotationHelper::makeTupletGroup: time " << t << ", unit "<< unit << ", params " << untupled << "/" << tupled << ", id " << groupId << endl;

    list<Event *> toInsert;
    list<iterator> toErase;
    timeT notationTime = t;
    timeT fillWithRestsTo = t;
    bool haveStartNotationTime = false;

    for (iterator i = segment().findTime(t); i != end(); ++i) {

	if (!haveStartNotationTime) {
	    notationTime = (*i)->getNotationAbsoluteTime();
	    fillWithRestsTo = notationTime + (untupled * unit);
	    haveStartNotationTime = true;
	}

	if ((*i)->getNotationAbsoluteTime() >=
	    notationTime + (untupled * unit)) break;

	timeT offset = (*i)->getNotationAbsoluteTime() - notationTime;
	timeT duration = (*i)->getNotationDuration();

	if ((*i)->isa(Note::EventRestType) &&
	    ((offset + duration) > (untupled * unit))) {
	    fillWithRestsTo = std::max(fillWithRestsTo,
				       notationTime + offset + duration);
	    duration = (untupled * unit) - offset;
	    if (duration <= 0) {
		toErase.push_back(i);
		continue;
	    }
	}

	Event *e = new Event(**i,
			     notationTime + (offset * tupled / untupled),
			     duration * tupled / untupled);

	cerr << "SegmentNotationHelper::makeTupletGroup: made event at time " << e->getAbsoluteTime() << ", duration " << e->getDuration() << endl;

	e->set<Int>(BEAMED_GROUP_ID, groupId);
	e->set<String>(BEAMED_GROUP_TYPE, GROUP_TYPE_TUPLED);

	e->set<Int>(BEAMED_GROUP_TUPLET_BASE, unit);
	e->set<Int>(BEAMED_GROUP_TUPLED_COUNT, tupled);
	e->set<Int>(BEAMED_GROUP_UNTUPLED_COUNT, untupled);

	toInsert.push_back(e);
	toErase.push_back(i);
    }

    for (list<iterator>::iterator i = toErase.begin();
	 i != toErase.end(); ++i) {
	segment().erase(*i);
    }

    for (list<Event *>::iterator i = toInsert.begin();
	 i != toInsert.end(); ++i) {
	segment().insert(*i);
    }

    if (haveStartNotationTime) {
	segment().fillWithRests(notationTime + (tupled * unit),
				fillWithRestsTo);
    }
}

    


void
SegmentNotationHelper::unbeam(timeT from, timeT to)
{
    unbeamAux(segment().findTime(from), segment().findTime(to));
}

void
SegmentNotationHelper::unbeam(iterator from, iterator to)
{
    unbeamAux
     ((from == end()) ? from : segment().findTime((*from)->getAbsoluteTime()),
        (to == end()) ? to   : segment().findTime((*to  )->getAbsoluteTime()));
}

void
SegmentNotationHelper::unbeamAux(iterator from, iterator to)
{
    for (iterator i = from; i != to; ++i) {
	(*i)->unset(BEAMED_GROUP_ID);
	(*i)->unset(BEAMED_GROUP_TYPE);
	(*i)->clearNonPersistentProperties();
    }
}



/*

  Auto-beaming code derived from Rosegarden 2.1's ItemListAutoBeam
  and ItemListAutoBeamSub in editor/src/ItemList.c.
  
*/

void
SegmentNotationHelper::autoBeam(timeT from, timeT to, string type)
{
    /*
    std::cerr << "autoBeam from " << from << " to " << to << " on segment start time " << segment().getStartTime() << ", end time " << segment().getEndTime() << ", end marker " << segment().getEndMarkerTime() << std::endl;
    */

    autoBeam(segment().findTime(from), segment().findTime(to), type);
}

void
SegmentNotationHelper::autoBeam(iterator from, iterator to, string type)
{
    // This can only manage whole bars at a time, and it will split
    // the from-to range out to encompass the whole bars in which they
    // each occur

    if (!segment().getComposition()) {
	cerr << "WARNING: SegmentNotationHelper::autoBeam requires Segment be in a Composition" << endl;
	return;
    }

    if (!segment().isBeforeEndMarker(from)) return;

    Composition *comp = segment().getComposition();

    int fromBar = comp->getBarNumber((*from)->getAbsoluteTime());
    int toBar = comp->getBarNumber(segment().isBeforeEndMarker(to) ?
				   (*to)->getAbsoluteTime() :
				   segment().getEndMarkerTime());

    for (int barNo = fromBar; barNo <= toBar; ++barNo) {

	std::pair<timeT, timeT> barRange = comp->getBarRange(barNo);
	iterator barStart = segment().findTime(barRange.first);
	iterator barEnd   = segment().findTime(barRange.second);

	// Make sure we're examining the notes defined to be within
	// the bar in notation terms rather than raw terms
	
	while (barStart != segment().end() &&
	       (*barStart)->getNotationAbsoluteTime() < barRange.first) ++barStart;

	iterator scooter = barStart;
	if (barStart != segment().end()) {
	    while (scooter != segment().begin()) {
		--scooter;
		if ((*scooter)->getNotationAbsoluteTime() < barRange.first) break;
		barStart = scooter;
	    }
	}

	while (barEnd != segment().end() &&
	       (*barEnd)->getNotationAbsoluteTime() < barRange.second) ++barEnd;

	scooter = barEnd;
	if (barEnd != segment().end()) {
	    while (scooter != segment().begin()) {
		--scooter;
		if ((*scooter)->getNotationAbsoluteTime() < barRange.second) break;
		barEnd = scooter;
	    }
	}

	TimeSignature timeSig =
	    segment().getComposition()->getTimeSignatureAt(barRange.first);

	autoBeamBar(barStart, barEnd, timeSig, type);
    }
}


/*

  Derived from (and no less mystifying than) Rosegarden 2.1's
  ItemListAutoBeamSub in editor/src/ItemList.c.

  "Today I want to celebrate "Montreal" by Autechre, because of
  its sleep-disturbing aura, because it sounds like the sort of music
  which would be going around in the gunman's head as he trains a laser
  sight into your bedroom through the narrow gap in your curtains and
  dances the little red dot around nervously on your wall."
  
*/

void
SegmentNotationHelper::autoBeamBar(iterator from, iterator to,
				   TimeSignature tsig, string type)
{
    int num = tsig.getNumerator();
    int denom = tsig.getDenominator();

    timeT average;
    timeT minimum = 0;

    // If the denominator is 2 or 4, beam in twos (3/4, 6/2 etc).
    
    if (denom == 2 || denom == 4) {

        if (num % 3) {
            average = Note(Note::Quaver).getDuration();
        } else {
            average = Note(Note::Semiquaver).getDuration();
            minimum = average;
        }

    } else {

        if (num == 6 && denom == 8) { // special hack for 6/8
            average = 3 * Note(Note::Quaver).getDuration();

        } else {
            // find a divisor (at least 2) for the numerator
            int n = 2;
            while (num >= n && num % n != 0) ++n;
            average = n * Note(Note::Semiquaver).getDuration();
        }
    }

    if (minimum == 0) minimum = average / 2;
    if (denom > 4) average /= 2;

    autoBeamBar(from, to, average, minimum, average * 4, type);
}


void
SegmentNotationHelper::autoBeamBar(iterator from, iterator to,
				   timeT average, timeT minimum,
				   timeT maximum, string type)
{
    timeT accumulator = 0;
    timeT crotchet    = Note(Note::Crotchet).getDuration();
    timeT semiquaver  = Note(Note::Semiquaver).getDuration();

    iterator e = end();

    for (iterator i = from; i != to && i != e; ++i) {

        // only look at one note in each chord, and at rests
        if (!hasEffectiveDuration(i)) continue;
        timeT idur = (*i)->getNotationDuration();

	if (accumulator % average == 0 &&  // "beamable duration" threshold
	    idur < crotchet) {

	    // This could be the start of a beamed group.  We maintain
	    // two sorts of state as we scan along here: data about
	    // the best group we've found so far (beamDuration,
	    // prospective, k etc), and data about the items we're
	    // looking at (count, beamable, longerThanDemi etc) just
	    // in case we find a better candidate group before the
	    // eight-line conditional further down makes us give up
	    // the search, beam our best shot, and start again.

	    // I hope this is clear.

	    iterator k = end(); // best-so-far last item in group;
				// end() indicates that we've found nothing

	    timeT tmin         = minimum;
	    timeT count        = 0;
	    timeT prospective  = 0;
	    timeT beamDuration = 0;

	    int beamable       = 0;
	    int longerThanDemi = 0;

	    for (iterator j = i; j != to; ++j) {

		if (!hasEffectiveDuration(j)) continue;
                timeT jdur = (*j)->getNotationDuration();

		if ((*j)->isa(Note::EventType)) {
		    if (jdur < crotchet) ++beamable;
		    if (jdur >= semiquaver) ++longerThanDemi;
		}

		count += jdur;

		if (count % tmin == 0) {

		    k = j;
		    beamDuration = count;
		    prospective = accumulator + count;

		    // found a group; now accept only double this
		    // group's length for a better one
		    tmin *= 2;
		}

		// Stop scanning and make the group if our scan has
		// reached the maximum length of beamed group, we have
		// more than 4 semis or quavers, we're at the end of
		// our run, the next chord is longer than the current
		// one, or there's a rest ahead.  (We used to check
		// that the rest had non-zero duration, but the new
		// quantization regime should ensure that this doesn't
		// happen unless we really are displaying completely
		// unquantized data in which case anything goes.)

		iterator jnext(j);

		if ((count > maximum)
		    || (longerThanDemi > 4)
		    || (++jnext == to)     
		    || ((*j    )->isa(Note::EventType) &&
			(*jnext)->isa(Note::EventType) &&
			(*jnext)->getNotationDuration() > jdur)
		    || ((*jnext)->isa(Note::EventRestType))) {

		    if (k != end() && beamable >= 2) {

			iterator knext(k);
			++knext;

			makeBeamedGroup(i, knext, type);
		    }

		    // If this group is at least as long as the check
		    // threshold ("average"), its length must be a
		    // multiple of the threshold and hence we can
		    // continue scanning from the end of the group
		    // without losing the modulo properties of the
		    // accumulator.

		    if (k != end() && beamDuration >= average) {

			i = k;
			accumulator = prospective;

		    } else {

			// Otherwise, we continue from where we were.
			// (This must be safe because we can't get
			// another group starting half-way through, as
			// we know the last group is shorter than the
			// check threshold.)

			accumulator += idur;
		    }

		    break;
		}
	    }
	} else {

	    accumulator += idur;
	}
    }
}


// based on Rosegarden 2.1's GuessItemListClef in editor/src/MidiIn.c

Clef
SegmentNotationHelper::guessClef(iterator from, iterator to)
{
    long totalHeight = 0;
    int noteCount = 0;

    // just the defaults:
    Clef clef;
    Key key;

    for (iterator i = from; i != to; ++i) {
        if ((*i)->isa(Note::EventType)) {
//!!!            NotationDisplayPitch p((*i)->get<Int>(PITCH), clef, key);
	    try {
		Pitch p(**i);
		totalHeight += p.getHeightOnStaff(clef, key);
		++noteCount;
	    } catch (Exception e) {
		// no pitch in note
	    }
        }
    }

    if    (noteCount == 0) return Clef(Clef::Treble);

    int average = totalHeight / noteCount;

    if      (average < -6) return Clef(Clef::Bass);
    else if (average < -3) return Clef(Clef::Tenor);
    else if (average <  1) return Clef(Clef::Alto);
    else                   return Clef(Clef::Treble);
}


bool
SegmentNotationHelper::removeRests(timeT time, timeT &duration, bool testOnly)
{
    Event dummy("dummy", time, 0, MIN_SUBORDERING);
    
    std::cerr << "SegmentNotationHelper::removeRests(" << time
	      << ", " << duration << ")" << std::endl;

    iterator from = segment().lower_bound(&dummy);

    // ignore any number of zero-duration events at the start
    while (from != segment().end() &&
	   (*from)->getAbsoluteTime() == time &&
	   (*from)->getDuration() == 0) ++from;
    if (from == segment().end()) return false;
    
    iterator to = from;

    timeT eventTime = time;
    timeT finalTime = time + duration;

    //!!! We should probably not use an accumulator, but instead
    // calculate based on each event's absolute time + duration --
    // in case we've somehow ended up with overlapping rests

    // Iterate on events, checking if all are rests
    //
    while ((eventTime < finalTime) && (to != end())) {

        if (!(*to)->isa(Note::EventRestType)) {
            // a non-rest was found
	    duration = (*to)->getAbsoluteTime() - time;
            return false;
        }

        timeT nextEventDuration = (*to)->getDuration();

        if ((eventTime + nextEventDuration) <= finalTime) {
            eventTime += nextEventDuration;
	    duration = eventTime - time;
	} else break;

        ++to;
    }

    bool checkLastRest = false;
    iterator lastEvent = to;
    
    if (eventTime < finalTime) {
        // shorten last event's duration, if possible


        if (lastEvent == end()) {
	    duration = segment().getEndTime() - time;
            return false;
        }

	if (!testOnly) {
	    // can't safely change the absolute time of an event in a segment
	    Event *newEvent = new Event(**lastEvent, finalTime,
					(*lastEvent)->getDuration() -
					(finalTime - eventTime));
	    duration = finalTime + (*lastEvent)->getDuration() - time;
	    bool same = (from == to);
	    segment().erase(lastEvent);
	    to = lastEvent = segment().insert(newEvent);
	    if (same) from = to;
	    checkLastRest = true;
	}
    }

    if (testOnly) return true;

    segment().erase(from, to);

    // we must defer calling makeRestViable() until after erase,
    // because it will invalidate 'to'
    //
    if (checkLastRest) makeRestViable(lastEvent);

    return true;
}


void
SegmentNotationHelper::collapseRestsAggressively(timeT startTime,
						 timeT endTime)
{
    reorganizeRests(startTime, endTime,
		    &SegmentNotationHelper::mergeContiguousRests);
}


void
SegmentNotationHelper::reorganizeRests(timeT startTime, timeT endTime,
				       Reorganizer reorganizer)
{
    iterator ia = segment().findTime(startTime);
    iterator ib = segment().findTime(endTime);
    
    if (ia == end()) return;

    std::vector<iterator> erasable;
    std::vector<Event *> insertable;

//    cerr << "SegmentNotationHelper::reorganizeRests (" << startTime << ","
//	 << endTime << ")" << endl;

    for (iterator i = ia; i != ib; ++i) {

	if ((*i)->isa(Note::EventRestType)) {

	    timeT startTime = (*i)->getAbsoluteTime();
	    timeT duration = 0;
	    iterator j = i;

	    for ( ; j != ib; ++j) {

		if ((*j)->isa(Note::EventRestType)) {
		    duration += (*j)->getDuration();
		    erasable.push_back(j);
		} else break;
	    }

	    (this->*reorganizer)(startTime, duration, insertable);
	    if (j == ib) break;
	    i = j;
	}
    }

    for (unsigned int ei = 0; ei < erasable.size(); ++ei)
	segment().erase(erasable[ei]);

    for (unsigned int ii = 0; ii < insertable.size(); ++ii)
	segment().insert(insertable[ii]);
}


void
SegmentNotationHelper::normalizeContiguousRests(timeT startTime,
						timeT duration,
						std::vector<Event *> &toInsert)
{
    TimeSignature ts;
    timeT sigTime =
	segment().getComposition()->getTimeSignatureAt(startTime, ts);

//    cerr << "SegmentNotationHelper::normalizeContiguousRests:"
//	 << " startTime = " << startTime << ", duration = "
//	 << duration << endl;

    DurationList dl;
    ts.getDurationListForInterval(dl, duration, startTime - sigTime);

    timeT acc = startTime;

    for (DurationList::iterator i = dl.begin(); i != dl.end(); ++i) {
	Event *e = new Event(Note::EventRestType, acc, *i,
			     Note::EventRestSubOrdering);
	toInsert.push_back(e);
	acc += *i;
    }
}


void
SegmentNotationHelper::mergeContiguousRests(timeT startTime,
					    timeT duration,
					    std::vector<Event *> &toInsert)
{
    while (duration > 0) {

	timeT d = Note::getNearestNote(duration).getDuration();

	Event *e = new Event(Note::EventRestType, startTime, d,
			     Note::EventRestSubOrdering);
	toInsert.push_back(e);

	startTime += d;
	duration -= d;
    }
}


Segment::iterator
SegmentNotationHelper::collapseNoteAggressively(Event *note,
						timeT rangeEnd)
{
    iterator i = segment().findSingle(note);
    if (i == end()) return end();

    iterator j = getNextAdjacentNote(i, true, true);
    if (j == end() || (*j)->getAbsoluteTime() >= rangeEnd) return end();

    timeT iEnd = (*i)->getAbsoluteTime() + (*i)->getDuration();
    timeT jEnd = (*j)->getAbsoluteTime() + (*j)->getDuration();

    Event *newEvent = new Event
	(**i, (*i)->getAbsoluteTime(),
	 (std::max(iEnd, jEnd) - (*i)->getAbsoluteTime()));

    newEvent->unset(TIED_BACKWARD);
    newEvent->unset(TIED_FORWARD);

    segment().erase(i);
    segment().erase(j);
    return segment().insert(newEvent);
}

std::pair<Event *, Event *>
SegmentNotationHelper::splitPreservingPerformanceTimes(Event *e, timeT q1)
{
    timeT ut = e->getAbsoluteTime();
    timeT ud = e->getDuration();
    timeT qt = e->getNotationAbsoluteTime();
    timeT qd = e->getNotationDuration();

    timeT u1 = (qt + q1) - ut;
    timeT u2 = (ut + ud) - (qt + q1);

//    std::cerr << "splitPreservingPerformanceTimes: (ut,ud) (" << ut << "," << ud << "), (qt,qd) (" << qt << "," << qd << ") q1 " << q1 << ", u1 " << u1 << ", u2 " << u2 << std::endl;

    if (u1 <= 0 || u2 <= 0) { // can't do a meaningful split
	return std::pair<Event *, Event *>(0, 0);
    }

    Event *e1 = new Event(*e, ut, u1, e->getSubOrdering(), qt, q1);
    Event *e2 = new Event(*e, ut + u1, u2, e->getSubOrdering(), qt + q1, qd - q1);

    e1->set<Bool>(TIED_FORWARD, true);
    e2->set<Bool>(TIED_BACKWARD, true);

    return std::pair<Event *, Event *>(e1, e2);
}

void
SegmentNotationHelper::deCounterpoint(timeT startTime, timeT endTime)
{
    // How this should work: scan through the range and, for each
    // note "n" found, if the next following note "m" not at the same
    // absolute time as n starts before n ends, then split n at m-n.

    // also, if m starts at the same time as n but has a different
    // duration, we should split the longer of n and m at the shorter
    // one's duration.

    for (Segment::iterator i = segment().findTime(startTime);
	 segment().isBeforeEndMarker(i); ) {

	timeT t = (*i)->getAbsoluteTime();
	if (t >= endTime) break;

#ifdef DEBUG_DECOUNTERPOINT
	std::cerr << "SegmentNotationHelper::deCounterpoint: event at " << (*i)->getAbsoluteTime() << " notation " << (*i)->getNotationAbsoluteTime() << ", duration " << (*i)->getNotationDuration() << ", type " << (*i)->getType() << std::endl;
#endif	    

	if (!(*i)->isa(Note::EventType)) { ++i; continue; }

	timeT ti = (*i)->getNotationAbsoluteTime();
	timeT di = (*i)->getNotationDuration();

#ifdef DEBUG_DECOUNTERPOINT
	std::cerr<<"looking for k"<<std::endl;
#endif	    

	// find next event that's either at a different time or (if a
	// note) has a different duration
	Segment::iterator k = i;
	while (segment().isBeforeEndMarker(k)) {
	    if ((*k)->isa(Note::EventType)) {
#ifdef DEBUG_DECOUNTERPOINT
		std::cerr<<"abstime "<<(*k)->getAbsoluteTime()<< std::endl;
#endif	    
		if ((*k)->getNotationAbsoluteTime() > ti ||
		    (*k)->getNotationDuration() != di) break;
	    }
	    ++k;
	}

	if (!segment().isBeforeEndMarker(k)) break; // no split, no more notes

#ifdef DEBUG_DECOUNTERPOINT
	std::cerr << "k is at " << (k == segment().end() ? -1 : (*k)->getAbsoluteTime()) << ", notation " << (*k)->getNotationAbsoluteTime() << ", duration " << (*k)->getNotationDuration() << std::endl;
#endif	    

	timeT tk = (*k)->getNotationAbsoluteTime();
	timeT dk = (*k)->getNotationDuration();

	Event *e1 = 0, *e2 = 0;
	std::pair<Event *, Event *> splits;
	Segment::iterator toGo = segment().end();

	if (tk == ti && dk != di) {
	    // do the same-time-different-durations case
	    if (di > dk) { // split *i
#ifdef DEBUG_DECOUNTERPOINT
		std::cerr << "splitting i into " << dk << " and "<< (di-dk) << std::endl;
#endif	    
		splits = splitPreservingPerformanceTimes(*i, dk);

		toGo = i;
	    } else { // split *k
#ifdef DEBUG_DECOUNTERPOINT
		std::cerr << "splitting k into " << di << " and "<< (dk-di) << std::endl;
#endif	    
		splits = splitPreservingPerformanceTimes(*k, di);

		toGo = k;
	    }
	} else if (tk - ti > 0 && tk - ti < di) { // split *i
#ifdef DEBUG_DECOUNTERPOINT
	    std::cerr << "splitting i[*] into " << (tk-ti) << " and "<< (di-(tk-ti)) << std::endl;
#endif	    
	    splits = splitPreservingPerformanceTimes(*i, tk - ti);

	    toGo = i;
	}
	
	e1 = splits.first;
	e2 = splits.second;

	if (e1 && e2) { // e2 is the new note

	    e1->set<Bool>(TIED_FORWARD, true);
	    e2->set<Bool>(TIED_BACKWARD, true);

#ifdef DEBUG_DECOUNTERPOINT
	    std::cerr<<"Erasing:"<<std::endl;
	    (*toGo)->dump(std::cerr);
#endif	    

	    segment().erase(toGo);

#ifdef DEBUG_DECOUNTERPOINT
	    std::cerr<<"Inserting:"<<std::endl;
	    e1->dump(std::cerr);
#endif	    

	    segment().insert(e1);

#ifdef DEBUG_DECOUNTERPOINT
	    std::cerr<<"Inserting:"<<std::endl;
	    e2->dump(std::cerr);
#endif	    

	    segment().insert(e2);

	    i = segment().findTime(t);

#ifdef DEBUG_DECOUNTERPOINT
	    std::cerr<<"resync at " << t << ":" << std::endl;
	    if (i != segment().end()) (*i)->dump(std::cerr);
	    else std::cerr << "(end)" << std::endl;
#endif	    

	} else {

	    // no split here

#ifdef DEBUG_DECOUNTERPOINT
	    std::cerr<<"no split"<<std::endl;
#endif	    
	    ++i;
	}
    }

    segment().normalizeRests(startTime, endTime);
}


void
SegmentNotationHelper::autoSlur(timeT startTime, timeT endTime, bool legatoOnly)
{
    iterator from = segment().findTime(startTime);
    iterator to = segment().findTime(endTime);

    timeT potentialStart = segment().getEndTime();
    long  groupId = -1;
    timeT prevTime = startTime;
    int   count = 0;
    bool  thisLegato = false, prevLegato = false;

    for (iterator i = from; i != to && segment().isBeforeEndMarker(i); ++i) {

	timeT t = (*i)->getNotationAbsoluteTime();

	long newGroupId = -1;
	if ((*i)->get<Int>(BEAMED_GROUP_ID, newGroupId)) {
	    if (groupId == newGroupId) { // group continuing
		if (t > prevTime) {
		    ++count;
		    prevLegato = thisLegato;
		    thisLegato = Marks::hasMark(**i, Marks::Tenuto);
		}
		prevTime = t;
		continue;
	    }
	} else {
	    if (groupId == -1) continue; // no group
	}

	// a group has ended (and a new one might have begun)

	if (groupId >= 0 && count > 1 && (!legatoOnly || prevLegato)) {
	    Indication ind(Indication::Slur, t - potentialStart);
	    segment().insert(ind.getAsEvent(potentialStart));
	    if (legatoOnly) {
		for (iterator j = segment().findTime(potentialStart); j != i; ++j) {
		    Marks::removeMark(**j, Marks::Tenuto);
		}
	    }
	}

	potentialStart = t;
	groupId = newGroupId;
	prevTime = t;
	count = 0;
	thisLegato = false;
	prevLegato = false;
    }

    if (groupId >= 0 && count > 1 && (!legatoOnly || prevLegato)) {
	Indication ind(Indication::Slur, endTime - potentialStart);
	segment().insert(ind.getAsEvent(potentialStart));
	if (legatoOnly) {
	    for (iterator j = segment().findTime(potentialStart);
		 segment().isBeforeEndMarker(j) && j != to; ++j) {
		Marks::removeMark(**j, Marks::Tenuto);
	    }
	}
    }
}


} // end of namespace

