/*
    Copyright (C) 2017 Paul Davis

    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation; either version 2 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program; if not, write to the Free Software
    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/

#pragma once

#include <list>
#include <string>
#include <vector>
#include <cmath>
#include <exception>
#include <unordered_map>

#include <boost/intrusive/list.hpp>

#include "pbd/command.h"
#include "pbd/enum_convert.h"
#include "pbd/integer_division.h"
#include "pbd/memento_command.h"
#include "pbd/rcu.h"
#include "pbd/signals.h"
#include "pbd/statefuldestructible.h"

#include "temporal/visibility.h"
#include "temporal/beats.h"
#include "temporal/bbt_argument.h"
#include "temporal/bbt_time.h"
#include "temporal/domain_swap.h"
#include "temporal/superclock.h"
#include "temporal/timeline.h"
#include "temporal/types.h"

/* A tempo map is built from 3 types of entities

   1) tempo markers
   2) meter (time signature) markers
   3) position markers

   Beats increase monotonically throughout the tempo map (BBT may not).

   The map has a single time domain at any time.
*/

namespace Temporal {

class Meter;
class TempoMap;
class TempoMapCutBuffer;
class ScopedTempoMapOwner;

class MapOwned {
 protected:
	MapOwned (TempoMap const & map) : _map (&map) {}
	virtual ~MapOwned () {}

 public:
	TempoMap const & map() const { return *_map; }

 protected:
	friend class TempoMap;
	void set_map (TempoMap const & map) { _map = &map; }
	TempoMap const * _map;
};

/* Conceptually, Point is similar to timepos_t. However, whereas timepos_t can
 * use the TempoMap to translate between time domains, Point cannot. Why not?
 * Because Point is foundational in building the tempo map, and we cannot
 * create a circular functional dependency between them. So a Point always has
 * its superclock and beat time defined and no translation between them is possible.
 */

typedef boost::intrusive::list_base_hook<boost::intrusive::tag<struct point_tag>> point_hook;
class /*LIBTEMPORAL_API*/ Point : public point_hook, public MapOwned  {
  public:
	LIBTEMPORAL_API Point (TempoMap const & map, superclock_t sc, Beats const & b, BBT_Time const & bbt) : MapOwned (map), _sclock (sc), _quarters (b), _bbt (bbt)  {}
	LIBTEMPORAL_API Point (TempoMap const & map, XMLNode const &);

	LIBTEMPORAL_API virtual ~Point() {}

	LIBTEMPORAL_API void set (superclock_t sc, Beats const & b, BBT_Time const & bbt) {
		_sclock = sc;
		_quarters = b;
		_bbt = bbt;
	}

	LIBTEMPORAL_API superclock_t sclock() const  { return _sclock; }
	LIBTEMPORAL_API Beats const & beats() const  { return _quarters; }
	LIBTEMPORAL_API BBT_Time const & bbt() const { return _bbt; }
	LIBTEMPORAL_API samplepos_t sample_is_dangerous (int sr) const { return superclock_to_samples (sclock(), sr); }

	LIBTEMPORAL_API virtual timepos_t time() const = 0;

	struct LIBTEMPORAL_API sclock_comparator {
		bool operator() (Point const & a, Point const & b) const {
			return a.sclock() < b.sclock();
		}
		bool operator() (Point const & a, superclock_t sc) const {
			return a.sclock() < sc;
		}
	};

	struct LIBTEMPORAL_API ptr_sclock_comparator {
		bool operator() (Point const * a, Point const * b) const {
			return a->sclock() < b->sclock();
		}
	};

	struct LIBTEMPORAL_API beat_comparator {
		bool operator() (Point const & a, Point const & b) const {
			return a.beats() < b.beats();
		}
		bool operator() (Point const & a, Beats const & beats) const {
			return a.beats() < beats;
		}
	};

	struct LIBTEMPORAL_API bbt_comparator {
		bool operator() (Point const & a, Point const & b) const {
			return a.bbt() < b.bbt();
		}
		bool operator() (Point const & a, BBT_Time const & bbt) const {
			return a.bbt() < bbt;
		}
	};

	/* all time members are supposed to be synced at all times, so we need
	   test only one.
	*/
	LIBTEMPORAL_API inline bool operator== (Point const & other) const { return _sclock == other._sclock; }
	LIBTEMPORAL_API inline bool operator!= (Point const & other) const { return _sclock != other._sclock; }

  protected:
	superclock_t     _sclock;
	Beats            _quarters;
	BBT_Time         _bbt;

	void add_state (XMLNode &) const;

  protected:
	friend class TempoMap;
	void map_reset_set_sclock_for_sr_change (superclock_t sc) { _sclock = sc; }
};

/** Tempo, the speed at which musical time progresses (BPM).
 */

class LIBTEMPORAL_API Tempo {
  public:
	enum Type {
		Ramped,
		Constant
	};

	static std::string xml_node_name;

	Tempo (XMLNode const &);
	virtual ~Tempo () {}

	/**
	 * @param npm Note Types per minute
	 * @param note_type Note Type (default `4': quarter note)
	 */
	Tempo (double npm, uint8_t note_type)
		: _npm (npm)
		, _enpm (npm)
		, _superclocks_per_note_type (double_npm_to_scpn (npm))
		, _end_superclocks_per_note_type (double_npm_to_scpn (npm))
		, _note_type (note_type)
		, _locked_to_meter (false)
		, _continuing (false)
		{}

	Tempo (double npm, double enpm, int8_t note_type)
		: _npm (npm)
		, _enpm (npm)
		, _superclocks_per_note_type (double_npm_to_scpn (npm))
		, _end_superclocks_per_note_type (double_npm_to_scpn (enpm))
		, _note_type (note_type)
		, _locked_to_meter (false)
		, _continuing (false)
		{}

	/* these five methods should only be used to show and collect information to the user (for whom
	 * bpm as a floating point number is the obvious representation)
	 */
	double note_types_per_minute () const { return ((double) superclock_ticks_per_second() * 60.0) / (double) _superclocks_per_note_type; }
	double end_note_types_per_minute () const { return ((double) superclock_ticks_per_second() * 60.0) / (double) _end_superclocks_per_note_type; }
	double quarter_notes_per_minute() const { return ((double) superclock_ticks_per_second() * 60.0 * 4.0) / (_note_type * (double) _superclocks_per_note_type); }
	double samples_per_note_type(int sr) const { return superclock_to_samples (superclocks_per_note_type (), sr); }
	double samples_per_quarter_note(int sr) const { return superclock_to_samples (superclocks_per_quarter_note(), sr); }

	void   set_note_types_per_minute (double npm);

	int note_type () const { return _note_type; }

	superclock_t superclocks_per_note_type () const {
		return _superclocks_per_note_type;
	}
	superclock_t superclocks_per_note_type (int note_type) const {
		return (_superclocks_per_note_type * _note_type) / note_type;
	}
	superclock_t superclocks_per_quarter_note () const {
		return superclocks_per_note_type (4);
	}
	superclock_t end_superclocks_per_note_type () const {
		return _end_superclocks_per_note_type;
	}
	superclock_t end_superclocks_per_note_type (int note_type) const {
		return (_end_superclocks_per_note_type * _note_type) / note_type;
	}
	superclock_t end_superclocks_per_quarter_note () const {
		return end_superclocks_per_note_type (4);
	}

	bool locked_to_meter ()  const { return _locked_to_meter; }
	void set_locked_to_meter (bool yn) { _locked_to_meter = yn; }

	bool continuing() const { return _continuing; }
	void set_continuing (bool yn);

	Type type() const { return _superclocks_per_note_type == _end_superclocks_per_note_type ? Constant : Ramped; }
	bool ramped () const { return _superclocks_per_note_type != _end_superclocks_per_note_type; }

	XMLNode& get_state () const;
	int set_state (XMLNode const&, int version);

	bool operator== (Tempo const & other) const {
		return _superclocks_per_note_type == other._superclocks_per_note_type &&
		_end_superclocks_per_note_type == other._end_superclocks_per_note_type &&
		_note_type == other._note_type &&
		_locked_to_meter == other._locked_to_meter &&
		_continuing == other._continuing;
	}

	bool operator!= (Tempo const & other) const {
		return _superclocks_per_note_type != other._superclocks_per_note_type ||
			_end_superclocks_per_note_type != other._end_superclocks_per_note_type ||
			_note_type != other._note_type ||
			_locked_to_meter != other._locked_to_meter ||
			_continuing != other._continuing;
	}

  protected:
	double       _npm;
	double       _enpm;
	superclock_t _superclocks_per_note_type;
	superclock_t _end_superclocks_per_note_type;
	uint8_t      _note_type;
	bool         _locked_to_meter; /* XXX name has unclear meaning with nutempo */
	bool         _continuing; /* true if our effective end tempo is defined
	                           * by the following tempo in the TempoMap;
	                           * false if we use our own end tempo. */

	static inline superclock_t double_npm_to_scpn (double npm) { return (superclock_t) llround ((60./npm) * superclock_ticks_per_second()); }

  protected:
	friend class TempoMap;
	void set_end_npm (double);
};

/** Meter, or time signature (subdivisions per bar, and which note type is a single subdivision). */
class LIBTEMPORAL_API Meter {
  public:

	static std::string xml_node_name;

	Meter (XMLNode const &);
	Meter (int8_t dpb, int8_t nv) : _note_value (nv), _divisions_per_bar (dpb) {}
	Meter (Meter const & other) : _note_value (other._note_value), _divisions_per_bar (other._divisions_per_bar) {}

	virtual ~Meter () {}

	int divisions_per_bar () const { return _divisions_per_bar; }
	int note_value() const { return _note_value; }

	int32_t ticks_per_grid () const { return (4 * Beats::PPQN) / _note_value; }

	inline bool operator==(const Meter& other) const { return _divisions_per_bar == other.divisions_per_bar() && _note_value == other.note_value(); }
	inline bool operator!=(const Meter& other) const { return _divisions_per_bar != other.divisions_per_bar() || _note_value != other.note_value(); }

	Meter& operator=(Meter const & other) {
		if (&other != this) {
			_divisions_per_bar = other._divisions_per_bar;
			_note_value = other._note_value;
		}
		return *this;
	}

	BBT_Offset bbt_delta (BBT_Time const & later, BBT_Time const &earlier) const;
	BBT_Time bbt_add (BBT_Time const & bbt, BBT_Offset const & add) const;
	BBT_Time bbt_subtract (BBT_Time const & bbt, BBT_Offset const & sub) const;
	BBT_Time round_to_bar (BBT_Time const &) const;
	BBT_Time round_up_to_bar (BBT_Time const &) const;
	BBT_Time round_up_to_beat_div (BBT_Time const &, int beat_div) const;
	BBT_Time round_up_to_beat (BBT_Time const & bbt) const { return round_up_to_beat_div (bbt, 1); }
	BBT_Time round_to_beat (BBT_Time const &) const;
	Beats    to_quarters (BBT_Offset const &) const;

	Beats round_to_beat (Beats const &) const;

	XMLNode& get_state () const;
	int set_state (XMLNode const&, int version);

  protected:
	/** The type of "note" that a division represents.  For example, 4 is
	    a quarter (crotchet) note, 8 is an eighth (quaver) note, etc.
	*/
	int8_t _note_value;
	/* how many of '_note_value' make up a bar or measure */
	int8_t _divisions_per_bar;
};

/* A MeterPoint is literally just the combination of a Meter with a Point
 */
typedef boost::intrusive::list_base_hook<boost::intrusive::tag<struct meterpoint_tag>> meter_hook;
class /*LIBTEMPORAL_API*/ MeterPoint : public Meter, public meter_hook, public virtual Point
{
  public:
	LIBTEMPORAL_API MeterPoint (TempoMap const & map, Meter const & m, superclock_t sc, Beats const & b, BBT_Time const & bbt) : Point (map, sc, b, bbt), Meter (m) {}
	LIBTEMPORAL_API MeterPoint (TempoMap const & map, XMLNode const &);
	LIBTEMPORAL_API MeterPoint (Meter const & m, Point const & p) : Point (p), Meter (m) {}

	virtual ~MeterPoint () {}

	LIBTEMPORAL_API Beats quarters_at (BBT_Time const & bbt) const;
	LIBTEMPORAL_API BBT_Time bbt_at (Beats const & beats) const;

	LIBTEMPORAL_API bool operator== (MeterPoint const & other) const {
		return Meter::operator== (other) && Point::operator== (other);
	}
	LIBTEMPORAL_API bool operator!= (MeterPoint const & other) const {
		return Meter::operator!= (other) || Point::operator!= (other);
	}

	LIBTEMPORAL_API XMLNode& get_state () const;

	LIBTEMPORAL_API timepos_t time() const { return timepos_t (beats()); }
};

/* A TempoPoint is a combination of a Tempo with a Point. However, if the temp
 * is ramped, then at some point we will need to compute the ramp coefficient
 * (_omega) and store it so that we can compute tempo-at-time and
 * time-at-quarter-note on demand.
 */

typedef boost::intrusive::list_base_hook<boost::intrusive::tag<struct tempo_tag>> tempo_hook;
class /*LIBTEMPORAL_API*/ TempoPoint : public Tempo, public tempo_hook, public virtual Point
{
  public:
	LIBTEMPORAL_API TempoPoint (TempoMap const & map, Tempo const & t, superclock_t sc, Beats const & b, BBT_Time const & bbt) : Point (map, sc, b, bbt), Tempo (t), _omega (0.)  {}
	LIBTEMPORAL_API TempoPoint (Tempo const & t, Point const & p) : Point (p), Tempo (t), _omega (0.) {}
	LIBTEMPORAL_API TempoPoint (TempoMap const & map, XMLNode const &);

	virtual ~TempoPoint () {}

	/* just change the tempo component, without moving */
	LIBTEMPORAL_API TempoPoint& operator=(Tempo const & t) {
		*((Tempo*)this) = t;
		return *this;
	}

	/* Given that this tempo point controls tempo for the time indicated by
	 * the argument of the following 3 functions, return information about
	 * that time. The first 3 return convert between domains (with
	 * ::sample_at() just being a convenience function); the third returns
	 * information about the tempo at that time.
	 */

	LIBTEMPORAL_API superclock_t superclock_at (Beats const & qn) const;
	LIBTEMPORAL_API samplepos_t  sample_at (Beats const & qn) const { return Temporal::superclock_to_samples (superclock_at (qn), TEMPORAL_SAMPLE_RATE); }
	LIBTEMPORAL_API superclock_t superclocks_per_note_type_at (timepos_t const &) const;

	/* XXX at some point, we have had discussions about representing tempo
	 * as a rational number rather than a double. We have not reached that
	 * point yet (Nov 2021), and so at this point, this method is the
	 * canonical way to get "bpm at position" from  a TempoPoint object.
	 */

	LIBTEMPORAL_API double note_types_per_minute_at_DOUBLE (timepos_t const & pos) const {
		return (superclock_ticks_per_second() * 60.0) / superclocks_per_note_type_at (pos);
	}

	LIBTEMPORAL_API double omega() const { return _omega; }

	LIBTEMPORAL_API void compute_omega_from_next_tempo (TempoPoint const & next_tempo);
	LIBTEMPORAL_API void compute_omega_from_distance_and_next_tempo (Beats const & quarter_duration, TempoPoint const & next_tempo);
	LIBTEMPORAL_API void compute_omega_from_quarter_duration (Beats const & quarter_duration, superclock_t end_scpqn);

	LIBTEMPORAL_API bool actually_ramped () const { return Tempo::ramped() && ( _omega != 0); /* do not need to check both omegas */ }

	LIBTEMPORAL_API XMLNode& get_state () const;
	LIBTEMPORAL_API int set_state (XMLNode const&, int version);

	LIBTEMPORAL_API bool operator== (TempoPoint const & other) const {
		return Tempo::operator== (other) && Point::operator== (other);
	}
	LIBTEMPORAL_API bool operator!= (TempoPoint const & other) const {
		return Tempo::operator!= (other) || Point::operator!= (other);
	}

	LIBTEMPORAL_API Beats quarters_at_sample (samplepos_t sc) const { return quarters_at_superclock (samples_to_superclock (sc, TEMPORAL_SAMPLE_RATE)); }
	LIBTEMPORAL_API Beats quarters_at_superclock (superclock_t sc) const;

	LIBTEMPORAL_API timepos_t time() const { return timepos_t (beats()); }

  private:
	double _omega;

	friend TempoMap;
	void set_omega (double v);
};

/** Helper class to perform computations that require both Tempo and Meter
    at a given point in time.

    It may seem nicer to make this IS-A TempoPoint and IS-A MeterPoint. Doing
    so runs into multiple inheritance of Point, plus the major semantic issue
    that pairing a tempo and a meter does in fact allow for two positions, not
    one. That means we have to provide accessors to the TempoPoint and
    MeterPoint and thus it may as well be HAS-A rather than IS-A.

    This object should always be short lived. It holds references to a
    TempoPoint and a MeterPoint that are not lifetime-managed. It's just a
    convenience object, in essence, to avoid having to replicate the
    computation code that requires both tempo and meter information every place
    it is used.
*/
class LIBTEMPORAL_API TempoMetric
{
  public:
	TempoMetric (TempoPoint const & t, MeterPoint const & m);
	virtual ~TempoMetric () {}

	superclock_t reftime() const { return _reftime; }

	TempoPoint const & tempo() const { return *_tempo; }
	MeterPoint const & meter() const { return *_meter; }

	TempoPoint & get_editable_tempo() const { return *const_cast<TempoPoint*> (_tempo); }
	MeterPoint & get_editable_meter() const { return *const_cast<MeterPoint*> (_meter); }

	/* even more convenient wrappers for individual aspects of a
	 * TempoMetric (i.e. just tempo or just meter information required
	 */

	superclock_t superclock_at (Beats const & qn) const { return _tempo->superclock_at (qn); }
	samplepos_t  sample_at (Beats const & qn) const { return _tempo->sample_at (qn); }
	Beats        quarters_at (BBT_Time const & bbt) const { return _meter->quarters_at (bbt); }
	BBT_Argument     bbt_at (Beats const & beats) const { return BBT_Argument (reftime(), _meter->bbt_at (beats)); }

	superclock_t superclocks_per_note_type () const { return _tempo->superclocks_per_note_type (); }
	superclock_t end_superclocks_per_note_type () const {return _tempo->end_superclocks_per_note_type (); }
	superclock_t superclocks_per_note_type (int note_type) const {return _tempo->superclocks_per_note_type (note_type); }
	superclock_t superclocks_per_quarter_note () const {return _tempo->superclocks_per_quarter_note (); }

	int note_type () const { return _tempo->note_type(); }
	int divisions_per_bar () const { return _meter->divisions_per_bar(); }
	int note_value() const { return _meter->note_value(); }
	BBT_Argument   bbt_add (BBT_Time const & bbt, BBT_Offset const & add) const { return BBT_Argument (reftime(), _meter->bbt_add (bbt, add)); }
	BBT_Argument   bbt_subtract (BBT_Time const & bbt, BBT_Offset const & sub) const { return BBT_Argument (reftime(), _meter->bbt_subtract (bbt, sub)); }
	BBT_Argument round_to_bar (BBT_Time const & bbt) const { return BBT_Argument (reftime(), _meter->round_to_bar (bbt)); }
	BBT_Argument round_up_to_bar (BBT_Time const & bbt) const { return BBT_Argument (reftime(), _meter->round_up_to_bar (bbt)); }
	Beats to_quarters (BBT_Offset const & bbo) const { return _meter->to_quarters (bbo); }
	Beats round_to_beat (Beats const & b) const { return _meter->round_to_beat (b); }

	/* combination methods that require both tempo and meter information */

	superclock_t superclocks_per_bar () const {
		return superclocks_per_grid () * _meter->divisions_per_bar();
	}
	superclock_t superclocks_per_grid () const {
		return PBD::muldiv_round (_tempo->superclocks_per_note_type(), _tempo->note_type(), (int64_t) _meter->note_value());
	}

	superclock_t superclocks_per_note_type_at_superclock (superclock_t sc) const {
		if (!_tempo->actually_ramped()) {
			return _tempo->superclocks_per_note_type ();
		}
		return _tempo->superclocks_per_note_type() * exp (-_tempo->omega() * (sc - _tempo->sclock()));
	}

	BBT_Argument bbt_at (timepos_t const &) const;
	BBT_Argument bbt_at_superclock (superclock_t) const;
	superclock_t superclock_at (BBT_Time const &) const;

	samplepos_t samples_per_bar (samplecnt_t sr) const {
		return superclock_to_samples (superclocks_per_bar (), sr);
	}

	Beats quarters_at_sample (samplepos_t sc) const { return quarters_at_superclock (samples_to_superclock (sc, TEMPORAL_SAMPLE_RATE)); }
	Beats quarters_at_superclock (superclock_t sc) const { return _tempo->quarters_at_superclock (sc); }

  protected:
	TempoPoint const * _tempo;
	MeterPoint const * _meter;
	superclock_t       _reftime;

};

/* A music time point is a place where BBT time is reset from
 * whatever it would be when just inferring from the usual counting. Its
 * position is given by a Point that might use superclock or Beats, and the
 * Point's BBT time member is overwritten.
 */
typedef boost::intrusive::list_base_hook<boost::intrusive::tag<struct bartime_tag>> bartime_hook;
class /*LIBTEMPORAL_API*/ MusicTimePoint :  public bartime_hook, public virtual TempoPoint, public virtual MeterPoint
{
  public:
	LIBTEMPORAL_API MusicTimePoint (TempoMap const & map, superclock_t sc, Beats const & b, BBT_Time const & bbt, Tempo const & t, Meter const & m, std::string name = std::string())
		: Point (map, sc, b, bbt), TempoPoint (t, *this), MeterPoint (m, *this),  _name (name) {}

	LIBTEMPORAL_API MusicTimePoint (TempoMap const & map, XMLNode const &);

	virtual ~MusicTimePoint () {}

	LIBTEMPORAL_API bool operator== (MusicTimePoint const & other) const {
		return TempoPoint::operator== (other) && MeterPoint::operator== (other);
	}

	LIBTEMPORAL_API timepos_t time() const { return timepos_t::from_superclock (TempoPoint::sclock()); }

	LIBTEMPORAL_API std::string name() const { return _name; }
	LIBTEMPORAL_API void set_name (std::string const &);

	LIBTEMPORAL_API XMLNode & get_state () const;

 private:
	std::string _name;
};

/** Tempo Map - mapping of timecode to musical time.
 * convert audio-samples, sample-rate to Bar/Beat/Tick, Meter/Tempo
 */

/* TempoMap concepts

   we have several different ways of talking about time:

   * PULSE : whole notes, just because. These are linearly related to any other
             note type, so if you know a number of pulses (whole notes), you
             know the corresponding number of any other note type (e.g. quarter
             notes).

   * QUARTER NOTES : just what the name says. A lot of MIDI software and
                     concepts assume that a "beat" is a quarter-note.

   * BEAT : a fraction of a PULSE. Defined by the meter in effect, so requires
            meter (time signature) information to convert to/from PULSE or QUARTER NOTES.
            In a 5/8 time, a BEAT is 1/8th note. In a 4/4 time, a beat is quarter note.
            This means that measuring time in BEATS is potentially non-linear (if
            the time signature changes, there will be a different number of BEATS
            corresponding to a given time in any other unit).

   * SUPERCLOCK : a very high resolution clock whose frequency
                  has as factors all common sample rates and all common note
                  type divisors. Related to MINUTES or SAMPLES only when a
                  sample rate is known. Related to PULSE or QUARTER NOTES only
                  when a tempo is known.

   * MINUTES : wallclock time measurement. related to SAMPLES or SUPERCLOCK
               only when a sample rate is known.


   * SAMPLES : audio time measurement. Related to MINUTES or SUPERCLOCK only
               when a sample rate is known

   * BBT : bars|beats|ticks ... linearly related to BEATS but with the added
           semantics of bars ("measures") added, in which beats are broken up
           into groups of bars ("measures"). Requires meter (time signature)
           information to compute to/from a given BEATS value. Contains no
           additional time information compared to BEATS, but does have
           additional semantic information.

  Nick sez: not every note onset is on a tick
  Paul wonders: if it's 8 samples off, does it matter?
  Nick sez: it should not phase with existing audio

 */

class LIBTEMPORAL_API TempoMapPoint : public Point, public TempoMetric
{
  public:
	TempoMapPoint (TempoMap const & map, TempoMetric const & tm, superclock_t sc, Beats const & q, BBT_Time const & bbt)
		: Point (map, sc, q, bbt), TempoMetric (tm) {}
	~TempoMapPoint () {}

	bool is_explicit_meter() const { return _meter->sclock() == sclock(); }
	bool is_explicit_tempo() const { return _tempo->sclock() == sclock(); }
	bool is_explicit_position() const { return false; }
	bool is_explicit () const { return is_explicit_meter() || is_explicit_tempo() || is_explicit_position(); }

	timepos_t time() const { if (is_explicit_meter()) { return _meter->time(); } else if (is_explicit_tempo()) { return _tempo->time(); } else { return timepos_t::from_superclock (sclock()); } }
};

}

namespace std {
LIBTEMPORAL_API std::ostream& operator<<(std::ostream& str, Temporal::TempoMapPoint const &);
LIBTEMPORAL_API std::ostream& operator<<(std::ostream& str, Temporal::Tempo const &);
LIBTEMPORAL_API std::ostream& operator<<(std::ostream& str, Temporal::Meter const &);
LIBTEMPORAL_API std::ostream& operator<<(std::ostream& str, Temporal::Point const &);
LIBTEMPORAL_API std::ostream& operator<<(std::ostream& str, Temporal::TempoPoint const &);
LIBTEMPORAL_API std::ostream& operator<<(std::ostream& str, Temporal::MeterPoint const &);
LIBTEMPORAL_API std::ostream& operator<<(std::ostream& str, Temporal::MusicTimePoint const &);
LIBTEMPORAL_API std::ostream& operator<<(std::ostream& str, Temporal::TempoMetric const &);
}

namespace Temporal {

typedef std::vector<TempoMapPoint> TempoMapPoints;

typedef boost::intrusive::list<TempoPoint, boost::intrusive::base_hook<tempo_hook>> Tempos;
typedef boost::intrusive::list<MeterPoint, boost::intrusive::base_hook<meter_hook>> Meters;
typedef boost::intrusive::list<MusicTimePoint, boost::intrusive::base_hook<bartime_hook>> MusicTimes;
typedef boost::intrusive::list<Point, boost::intrusive::base_hook<point_hook>> Points;

/* An object used to retain "position" across calls to get_grid()
 */
class LIBTEMPORAL_API GridIterator
{
  public:
	GridIterator () : sclock (0), tempo (nullptr), meter (nullptr), end (0), bar_mod (0), beat_div (1), valid (false), map (nullptr) {}
	GridIterator (TempoMap const & m, TempoPoint const * tp, MeterPoint const * mp, superclock_t sc, Beats const & b, BBT_Time const & bb, Points::const_iterator p, superclock_t e,
	              uint32_t bmod, uint32_t bdiv)
		: sclock (sc)
		, beats (b)
		, bbt (bb)
		, tempo (tp)
		, meter (mp)
		, points_iterator (p)
		, end (e)
		, bar_mod (bmod)
		, beat_div (bdiv)
		, valid (false)
		, map (&m)
	{
		valid = (tempo && meter);
	}

	void set (TempoMap const & m, TempoPoint const * tp, MeterPoint const * mp, superclock_t sc, Beats const & b, BBT_Time const & bb, Points::const_iterator p, superclock_t e,
	          uint32_t bmod, uint32_t bdiv) {
		map = &m;
		tempo = tp;
		meter = mp;
		sclock = sc;
		beats = b;
		bbt = bb;
		points_iterator = p;
		end = e;
		bar_mod = bmod;
		beat_div = bdiv;
	}

	bool valid_for (TempoMap const & map, superclock_t start, uint32_t bar_mod, uint32_t beat_div) const;
	void catch_up_to (superclock_t e) { end = e; }
	void invalidate () { valid = false; }


	/* These 3 members hold the position of the last discovered grid point */
	superclock_t           sclock;
	Beats                  beats;
	BBT_Time               bbt;

	/* These 3 members hold the TempoMetric information that was in effect
	 * at the *end* of the last use of the GridIterator
	 */
	TempoPoint const *     tempo;
	MeterPoint const *     meter;

	/* the iterator in the tempo map's _points list that points to the next
	 * point to be considered, or _points.end()
	 */
	Points::const_iterator points_iterator;

	/* The position of the end of the last use of the GridIterator. For the
	   iterator to be considered valid on the next call, the start must
	   match this value (see ::valid_for() above).
	*/
	superclock_t           end;


	/* bar modulus and beat division used by GridIterator. These must match
	   the current call to ::get_grid() for the iterator to
	   be valid.
	*/

	uint32_t bar_mod;
	uint32_t beat_div;

  private:
	bool             valid;

	TempoMap const * map; /* nullptr or the map instance this GridIterator
	                       * was last used with.
	                       */
};

class /*LIBTEMPORAL_API*/ TempoMap : public PBD::StatefulDestructible
{
	/* Any given thread must be able to carry out tempo-related arithmetic
	 * and time domain conversions using a consistent version of a
	 * TempoMap. The map could be updated at any time, and for any reason
	 * (typically from a GUI thread), but some other thread could be
	 * using the map to convert from audio to music time (for example).
	 *
	 * We do not want to use locks for this - this math may happen in a
	 * realtime thread, and even worse, the lock may need to be held for
	 * long periods of time in order to have the desired effect: a thread
	 * may be performing some tempo-based arithmetic as part of a complex
	 * operation that requires multiple steps. The tempo map it uses must
	 * remain consistent across all steps, and so we would have to hold the
	 * lock across them all. That would create awkward and difficult
	 * semantics for map users - somewhat arbitrary rules about how long
	 * one could hold the map for, etc.
	 *
	 * Elsewhere in the codebase, we use RCU to solve this sort of
	 * issue. For example, if we need to operate on the current list of
	 * Routes, we get read-only copy of the list, and iterate over it,
	 * knowing that even if the canonical version is being changed, the
	 * copy we are using will not.
	 *
	 * However, the tempo map's use is often implicit rather than
	 * explicit. The callstack to convert between an audio domain time and
	 * a music domain time should not require passing a tempo map into
	 * every call.
	 *
	 * The approach taken here is to use a combination of RCU and
	 * thread-local variables. Any given thread is by definition ... single
	 * threaded. If the thread has a thread-local copy of a tempo map, it
	 * will not change except at explicit calls to change it. The tempo map
	 * can be accessed from any method executed by the thread. But the
	 * relationship between the thread-local copy and "actual" tempo map(s)
	 * is managed via RCU, meaning that read-only access is cheap (no
	 * actual copy required).
	 *
	 */
  public:
	typedef std::shared_ptr<TempoMap const> SharedPtr;
	typedef std::shared_ptr<TempoMap> WritableSharedPtr;
  private:
	static thread_local SharedPtr _tempo_map_p;
	static SerializedRCUManager<TempoMap> _map_mgr;
	static bool fetch_condition ();
  public:
	/* These are only for use in unit tests */
	Points::size_type count_tempos_in_points() const;
	Points::size_type count_meters_in_points() const;
  public:
	LIBTEMPORAL_API static void init ();

	LIBTEMPORAL_API static void      update_thread_tempo_map() { _tempo_map_p = _map_mgr.reader(); }
	LIBTEMPORAL_API static SharedPtr use() { assert (_tempo_map_p); return _tempo_map_p; }
	LIBTEMPORAL_API static SharedPtr fetch() { assert (fetch_condition()); update_thread_tempo_map(); return _tempo_map_p; }
	/* No fetch condition for this, to be used only in association with LocalTempoMapScope */
	LIBTEMPORAL_API static SharedPtr global_fetch() { return _map_mgr.reader(); }

	/* Used only by the ARDOUR::AudioEngine API to reset the process thread
	 * tempo map only when it has changed.
	 */

	LIBTEMPORAL_API static SharedPtr read() { return _map_mgr.reader(); }

	/* Because WritableSharedPtr can be implicitly cast to SharedPtr, this
	 * can be used on either a write_copy()'ed map, or one obtained via the
	 * RCU reader() method.
	 */
	LIBTEMPORAL_API static void      set (SharedPtr new_map) { _tempo_map_p = new_map; }

	/* API for typical tempo map changes */

	LIBTEMPORAL_API static WritableSharedPtr write_copy();
	LIBTEMPORAL_API static int  update (WritableSharedPtr m);
	LIBTEMPORAL_API static void abort_update ();

	/* not part of public API */
	superclock_t reftime(TempoPoint const &, MeterPoint const &) const;

	/* and now on with the rest of the show ... */

  public:
	LIBTEMPORAL_API TempoMap () : _scope_owner (nullptr) {}
	LIBTEMPORAL_API TempoMap (Tempo const& initial_tempo, Meter const& initial_meter);
	LIBTEMPORAL_API TempoMap (TempoMap const&);
	LIBTEMPORAL_API TempoMap (XMLNode const&, int version);
	LIBTEMPORAL_API ~TempoMap();

	/* For use ONLY when building a tempo map from an SMF tempo map */
	LIBTEMPORAL_API void smf_begin ();
	LIBTEMPORAL_API void smf_end ();
	LIBTEMPORAL_API void smf_add (TempoPoint&);
	LIBTEMPORAL_API void smf_add (MeterPoint&);

	LIBTEMPORAL_API TempoMap& operator= (TempoMap const&);

	LIBTEMPORAL_API void sample_rate_changed (samplecnt_t new_sr);

	/* methods which modify the map. These must all be called using
	 * RCU-style semantics: get a writable copy, modify it, then update via
	 * the RCU manager.
	 */

	LIBTEMPORAL_API bool set_ramped (TempoPoint&, bool);
	LIBTEMPORAL_API bool set_continuing (TempoPoint&, bool);

	LIBTEMPORAL_API bool remove_time (timepos_t const & pos, timecnt_t const & duration);

	LIBTEMPORAL_API	void change_tempo (TempoPoint&, Tempo const&);

	LIBTEMPORAL_API void set_bartime (BBT_Time const &, timepos_t const &, std::string name = std::string());
	LIBTEMPORAL_API void remove_bartime (MusicTimePoint const & tp, bool with_reset = true);
	LIBTEMPORAL_API void replace_bartime (MusicTimePoint & tp, bool with_reset = true);

	LIBTEMPORAL_API superclock_t previous_bbt_reference_at_superclock (superclock_t) const;

	LIBTEMPORAL_API TempoPoint& set_tempo (Tempo const &, BBT_Argument const &);
	LIBTEMPORAL_API	TempoPoint& set_tempo (Tempo const &, timepos_t const &);

	LIBTEMPORAL_API	void replace_tempo (TempoPoint const & old, Tempo const & thenew, timepos_t const &);

	LIBTEMPORAL_API	MeterPoint& set_meter (Meter const &, BBT_Argument const &);
	LIBTEMPORAL_API	MeterPoint& set_meter (Meter const &, timepos_t const &);

	LIBTEMPORAL_API void remove_tempo (TempoPoint const &, bool with_reset = true);
	LIBTEMPORAL_API void remove_meter (MeterPoint const &, bool with_reset = true);

	/* these are a convenience method that just wrap some odd semantics */
	LIBTEMPORAL_API bool move_tempo (TempoPoint const & point, timepos_t const & destination, bool push = false);
	LIBTEMPORAL_API bool move_meter (MeterPoint const & point, timepos_t const & destination, bool push = false);

	LIBTEMPORAL_API int set_state (XMLNode const&, int version);

	LIBTEMPORAL_API void constant_twist_tempi (TempoPoint& prev, TempoPoint& focus, TempoPoint& next, double tempo_delta);
	LIBTEMPORAL_API void ramped_twist_tempi (TempoPoint& prev, TempoPoint& focus, TempoPoint& next, double tempo_delta);

	LIBTEMPORAL_API void stretch_tempo (TempoPoint& ts, double new_npm);
	LIBTEMPORAL_API void stretch_tempo_end (TempoPoint* ts, samplepos_t sample, samplepos_t end_sample);

	LIBTEMPORAL_API bool clear_tempos_before (timepos_t const &, bool stop_at_music_time);
	LIBTEMPORAL_API bool clear_tempos_after (timepos_t const &, bool stop_at_music_time);

	/* END OF MODIFYING METHODS */

	/* rather than giving direct access to the intrusive list members,
	 * offer one that uses an STL container instead.
	 */

	typedef std::list<Point const *> Metrics;

	void get_metrics (Metrics& m) const {
		for (auto const & p : _points) {
			m.push_back (&p);
		}
	}

	LIBTEMPORAL_API	bool can_remove (TempoPoint const &) const;
	LIBTEMPORAL_API	bool can_remove (MeterPoint const &) const;

	LIBTEMPORAL_API	bool is_initial (TempoPoint const &) const;
	LIBTEMPORAL_API	bool is_initial (MeterPoint const &) const;

	LIBTEMPORAL_API	uint32_t n_meters() const;
	LIBTEMPORAL_API	uint32_t n_tempos() const;

	LIBTEMPORAL_API TempoPoint const* previous_tempo (TempoPoint const &) const;
	LIBTEMPORAL_API TempoPoint const* next_tempo (TempoPoint const &) const;

	LIBTEMPORAL_API MeterPoint const* previous_meter (MeterPoint const &) const;
	LIBTEMPORAL_API MeterPoint const* next_meter (MeterPoint const &) const;

	LIBTEMPORAL_API bool tempo_exists_before (TempoPoint const & t) const { return (bool) previous_tempo (t); }
	LIBTEMPORAL_API bool tempo_exists_after (TempoPoint const & t) const { return (bool) next_tempo (t); }

	LIBTEMPORAL_API Meter const* next_meter (Meter const &) const;

	LIBTEMPORAL_API	TempoMetric metric_at (timepos_t const &) const;

	/* These return the TempoMetric in effect at the given time. If
	   can_match is true, then the TempoMetric may refer to a Tempo or
	   Meter at the given time. If can_match is false, the TempoMetric will
	   only refer to the Tempo or Metric preceding the given time.
	*/
	LIBTEMPORAL_API	TempoMetric metric_at (Beats const &, bool can_match = true) const;
	LIBTEMPORAL_API	TempoMetric metric_at (BBT_Argument const &, bool can_match = true) const;

	LIBTEMPORAL_API TempoMapCutBuffer* cut (timepos_t const & start, timepos_t const & end, bool ripple);
	LIBTEMPORAL_API TempoMapCutBuffer* copy (timepos_t const & start, timepos_t const & end);
	LIBTEMPORAL_API void paste (TempoMapCutBuffer const &, timepos_t const & position, bool ripple, std::string = std::string());

	LIBTEMPORAL_API void shift (timepos_t const & at, BBT_Offset const & by);
	LIBTEMPORAL_API void shift (timepos_t const & at, timecnt_t const & by);

	LIBTEMPORAL_API timepos_t duration (TimeDomain) const;

  private:
	template<typename TimeType, typename Comparator> TempoPoint const & _tempo_at (TimeType when, Comparator cmp) const {
		assert (!_tempos.empty());

		if (_tempos.size() == 1) {
			return _tempos.front();
		}

		Tempos::const_iterator prev = _tempos.end();
		for (Tempos::const_iterator t = _tempos.begin(); t != _tempos.end(); ++t) {
			if (cmp (*t, when)) {
				prev = t;
			} else {
				break;
			}
		}
		if (prev == _tempos.end()) {
			return _tempos.front();
		}
		return *prev;
	}

	template<typename TimeType, typename Comparator> MeterPoint const & _meter_at (TimeType when, Comparator cmp) const {
		assert (!_meters.empty());

		if (_meters.size() == 1) {
			return _meters.front();
		}

		Meters::const_iterator prev = _meters.end();
		for (Meters::const_iterator m = _meters.begin(); m != _meters.end(); ++m) {
			if (cmp (*m, when)) {
				prev = m;
			} else {
				break;
			}
		}
		if (prev == _meters.end()) {
			return _meters.front();
		}
		return *prev;
	}

  public:
	LIBTEMPORAL_API	MeterPoint const& meter_at (timepos_t const & p) const;
	LIBTEMPORAL_API	MeterPoint const& meter_at (superclock_t sc) const { return _meter_at (sc, Point::sclock_comparator()); }
	LIBTEMPORAL_API	MeterPoint const& meter_at (Beats const & b) const { return _meter_at (b, Point::beat_comparator()); }
	LIBTEMPORAL_API	MeterPoint const& meter_at (BBT_Argument const & bbt) const { return _meter_at (bbt, Point::bbt_comparator()); }

	LIBTEMPORAL_API	TempoPoint const& tempo_at (timepos_t const & p) const;
	LIBTEMPORAL_API	TempoPoint const& tempo_at (superclock_t sc) const { return _tempo_at (sc, Point::sclock_comparator()); }
	LIBTEMPORAL_API	TempoPoint const& tempo_at (Beats const & b) const { return _tempo_at (b, Point::beat_comparator()); }
	LIBTEMPORAL_API TempoPoint const& tempo_at (BBT_Argument const & bbt) const { return _tempo_at (bbt, Point::bbt_comparator()); }

	LIBTEMPORAL_API double max_notes_per_minute() const;
	LIBTEMPORAL_API double min_notes_per_minute() const;

	/* convenience function that hides some complexities behind fetching
	 * the bpm at position
	 */
	LIBTEMPORAL_API double quarters_per_minute_at (timepos_t const & pos) const;

	/* convenience functions */
	LIBTEMPORAL_API BBT_Argument round_to_bar (BBT_Argument const & bbt) const {
		return metric_at (bbt).round_to_bar (bbt);
	}
	LIBTEMPORAL_API BBT_Argument round_up_to_bar (BBT_Argument const & bbt) const {
		return metric_at (bbt).round_up_to_bar (bbt);
	}

	LIBTEMPORAL_API BBT_Argument bbt_at (timepos_t const &) const;
	LIBTEMPORAL_API BBT_Argument bbt_at (Beats const &) const;

	LIBTEMPORAL_API	Beats quarters_at (BBT_Argument const &) const;
	LIBTEMPORAL_API	Beats quarters_at (timepos_t const &) const;

	LIBTEMPORAL_API	superclock_t superclock_at (Beats const &) const;
	LIBTEMPORAL_API	superclock_t superclock_at (BBT_Argument const &) const;
	LIBTEMPORAL_API	superclock_t superclock_at (timepos_t const &) const;

	LIBTEMPORAL_API	samplepos_t sample_at (Beats const & b) const { return superclock_to_samples (superclock_at (b), TEMPORAL_SAMPLE_RATE); }
	LIBTEMPORAL_API	samplepos_t sample_at (BBT_Argument const & b) const { return superclock_to_samples (superclock_at (b), TEMPORAL_SAMPLE_RATE); }
	LIBTEMPORAL_API	samplepos_t sample_at (timepos_t const & t) const { return superclock_to_samples (superclock_at (t), TEMPORAL_SAMPLE_RATE); }

	/* ways to walk along the tempo map, measure distance between points,
	 * etc.
	 */

	LIBTEMPORAL_API Beats scwalk_to_quarters (superclock_t pos, superclock_t distance) const;
	LIBTEMPORAL_API Beats scwalk_to_quarters (Beats const & pos, superclock_t distance) const;

	LIBTEMPORAL_API	timecnt_t bbt_duration_at (timepos_t const & pos, BBT_Offset const & bbt) const;
	LIBTEMPORAL_API	Beats bbtwalk_to_quarters (Beats const & start, BBT_Offset const & distance) const;
	LIBTEMPORAL_API	Beats bbtwalk_to_quarters (BBT_Argument const & start, BBT_Offset const & distance) const;

	LIBTEMPORAL_API	Temporal::timecnt_t convert_duration (Temporal::timecnt_t const & duration, Temporal::timepos_t const &, Temporal::TimeDomain domain) const;

	LIBTEMPORAL_API	BBT_Argument bbt_walk (BBT_Argument const &, BBT_Offset const &) const;

	LIBTEMPORAL_API BBT_Offset bbt_distance (BBT_Argument const & a, BBT_Argument const & b) const;

	Tempos const & tempos() const { return _tempos; }
	Meters const & meters() const { return _meters; }
	MusicTimes const & bartimes() const { return _bartimes; }


	LIBTEMPORAL_API Points::const_iterator get_grid (TempoMapPoints & points, superclock_t start, superclock_t end, uint32_t bar_mod = 0, uint32_t beat_div = 1) const;
	LIBTEMPORAL_API void get_grid (GridIterator& iter, TempoMapPoints& ret, superclock_t rstart, superclock_t end, uint32_t bar_mod = 0, uint32_t beat_div = 1) const;

	/* This version exists for Lua bindings, to avoid having to wrap Points::iterator etc. */
	LIBTEMPORAL_API void grid (TempoMapPoints& points, superclock_t start, superclock_t end, uint32_t bar_mod = 0, uint32_t beat_div = 1) const {
		get_grid (points, start, end, bar_mod, beat_div);
	}

	struct EmptyTempoMapException : public std::exception {
		virtual const char* what() const throw() { return "TempoMap is empty"; }
	};

	LIBTEMPORAL_API void dump (std::ostream&) const;

	LIBTEMPORAL_API static PBD::Signal<void()> MapChanged;

	LIBTEMPORAL_API XMLNode& get_state() const;

	LIBTEMPORAL_API	Beats quarters_at_sample (samplepos_t sc) const { return quarters_at_superclock (samples_to_superclock (sc, TEMPORAL_SAMPLE_RATE)); }
	LIBTEMPORAL_API	Beats quarters_at_superclock (superclock_t sc) const;

	LIBTEMPORAL_API	void midi_clock_beat_at_or_after (samplepos_t const pos, samplepos_t& clk_pos, uint32_t& clk_beat) const;

	static void map_assert (bool expr, char const * exprstr, char const * file, int line);

	LIBTEMPORAL_API void set_scope_owner (ScopedTempoMapOwner&);
	LIBTEMPORAL_API void clear_scope_owner ();
	ScopedTempoMapOwner* scope_owner() const { return _scope_owner; }

  private:
	Tempos       _tempos;
	Meters       _meters;
	MusicTimes   _bartimes;
	Points       _points;
	ScopedTempoMapOwner* _scope_owner;

	int set_tempos_from_state (XMLNode const &);
	int set_meters_from_state (XMLNode const &);
	int set_music_times_from_state (XMLNode const &);

	TempoPoint* core_add_tempo (TempoPoint*, bool&);
	MeterPoint* core_add_meter (MeterPoint*, bool&);
	MusicTimePoint* core_add_bartime (MusicTimePoint*, bool&);
	void        core_add_point (Point*);

	MeterPoint & set_meter (Meter const &, superclock_t);

	TempoPoint* add_tempo (TempoPoint*);
	MeterPoint* add_meter (MeterPoint*);
	MusicTimePoint* add_or_replace_bartime (MusicTimePoint*);

	void add_point (Point &);

	void reset_starting_at (superclock_t, bool constant_bbt = true);
	void reset_starting_at (Beats const &);

	void remove_point (Point const &);

	void copy_points (TempoMap const & other);

	BBT_Argument bbt_at (superclock_t sc) const;

	template<typename T, typename T1> struct const_traits {
		typedef Points::const_iterator iterator_type;
		typedef TempoPoint const * tempo_point_type;
		typedef MeterPoint const * meter_point_type;
		using time_reference = T;
		using time_type = T1;
	};

	template<typename T, typename T1> struct non_const_traits {
		typedef Points::iterator iterator_type;
		typedef TempoPoint * tempo_point_type;
		typedef MeterPoint * meter_point_type;
		using time_reference = T;
		using time_type = T1;
	};

	/* A somewhat complex method that sets a TempoPoint* and MeterPoint* to
	 * refer to the correct tempo and meter points for the given start
	 * time.
	 *
	 * It also returns an iterator which may point at the latter of the two
	 * points (tempo & meter; always the meter point if they are at the
	 * same time) OR may point at the iterator *after* the latter of the
	 * two, depending on whether or not @p ret_iterator_after_not_at is
	 * true or false.
	 *
	 * If @p can_match is true, the points used can be located at the
	 * given time. If false, they must be before it. Setting it to false is
	 * useful when you need to know the TempoMetric in effect at a given
	 * time if there was no tempo or meter point at that time.
	 *
	 * The templated structure here is to avoid code duplication in 2
	 * separate versions of this method, one that would be const, and one
	 * that would be non-const. This is a challenging problem in C++, and
	 * seems best solved by using a "traits" object as shown here.
	 *
	 * The begini, endi, tstart and mstart arguments are an additional
	 * complication. If we try to use e.g. _points.begin() inside the
	 * method, which is labelled const, we will always get the const
	 * version of the iterator. This const iterator type will conflict with
	 * the non-const iterator type defined by the "non_const_traits"
	 * type. The same happens with _tempos.front() etc. This problem is
	 * addressed by calling these methods in the caller method, which maybe
	 * const or non-const, and will provide appropriate versions based on that.
	 */

	template<class constness_traits_t> typename constness_traits_t::iterator_type
		_get_tempo_and_meter (typename constness_traits_t::tempo_point_type &,
		                      typename constness_traits_t::meter_point_type &,
		                      typename constness_traits_t::time_reference (Point::*)() const,
		                      typename constness_traits_t::time_type,
		                      typename constness_traits_t::iterator_type begini,
		                      typename constness_traits_t::iterator_type endi,
		                      typename constness_traits_t::tempo_point_type tstart,
		                      typename constness_traits_t::meter_point_type mstart,
		                      bool can_match,
		                      bool ret_iterator_after_not_at) const;

	/* fetch non-const tempo/meter pairs and iterator (used in
	 * ::reset_starting_at() in which we will modify points.
	 */

	Points::iterator  get_tempo_and_meter (TempoPoint *& t, MeterPoint *& m, superclock_t sc, bool can_match, bool ret_iterator_after_not_at) {

		/* because `this` is non-const (because the method is not
		 * marked const), the following:

		   _points.begin()
		   _points.end()
		   _tempos.front()
		   _meters.front()

		   will all be the non-const versions of these methods.
		*/

		if (_tempos.size() == 1 && _meters.size() == 1) { t = &_tempos.front(); m = &_meters.front();  return _points.end(); }
		return _get_tempo_and_meter<non_const_traits<superclock_t, superclock_t> > (t, m, &Point::sclock, sc, _points.begin(), _points.end(), &_tempos.front(), &_meters.front(), can_match, ret_iterator_after_not_at);
	}

	/* fetch const tempo/meter pairs and iterator (used in metric_at() and
	 * other similar call sites where we do not modify the map
	 */

	Points::const_iterator get_tempo_and_meter (TempoPoint const *& t, MeterPoint const *& m, superclock_t sc, bool can_match, bool ret_iterator_after_not_at) const {
		if (_tempos.size() == 1 && _meters.size() == 1) { t = &_tempos.front(); m = &_meters.front();  return _points.end(); }
		return _get_tempo_and_meter<const_traits<superclock_t, superclock_t> > (t, m, &Point::sclock, sc, _points.begin(), _points.end(), &_tempos.front(), &_meters.front(), can_match, ret_iterator_after_not_at);
	}
	Points::const_iterator get_tempo_and_meter (TempoPoint const *& t, MeterPoint const *& m, Beats const & b, bool can_match, bool ret_iterator_after_not_at) const {
		if (_tempos.size() == 1 && _meters.size() == 1) { t = &_tempos.front(); m = &_meters.front();  return _points.end(); }
		return _get_tempo_and_meter<const_traits<Beats const &, Beats> > (t, m, &Point::beats, b, _points.begin(), _points.end(), &_tempos.front(), &_meters.front(), can_match, ret_iterator_after_not_at);
	}

	Points::const_iterator get_tempo_and_meter_bbt (TempoPoint const *& t, MeterPoint const *& m, BBT_Argument const & bbt, bool can_match, bool ret_iterator_after_not_at) const;

	Points::const_iterator get_tempo_and_meter (TempoPoint const *& t, MeterPoint const *& m, BBT_Argument const & bbt, bool can_match, bool ret_iterator_after_not_at) const {
		if (_tempos.size() == 1 && _meters.size() == 1) { t = &_tempos.front(); m = &_meters.front();  return _points.end(); }
		return get_tempo_and_meter_bbt (t, m, bbt, can_match, ret_iterator_after_not_at);
	}

	/* This is private, and should not be callable from outside the map
	   because of potential confusion between samplepos_t and
	   superclock_t. The timepos_t variant of ::metric_at() handles any
	   samplepos_t-passing call.
	*/
	TempoMetric metric_at (superclock_t, bool can_match = true) const;

	/* parsing legacy tempo maps */

	struct LegacyTempoState
	{
		samplepos_t sample;
		double pulses;
		double note_types_per_minute;
		double end_note_types_per_minute;
		double note_type;
		bool continuing; /* "clamped" in actual legacy stuff */
	};

	struct LegacyMeterState
	{
		samplepos_t sample;
		double pulses;
		BBT_Time bbt;
		double beat;
		double divisions_per_bar;
		double note_type;
	};

	int parse_tempo_state_3x (const XMLNode& node, LegacyTempoState& lts);
	int parse_meter_state_3x (const XMLNode& node, LegacyMeterState& lts);
	int set_state_3x (XMLNode const &);
	TempoPoint & set_tempo (Tempo const & t, timepos_t const & time, Beats const & beats);

	friend class TempoPoint;
	friend class MeterPoint;
	friend class TempoMetric;

	bool solve_ramped_twist (TempoPoint&, TempoPoint&);  /* this is implemented by iteration, and it might fail. */
	bool solve_constant_twist (TempoPoint&, TempoPoint&);  //TODO:  currently also done by iteration; should be possible to calculate directly

	Point* core_remove_meter (MeterPoint const &);
	Point* core_remove_tempo (TempoPoint const &);
	Point* core_remove_bartime (MusicTimePoint const &);

	void reset_section (Points::iterator& begin, superclock_t, TempoMetric& metric, bool constant_bbt);

	TempoMapCutBuffer* cut_copy (timepos_t const & start, timepos_t const & end, bool copy, bool ripple);

	void fill_grid_by_walking (TempoMapPoints& ret, Points::const_iterator& p, TempoMetric& metric, superclock_t& start, superclock_t rstart,
	                                           superclock_t end, int bar_mod, int beat_div, Beats& beats, BBT_Time& bbt) const;
	void fill_grid_with_final_metric (TempoMapPoints& ret, TempoMetric metric, superclock_t start, superclock_t rstart, superclock_t end, int bar_mod, int beat_div, Beats beats, BBT_Time bbt) const;
};

class LIBTEMPORAL_API TempoMapCutBuffer
{
  public:
	TempoMapCutBuffer (timecnt_t const &);
	~TempoMapCutBuffer ();

	timecnt_t duration() const { return _duration; }

	void add_start_tempo (Tempo const & t);
	void add_end_tempo (Tempo const & t);
	void add_start_meter (Meter const & t);
	void add_end_meter (Meter const & t);

	Tempo const * start_tempo () const { return _start_tempo; }
	Tempo const * end_tempo () const { return _end_tempo; }

	Meter const * start_meter () const { return _start_meter; }
	Meter const * end_meter () const { return _end_meter; }

	typedef boost::intrusive::list<TempoPoint, boost::intrusive::base_hook<tempo_hook>> Tempos;
	typedef boost::intrusive::list<MeterPoint, boost::intrusive::base_hook<meter_hook>> Meters;
	typedef boost::intrusive::list<MusicTimePoint, boost::intrusive::base_hook<bartime_hook>> MusicTimes;
	typedef boost::intrusive::list<Point, boost::intrusive::base_hook<point_hook>> Points;

	void add (TempoPoint const &);
	void add (MeterPoint const &);
	void add (MusicTimePoint const &);
	void add (Point const &);

	void clear ();
	void dump (std::ostream&);

	Tempos const & tempos() const { return _tempos; }
	Meters const & meters() const { return _meters; }
	MusicTimes const & bartimes() const { return _bartimes; }
	Points const & points() const { return _points; }

	bool empty() const {
		return _tempos.empty() && _meters.empty() && _bartimes.empty() && _points.empty();
	}

  private:
	Tempo* _start_tempo;
	Tempo* _end_tempo;
	Meter* _start_meter;
	Meter* _end_meter;
	timecnt_t   _duration;

	Tempos       _tempos;
	Meters       _meters;
	MusicTimes   _bartimes;
	Points       _points;
};

class LIBTEMPORAL_API TempoCommand : public PBD::Command {
	public:

	TempoCommand (std::string const & name, XMLNode const * before, XMLNode const * after);
	TempoCommand (XMLNode const &);
	~TempoCommand ();

	const std::string& name () const { return _name; }

	void operator() ();
	void undo ();

	XMLNode & get_state () const;

  protected:
	std::string _name;
	XMLNode const * _before;
	XMLNode const * _after;
};

} /* end of namespace Temporal */

#ifdef COMPILER_MSVC
#pragma warning(disable:4101)
#endif
