#ifndef _GLYRUS_BEHAVIOR_H_
#define _GLYRUS_BEHAVIOR_H_

#include "glyrus_mobile.h"
    
#include <stdlib.h> // Using rand()
#include <map>		// using multimap
#include <list>
#include <math.h>
#include <time.h>   // using clock_t clock()
#include <assert.h>

// #define DEBUG_FORMATION
// #define DEBUG_SHIP
// #define DEBUG_BULLET
// #define LOG_LINESTEP
// Faster calculations can be performed, knowing that glyrus faces forward.
#define GLYRUS_ORIENTATION_EQUALS_0

// File: glymotion.h
// Objective: Implement simple and complex motions, maneuvers, tactics.
// Author: David Kennerly.
// Date: 2004-04-26
// Version: 0.1.9



/***************************************
Begin design.doc 2004-04-21:

Motion and direction data structures
This defines  the basics of motion.  It builds upon each motion to create a sequence of motions, called a maneuver.  A sequence of maneuvers is called a tactic.  I'm suggesting implementing this sequence and most of these sequences as arrays, because arrays are quick and easy to program.  I'm guessing development time is more of our bottleneck more than memory.

The sequence of maneuvers becomes classes for units to refer to.  As an example, a formation may use a "swoop" tactic to dive down, loop 360 degrees, turn back up and return to a hovering position at the far end of plane.  

Since all units occur only in a two-dimensional plane, all motions also only occur in a two dimensional plane, that is normalized from 0 to 1 in the x and y axes.  Since the World width and height varies, an aspect ratio defines the proportion of horizontal to vertical motion.

class motion

class line : motion
public:
	const float getLine();
setLine(float length);

class arc : motion
public:
const glyTurn2fv getTurn();
void setTurn2fv(glyTurn2fv newTurn);

class maneuver
public:
	motion motionList[];
	GLint motionCount;

class tactic
public:
	maneuver maneuverList[];
	GLint maneuverCount;

Many mobile objects use motions.  The motions are relative to the current orientation of the object.  So the mobile object either goes straight ahead, or turns left or right.  If the degree of turn is negative, it is left (counterclockwise).  If the degree is positive, the turn is right (clockwise).  With units and motions it is possible to implement the geometry.

End design.doc 2004-04-21.
***************************************/


// Objective: Compile with local debugging options.
// #define DEBUG_MOTION

#define MOTION_COUNT_MAX    64
#define MANEUVER_COUNT_MAX  64

enum motionType{ M_NULL, M_LINE, M_ARC, M_HOVER, M_END };
enum turnType{ LEFT, RIGHT };






class motion
{
public:
    motion( motionType newMotionType, glyNormalf newDistance );
    motion( motionType newMotionType, bool newIsRight, 
        glyNormalf newRadius, glyOrientationf newOrientation );

    motionType type;
    // Objective: Define parameters for any line.
    glyNormalf distance;
    // Objective: Define parameters for any arc.
    bool isRight;
    glyNormalf radius;
    glyOrientationf orientation;
};


// Declaration of Motions
static motion Line( M_LINE, 0.2f );
static motion ArcL( M_ARC, LEFT, 0.05f, 90.0f );
static motion ArcR( M_ARC, RIGHT, 0.05f, 90.0f );
static motion WhipLeft( M_ARC, LEFT, 0.02f, 90.0f );
static motion WhipRight( M_ARC, RIGHT, 0.02f, 90.0f );

static motion ArcLeft30( M_ARC, LEFT, 0.05f, 30.0f );
static motion ArcRight30( M_ARC, RIGHT, 0.05f, 30.0f );
static motion WhipLeft30( M_ARC, LEFT, 0.02f, 30.0f );
static motion WhipRight30( M_ARC, RIGHT, 0.02f, 30.0f );

static motion ArcLeft45( M_ARC, LEFT, 0.05f, 45.0f );
static motion ArcRight45( M_ARC, RIGHT, 0.05f, 45.0f );
static motion WhipLeft45( M_ARC, LEFT, 0.02f, 45.0f );
static motion WhipRight45( M_ARC, RIGHT, 0.02f, 45.0f );

static motion ArcLeft90( M_ARC, LEFT, 0.05f, 90.0f );
static motion ArcRight90( M_ARC, RIGHT, 0.05f, 90.0f );
static motion WhipLeft90( M_ARC, LEFT, 0.02f, 90.0f );
static motion WhipRight90( M_ARC, RIGHT, 0.02f, 90.0f );

// Declare test motions
static motion testLine( M_LINE, 0.2f );
static motion testArcL( M_ARC, LEFT, 0.10f, 90.0f );
static motion testArcR( M_ARC, RIGHT, 0.05f, 90.0f );
static motion testWhipLeft( M_ARC, LEFT, 0.02f, 90.0f );
static motion testWhipRight( M_ARC, RIGHT, 0.02f, 90.0f );



class maneuver
// Objective: Define an arbitrary sequence of motions.
{
public:
    maneuver();
	// Clear all motions in the maneuver.
	void clear();
	void insert( motion * newMotion );
    motion * getMotion( int motion_i );
	// Get the number of motions in a maneuver.
    inline const int getLength();

protected:
    motion * motionList[ MANEUVER_COUNT_MAX ];
    int motionList_length;
};

static maneuver defaultManeuver;


/*
Time Event Scheduler Overview

This event scheduler operates on an object-oriented approach to subdivide time into self-contained segments.  
Generic sections are decomposed into tracks, which progress in parallel.
Tracks are subdivided into time events.  
Time events may be subdivided any number of times into component time events.
A few examples of this model are:  
	A book dividied into chapters, divided into paragraphs, divided into sentences, divided into words.
	A movie divided into acts, divided into sequences, divided into scenes, divided into shots.
	A song divided into sections, divided into phrases, divided into measures, divided into beats.

This scheduler applies dynamic timing of referenced subevents, 
which enables convenient construction of a whole from the bottom up.
A measure in a song can be reused many times.  
Upon editing a measure, its changes are propagated throughout, because the aggregate schedule is
composed of references to its sub events.
There is no theoretical limit to the level of subdivision, although each subdivision does incur
some additional processing.

Architectural Hierarchy
The section is composed of pointers to parallel tracks, which are subdivided into pointers to sub-events.
Pointers are used to enable multiple instances of the same track or event.

	timeEventSection
		* getTrack()
			_getSubEventList()
				* timeEvent
					_getSubEventList()
						* timeEvent
						* timeEvent
						...
				* timeEvent
					_getSubEventList()
						* timeEvent
						* timeEvent
						* timeEvent
							_getSubEventList()
								* timeEvent
								* timeEvent
								...
						...
		* getTrack()
			_getSubEventList()
				* timeEvent
		...

For example:

	introMusicSection
		* drumTrack
			_getSubEventList()
				* introDrumMeasure
					_getSubEventList()
						* quietDrumNote
						* quietDrumNote
						* loudDrumNote

A section is constructed from the bottom-up.  In the example of the drumTrack, 
the notes are defined first, and then inserted into a measure, which is inserted into the track.
The track is finally inserted into the music section.

	Bottom-up construction order:
		quietDrumNote, loudDrumNote
		introDrumMeasure
		drumTrack
		introMusicSection



Defining time events.

The time event should be longer than the longest frame.  
Since the application should play at 30 frames per second or faster, 
the time events should be longer than 1/30th of a second.  
With some error margin, 50 milliseconds should be the shortest time event.

*/

/** BASIC USAGE OF MUSIC SYNCHRONIZATION

	// Once per frame, check for any beat starting during this frame.
	TIME_EVENT_LIST * beat_list = & theSimulation->music->heavy_beat_list;
	if (   theSimulation->getEventSection()->is_event_starting( beat_list )   )
	{
		// Visualization of each beat, such as pulse
	}

The data member "heavy_beat_list" may be replaced by other relevant beat lists.

There are several default parameters that may be modified, 
which makes it convenient to test for span of time.  
The underlying search algorithm is log-N on a tiny tree, 
so tests have very little performance cost.

Header has details on parameters and usage.
    glyrus_behavior.h
    class timeEventSection

The operability of the music synchronization system may be verified in game.
Open the debug menu.  Select "Show rhythm".  Several beat lists and rhythms appear.
When the beat starts, the word "BEAT" appears.  

Examples in PlayState.cpp:

	void PlayState::update_rhythm_bar( rhythm_bar_vector * rhythm_vector )

This function contains extensive usage examples 
of the application's access to rhythm and beat lists.

Baht's change of color scheme to the phrases of the music provides a working visual example.  He used the same method "is_event_starting".
*/

/*
TODO: Generalize timeEvent classes as templates. E.g., 
	timeEvent			dynamicList< T >
	timeEventTrack		dynamicTrack< T >
	timeEventSection	dynamicSection< T >
Consider recursive definition of section:
	dynamicList< dynamicSection< T > >
*/



/*
SCHEDULE

A schedule is a flat version of all time events, without regard to track or depth.

Implemented as private member of timeEventSection:
	typedef multimap< absoluteEventTime, timeEvent * >		timeEvent_schedule;

Methods:
	// Return true if the time event occurs between after and until.
	bool is_event_starting( timeEvent *, eventTime after, eventTime until );

	// Construct the schedule.  
	// Precondition:  The time events in the tracks of the section are stable.
	void _build_schedule();

REQUIREMENTS

Schedule should be populated after the section is populated, when the section starts.
Schedule should be modified when section or a component track's time event duration or ordering is modified.

SCHEDULE ALGORITHMS

Build the schedule:
	Set the absolute time to the origin time.
	For each track:
		For each base event:
			Insert the absolute time and reference to the base event into the schedule.
			Increment the absolute time by the duration.

Is event starting 
		after time A 
		and until time B?
	Set lower bound to after time A.
	Set upper bound to until time B.
	Set bounded schedule to schedule within lower bound and upper bound.
	If event start time is found in bounded schedule, return yes.
	Otherwise, return no.

NOTES

Other designs considered included multiple playheads on a track, decoupling track data and playhead.
Multimap is more flexible for querying various times.

The preexisting methods could be modified to traverse the events and build the schedule.

void timeEventTrack :: _incrementCurrentEvent()
void timeEventTrack :: _startEachSubEvent( timeEvent * rootEvent )

Modifications needed:
	_incrementCurrentEvent()
	_startEachSubEvent( timeEvent * rootEvent )
	Do not adjust start time or start count of time events or of current time event.
	Save event iterator stack before operation and restore once complete, 
		or use a different event iterator stack.
	At each event, record the event to the schedule.

	Dividing the increment current event method into a sub method:
		_incrementEvent( event_iterator_stack )
	would allow that submethod to be used for generic forward traversal.
	A generic callback would extend the method:
		_applyToEachSubEvent( timeEvent * rootEvent, functor operation )
		_applyToEachSubEvent( timeEvent * rootEvent, start, getCurrentEventStartTime() )
		_applyToEachSubEvent( timeEvent * rootEvent, addToMap, absoluteTime )
	I've never used a callback, so that will take extra hours to get it right.
	I could make a macro that does nearly a callback, 
		by passing the function as a macro parameter.
		#define _APPLY_TO_EACH_SUB_EVENT( _rootEvent_, _childEvent_, _operation_, 
			_event_iterator_stack_ )
	void _mapEachSubEvent( timeEvent * rootEvent, absolute_time );
	schedule.insert(   pair< eventTime, timeEvent * > ( absolute_time, childEvent )   );

	pair->first		// first value in pair
	pair->second	// second value in pair
	// iterator to previous element whose key is less than or equal to key.
	iterator schedule.lower_bound( key )	
	// iterator to next element whose key is greater than key.
	iterator schedule.upper_bound( key )	
	// pair of iterators in range
	pair< iterator, iterator > equal_range( key )

Schedule is working well, and going to a future time is also working.
But going to point in the past is not.  It seems to corrupt the status.
Yet the schedule remains intact.
So, what if the schedule replaced the dynamic time events.
The dynamic time events can still work for going forward, if desired.
But the schedule would replace it for general use.
If an event is changed, though, the whole schedule needs rebuilding.  That sucks.
The schedule is static.
*/

/*
START TIME MAP

This is a pair of absolute event time and start count.

When a schedule is built, the start time map is also populated.
As each event is scheduled to start:
That start time and count of number of times starting
is inserted into that time event's start map.

When an event is queried to see if it is starting,
the window of opportunity becomes the boundaries for a search in the start map.
If there is a predicate to the periodic cycle, that is determined from the start count.
*/


// #define DEBUG_TIME_EVENT
// #define DEBUG_TIME_EVENT_INTENSITY
// #define ASSERT_TIME_EVENT_DURATION

// Forward declaration
class timeEvent;
class timeEventTrack;
class timeEventSection;

enum dataState {
	UNDEFINED_DATA,
	DEFINED_DATA
};

// Store event time as integer data of milliseconds, 
// which is consistent with other frame-precise time values.
typedef clock_t eventTime;
typedef clock_t absoluteEventTime;

// Define legible shorthand for a time event list.
#define TIME_EVENT_LIST             std::list< timeEvent * >

#define TIME_EVENT_ITERATOR		    TIME_EVENT_LIST::const_iterator
#define  _TIME_EVENT_ITERATOR		TIME_EVENT_LIST::iterator

#define TIME_EVENT_TRACK_LIST		std::list< timeEventTrack * >

#define _TIME_EVENT_TRACK_ITERATOR	TIME_EVENT_TRACK_LIST::iterator
#define TIME_EVENT_TRACK_ITERATOR	TIME_EVENT_TRACK_LIST::const_iterator

#define _TIME_EVENT_ITERATOR_LIST		std::list< _TIME_EVENT_ITERATOR >
#define _TIME_EVENT_ITERATOR_ITERATOR	_TIME_EVENT_ITERATOR_LIST::iterator

// List of lists.
#define TIME_EVENT_LIST_LIST		std::list< TIME_EVENT_LIST >

#define TIME_EVENT_LIST_ITERATOR	TIME_EVENT_LIST_LIST::const_iterator
#define  _TIME_EVENT_LIST_ITERATOR	TIME_EVENT_LIST_LIST::iterator

typedef std::pair< absoluteEventTime, timeEvent * >					schedule_pair;
typedef std::multimap< absoluteEventTime, timeEvent * >				timeEvent_schedule;
typedef std::multimap< absoluteEventTime, timeEvent * >::iterator	schedule_iterator;

typedef std::pair< absoluteEventTime, int >							start_pair;
typedef std::multimap< absoluteEventTime, int >						start_time_map;
typedef std::multimap< absoluteEventTime, int >::iterator			start_iterator;

// Define boundaries of values of a time event.
const float DEFINED_INTENSITY_MIN =		0.0f;
const float UNDEFINED_INTENSITY =		-1.0f;
const eventTime DEFINED_DURATION_MIN =	0;
//The time event should be longer than the longest frame.  
//Since the application should play at 20 frames per second or faster, 
//the time events should be longer than 1/20th of a second.  
//With some error margin, 50 milliseconds should be the shortest time event.
const eventTime VALID_DURATION_MIN =	50;
const eventTime MAX_VALID_DURATION =	1000000;

// Parallel tracks for a time event.
typedef int trackType;

// Whether or not to loop the section when it ends.
const bool DEFAULT_LOOP = false;

const int start_map_max_size = 100000;




class timeEvent
// An event in time, which may contain a list of sub-events.
{
	friend timeEventTrack;
	friend timeEventSection;
public:
	// Construct an event.
	// TODO:  Extend as template pointer to hold a variety of kinds of events.  Replace intensity with event pointer.
	// TODO:  Consider timeEventContainer class for non-terminal timeEvents.
	timeEvent( eventTime duration_msec = UNDEFINED_DURATION, 
		float new_intensity = UNDEFINED_INTENSITY );
	virtual ~timeEvent();

	// Time events are simplified by only having a duration.
	// Therefore, when a time event is inserted, it is considered to require the durational amount of time.
	// Any subsequent event is offset by the duration.
	// If space is needed between events, specify a duration of an spacing event.
	// The time, in seconds, when an event ends.
	// If this event has a list of subevents, then the end time is the end of those subevents.
	virtual eventTime getDuration();
	// The time event should be longer than the longest frame.  
	// Since the application should play at 30 frames per second or faster, 
	// the time events should be longer than 1/30th of a second.  
	// With some error margin, 100 milliseconds should be the shortest time event.
	void setDuration( eventTime newDurationMsec );

    // Push an event onto the sub event list.
    virtual void push_back( timeEvent * newTimeEvent );

	// Intensity of an event.  For a musical beat, this can be the volume and aftershock sensation.
	// Typical scale is from 0 to 1.
	virtual float getIntensity();
	virtual void setIntensity( float newIntensity );

	// DEPRECATE:  Replaced by start time map paradigm.
	//// Start the event, and increment by count, usually 1, unless going backwards.
	//virtual void start( eventTime currentTime );

	//// The number of times this time event has started.
	//int getStartCount();
	//void setStartCount( int newStartCount );

	//// The latest time when an event was started.
	//eventTime getStartTime();
	//void setStartTime( eventTime newStartTime );

	// Reset start count and time for this time event and each of its children.
	void recursivelyReset();

	// Useful to copy or modify the start time map.
	// Precondition:  Schedule was built for this time event.
	inline start_time_map & get_start_map()
	{ return _start_map; }

#ifdef DEBUG_TIME_EVENT
    // Test some necessary properties of a timeEvent object.
    timeEvent * getUnitTest();
#endif // def DEBUG_TIME_EVENT

protected:
	// The list of events.  An event may recursively contain a list of events.
	// Precondition:  Becareful not to include oneself in the list.
	// All members in a list are offset by the start time of the event containing the list.
	// Protected from accidental modification.
	// TODO:  Consider:  Generalize _getSubEventList as polymorphic getSubList.
	TIME_EVENT_LIST * _getSubEventList();

	// List is not public to enable alterations to the list, such as pushing or popping members.
	// Access through accessor method.
    // Subclasses may access directly.
	TIME_EVENT_LIST _subEventList;

private:
	/** Schedule the event and its children.
	Also populate duration data.
	Precondition:  Schedule was cleared or otherwise prepared for new additions.
	*/
	void _recursively_schedule( absoluteEventTime next_time, 
			timeEvent_schedule & existing_schedule );
	/** Return iterator to FIRST occurrence of timeEvent after the previous time and before or during the current time.
		If time does not occur, then return iterator at the end of the start time map.
		Precondition:  Start time map was constructed.
	*/
	start_iterator _get_start_iterator( eventTime previous_time, eventTime current_time );
	// Precondition:  start time map is not larger than max int -1.
	inline int _get_max_start_count() { 
		assert( this->_start_map.size() < start_map_max_size );
		return (int)this->_start_map.size() + 1; 
	};

	// Access to enable construction of the start times and start counts.
	start_time_map _start_map;

    eventTime _duration;

	// TODO:  Decouple intensity from time event.  Subclass musicEvent?
	float _intensity;

	// The number of times this time event has started.
	// DEPRECATE:  Replace with start_time_map.
	//int _startCount;
	//eventTime _startTime;
};



/*
Consolidated structure of time events for convenient reference and data transfer.

time cue structure:
	event list
	period
	modulus
	offset
	duration
	min
	max
*/
class time_cue 
{
public:
	time_cue() {
		start_map.clear();
		event_period = 0;
		event_modulus = 0;
		offset_msec = 0;
		duration_msec = 0;
		start_count_min = 0;
		start_count_max = 999999;
	};

	time_cue (
		start_time_map new_start_time_map,
		int new_event_period = 0,
		int new_event_modulus = 0,
		eventTime new_offset_msec = 0,
		eventTime new_duration_msec = 0,
		int new_start_count_min = 0,
		int new_start_count_max = 999999
		)
	{
		start_map = new_start_time_map;
		event_period = new_event_period;
		event_modulus = new_event_modulus;
		offset_msec = new_offset_msec;
		duration_msec = new_duration_msec;
		start_count_min = new_start_count_min;
		start_count_max = new_start_count_max;
	};

	time_cue (
		timeEvent * new_time_event,
		int new_event_period = 0,
		int new_event_modulus = 0,
		eventTime new_offset_msec = 0,
		eventTime new_duration_msec = 0,
		int new_start_count_min = 0,
		int new_start_count_max = 999999
		)
	{
		start_map.clear();
		start_map = new_time_event->get_start_map();
		event_period = new_event_period;
		event_modulus = new_event_modulus;
		offset_msec = new_offset_msec;
		duration_msec = new_duration_msec;
		start_count_min = new_start_count_min;
		start_count_max = new_start_count_max;
	};

	start_time_map start_map;
	int event_period;
	int event_modulus;
	eventTime offset_msec;
	eventTime duration_msec;
	int start_count_min;
	int start_count_max;
};


class timeEventTrack : public timeEvent
// This time event is dedicated to a track.
// A timeEventTrack stores a stack of iterators which retain static access to current position at
// each level of depth
{
	friend timeEventSection;
public:
	// Only use default constructor when declaring; default constructor is not a fully constructed track.
    // Construct a track with the type of track and the time at which the track begins.
    timeEventTrack( trackType newTrack = -1, eventTime newOriginTime = UNDEFINED_TIME );
	~timeEventTrack();

	// Each instrument, such as guitar, or bass, has a unique track.  
	trackType getTrack();
	void setTrack( trackType setTrack );

    // Origin time is when the track begins.  All time references are offset by this amount.
    eventTime getOriginTime();
    void setOriginTime( eventTime newOriginTime );

    //// Each frame of event scheduling, update with the current time.
    //void update( eventTime currentTime );

	// DEPRECATE:  Replaced by start time map paradigm.
	//// Return pointer to current time event in this track.
	//// Precondition:  Track has been updated at least once, 
	//// in order to ensure the current event has been defined.
	//timeEvent * getCurrentEvent();
	// Return true if the event or its subevent is the current event.
	bool isEventOccurring( timeEvent * aTimeEvent );

	//// Get the absolute start time for the current event.
	//// If the track has ended, then return the track aggregate time.
	//eventTime getCurrentEventStartTime();
	//eventTime getCurrentEventEndTime();

	// Get and set if the track has ended.  Once ended events no longer occur.
	bool getHasEnded( eventTime current_time );
	void setHasEnded( bool newHasEnded );

	// Start or restart the track and assign an origin time.
	void start( eventTime newOriginTime );

	// TODO ... if needed.
	//// Precondition:  The track has been updated at least once.
	//// Get the change in intensity, as a difference from last frame's intensity.
	//float getIntensityIncrease();

	// Return true if the event is starting during this frame, otherwise return false.
	// Precondition:  The time events were updated this frame.
	bool is_event_starting( timeEvent * aTimeEvent, 
		eventTime previousTime, eventTime currentTime );

#ifdef DEBUG_TIME_EVENT
    timeEventTrack * getUnitTest();
#endif // def DEBUG_TIME_EVENT

protected:

	// Construct schedule from events in the track.
	void _insert_into_schedule( timeEvent_schedule & existing_schedule );

private:
	// Reset current event start time and current event.
	void _reset();

	void _incrementEvent( _TIME_EVENT_ITERATOR_LIST * event_iterator_stack_pointer );
	//// DEPRECATE:  Replaced by start time map paradigm.
	//// Assign the current event start time.
	//void _setCurrentEventStartTime( eventTime newTime );
	// Advance the current event to the next event in the list.
	//void _incrementCurrentEvent();
	//void _decrementCurrentEvent();
	//void _decrementEvent( _TIME_EVENT_ITERATOR_LIST * event_iterator_stack_pointer );
	//// Push iterators of sub events onto the stack until terminal subevent reached.
	//// Root event is defined to avoid infinite loop (and its stack overflow) 
	//// which may be caused when an event calls for subevents from itself.
	//void _startEachSubEvent( timeEvent * rootEvent );
	//void _endEachSubEvent( timeEvent * rootEvent );

	void _insert_each_sub_event_into_schedule( timeEvent * rootEvent, 
		_TIME_EVENT_ITERATOR_LIST * event_iterator_stack_pointer,
		timeEvent_schedule & existing_schedule,
		absoluteEventTime & absolute_time );

	//// Intensity increase must be updated during each update.
	//void _updateIntensityIncrease();

	trackType _track;
	// DEPRECATE:  Replaced by start time map paradigm.
	//eventTime _currentEventStartTime;
	//dataState _currentEventStartTime_status;
    eventTime _originTime;
	// End of the track has been reached.
	bool _hasEnded;

	// DEPRECATE:  Replaced by start time map paradigm.
	//// An event iterator stack enables recursive timed events.  
	//// Once a sub event ends, then the stack is popped.
	//// The stack stores location at a depth in the recursive list of time events.
	//// For example, the sequence of positions with nested lists:  [0,[0,1,2],[0,1]]
	//// Is a compound list of [0,1,2] in which 1 has positions [0,1,2] and 2:  [0,1].
	//// The stack defined as [1,2] written more conveniently as [*1,**2]:
	//// points to this position:  [0,*[0,1,**2],[0,1]]
	//_TIME_EVENT_ITERATOR_LIST _eventIteratorStack;

	//// The difference in intensity from last update.
	//float _oldIntensity;
	//float _intensityIncrease;
};



class timeEventSection
// Manage sequence of events and recursively of subsequence each frame.
{
public:
	timeEventSection();
	virtual ~timeEventSection();

	// Get aggregate intensity from all tracks.
	float getIntensity();

    // Return duration of longest track in units of seconds.
    eventTime getDuration();
	// Manually override aggregate duration.
    void setDuration( eventTime newDuration );

	// Start or restart all the tracks and assign them an origin time.
	void start( eventTime newOriginTime );
	// Of all tracks, get the soonest origin time.
	eventTime getOriginTime();

	// The time when the section ends.
	clock_t inline get_end_time() {	return getDuration() + getOriginTime();	};

    // Each frame of event scheduling, update with the current time.
	// Necessary to call in the frame before queries to have the current time for time events.
    void update( eventTime currentTime );

	// Return whether all tracks have ended.
	bool getHasEnded( eventTime currentTime );

	// If true, repeat the section after it ends.
	bool getLoop();
	void setLoop( bool enableLoop );

	// Tracks are processed in parallel.
	TIME_EVENT_TRACK_LIST * getTrackList();
	void push_back( timeEventTrack * newTrack );
	// Return the first track of a given type.
	// If there are two or more tracks of the same type, only the first is returned.
	// Therefore to ensure access, each track should have a different type.
	// TODO:  Consider:  Track ID on a deque.
	timeEventTrack * getTrack( trackType targetTrackType );

	//// Get aggregate increase in intensity since last update.
	//float getIntensityIncrease();

	/** Test whether or not a time event is starting.
	P period:  occurrence in a cyclical period.  Such as once every 6, 12, occurence, etc.
		Period of 1 or less is overriden to always be true.
	M modulus:  the Mth occurence in a period.  0th means the end of a cycle.
		If modulus is greater than or equal to period, it can never be true.
		basic formula:  return ( start_count % period ) == modulus;
	offset_msec:  Offset to start.  1000 is one second in the future; 
		-2000 is two seconds in the past.	
	duration_msec:  Subtract additional time from the lower bound of the window.  
		To this duration the current frame's duration is added.
	*/
	inline bool timeEventSection :: is_event_starting(
		timeEvent * event_pointer,
		const int event_period = 0,
		const int event_modulus = 0,
		const eventTime offset_msec = 0,
		const eventTime duration_msec = 0,
		const int start_count_min = 0,
		const int start_count_max = 999999
		)
	{
		#ifdef _DEBUG
			assert( NULL != event_pointer && event_pointer
				&& "timeEventSection :: is_event_starting:  Time event is not defined." );
		#endif // _DEBUG

		return is_event_starting(
			& (start_time_map)( event_pointer->get_start_map() ),
			event_period,
			event_modulus,
			offset_msec,
			duration_msec,
			start_count_min,
			start_count_max );
	}


	bool timeEventSection :: is_event_starting(
			TIME_EVENT_LIST * event_list_pointer,
			const int event_period = 0,
			const int event_modulus = 0,
			const eventTime offset_msec = 0,
			const eventTime duration_msec = 0,
			const int start_count_min = 0,
			const int start_count_max = 999999
		)
	// Return true if the event is starting during this frame, otherwise return false.
	// Uses same method as a single time event and is bound by the same restrictions.
	{
			#ifdef _DEBUG
				assert( IS_POINTER_INITIALIZED( event_list_pointer ) 
					&& "timeEventSection :: is_event_starting:  Time event list is not defined." );
			#endif // _DEBUG

		// TODO:  Optimize this hotspot by parallel processing of independent iteration.
		// http://publib.boulder.ibm.com/infocenter/pseries/v5r3/index.jsp?topic=/com.ibm.vacpp7a.doc/compiler/ref/ruompplp.htm
		//#pragma omp parallel for
		FOR_ITER_POINTER( _TIME_EVENT_ITERATOR, time_event_iterator, event_list_pointer )
		{
			timeEvent * this_time_event = * time_event_iterator;
			if (   is_event_starting( this_time_event, 
				event_period, event_modulus, offset_msec, duration_msec, 
				start_count_min, start_count_max )   )
			{
				return true;
			}
		}

		return false;
	}


	start_iterator timeEventSection :: get_next_start_iterator(
			start_time_map * start_map_pointer,
			const int event_period = 0,
			const int event_modulus = 0,
			const eventTime offset_msec = 0,
			const eventTime duration_msec = 0,
			const int start_count_min = 0,
			const int start_count_max = 999999
		)
	{
		/*
		Procedure:  is the 
				specified event starting
				for the modulus(th) time
				within the period?
			Assert that the time event is defined.
			Get the start iterator of the specified event.
			If the start iterator does not equal the end of the start map for the specified event:
				Set the start count to the second part of the contents of the start iterator.
				If modulo of the start count at the period equals the modulus
				or the period is less than or equal to one:
					Return true.
			Return false.
		*/

		#ifdef _DEBUG
			assert( 0 <= duration_msec
				&& "timeEventSection :: is_event_starting:  Negative duration is undefined." );
		#endif // def _DEBUG

		// Get begin iterator in range.
		//		lower_bound actually returns iterator to the first value greater than or equal to given value.
		//		upper_bound returns iterator to first value strictly greater than given value.
		//		http://www.cplusplus.com/reference/stl/multimap/equal_range.html
		const start_iterator begin_iterator = start_map_pointer->upper_bound( 
			( _previous_time + offset_msec - duration_msec )   );
		// If begin iterator is not at the end of the list:
		if ( start_map_pointer->end() != begin_iterator )
		{
			// If the next occurrence is not after the end of the window:
			if ( (*begin_iterator).first <= _current_time + offset_msec )
			{
				// Get end iterator of range.
				const start_iterator end_iterator = start_map_pointer->upper_bound( 
					( _current_time + offset_msec )   );
				// If end iterator is at the end of the list:
				// Or start time of begin iterator is less than or equal to end iterator:
				if (   start_map_pointer->end() == end_iterator 
					|| (*begin_iterator).first <= (*end_iterator).first   )
				{
					// TODO:  Optimize this hotspot by parallel processing of independent iteration.
					// http://publib.boulder.ibm.com/infocenter/pseries/v5r3/index.jsp?topic=/com.ibm.vacpp7a.doc/compiler/ref/ruompplp.htm
					//#pragma omp parallel for
					for ( start_iterator this_iterator = begin_iterator;
						this_iterator != end_iterator;
						this_iterator ++ )
					{
						int start_count = (*this_iterator).second;
						if ( start_count_min <= start_count 
							&& start_count <= start_count_max )
						{
							if ( event_period <= 1 
								|| event_modulus == ( start_count % event_period )   )
							{
								return this_iterator;
							}
						}
					}
				}
			}
		}
		return start_map_pointer->end();
	}

	inline bool timeEventSection :: is_event_starting(
			start_time_map * start_map_pointer,
			const int event_period = 0,
			const int event_modulus = 0,
			const eventTime offset_msec = 0,
			const eventTime duration_msec = 0,
			const int start_count_min = 0,
			const int start_count_max = 999999
		)
	{
		return start_map_pointer->end() != 
			get_next_start_iterator(
				start_map_pointer,
				event_period,
				event_modulus,
				offset_msec,
				duration_msec,
				start_count_min,
				start_count_max
			);
	}
	inline bool is_event_starting( const time_cue & a_time_cue )
	{
		return is_event_starting( 
			& (start_time_map)(a_time_cue.start_map),
			a_time_cue.event_period,
			a_time_cue.event_modulus,
			a_time_cue.offset_msec,
			a_time_cue.duration_msec,
			a_time_cue.start_count_min,
			a_time_cue.start_count_max
		);
	}

	inline bool is_time_starting( const absoluteEventTime & target_time ) const
	{
		return ( target_time <= _current_time 
			&& _previous_time < target_time );
	}

	// Return next start time that meets the criteria.  If none, return UNDEFINED_TIME.
	const absoluteEventTime get_next_start_time( const time_cue & a_time_cue,
		const eventTime individual_offset = 0 )
	{
		start_iterator next_iterator = get_next_start_iterator( 
			& (start_time_map)(a_time_cue.start_map),
			a_time_cue.event_period,
			a_time_cue.event_modulus,
			a_time_cue.offset_msec + individual_offset,
			a_time_cue.duration_msec + MAX_VALID_DURATION,
			a_time_cue.start_count_min,
			a_time_cue.start_count_max
		);
		if ( a_time_cue.start_map.end() != next_iterator )
		{
			return (*next_iterator).first;
		}
		else
		{
			return UNDEFINED_TIME;
		}
	}

	// Is the event occurring?
	bool is_event_occurring( timeEvent * event_pointer );

	// DEPRECATE:  Use optional arguments version instead.
	//// Return true if the event is starting during this frame, otherwise return false.
	//// Precondition:  The time events were updated this frame.
	//bool is_event_starting( timeEvent * aTimeEvent );
	//bool is_event_starting( TIME_EVENT_LIST * time_event_list );
	//bool isEventOccurring( timeEvent * aTimeEvent );
	//// After P counts of a time event
	//bool is_event_starting(
	//		timeEvent * the_event,
	//		int event_period,
	//		int event_modulus
	//	);
	//bool is_event_starting(
	//		TIME_EVENT_LIST * the_event_list,
	//		int event_period,
	//		int event_modulus
	//	); 
	//// Return whether an event is starting in the future or past based on this frame's window.
	//bool is_event_starting(
	//	TIME_EVENT_LIST * the_event,
	//	eventTime offset_msec
	//	);
	//bool is_event_starting(
	//	timeEvent * the_event,
	//	eventTime offset_msec
	//	);

	// Get the number of cycles that have been completed.
	void setCycleDuration( eventTime newDuration );
	eventTime getCycleDuration();
	int getCycleCount( eventTime currentTime );
	// If the cycle was incremented this frame, return true.
	bool getCycleIncremented( eventTime currentTime );

#ifdef DEBUG_TIME_EVENT
	// Test some necessary functionality of time event section 
	// and its component tracks and events.
    timeEventSection * getUnitTest();
#endif // def DEBUG_TIME_EVENT

private:
	void _build_schedule();
	timeEvent_schedule _schedule;

	TIME_EVENT_TRACK_LIST _trackList;
	// Manually override aggregate duration and origin time.
	eventTime _duration;
	eventTime _originTime;
	// Whether or not to loop the section once it ends.
	bool _loop;
	// Duration of a component cycle, such as the measure within a phrase.
	eventTime _cycleDuration;
	// Time of update during previous frame.
	eventTime _previous_time;
	// Store time of current frame to copy to time of previous on next frame.
	eventTime _current_time;
};

#ifdef DEBUG_TIME_EVENT
// Boring music to standardize behavior tests.
timeEvent * get_test_music_track0_phrase();
timeEventTrack * get_test_music_track0_track();
timeEventSection * get_test_music_section();
#endif // def DEBUG_TIME_EVENT



// Insert start map from time event list to start map.
inline void insert_into_start_map( start_time_map & receiving_map, TIME_EVENT_LIST & scheduled_list )
{
	FOR_ITER_OBJECT( _TIME_EVENT_ITERATOR, eventIterator, scheduled_list )
    {
		start_time_map scheduled_event_map = (*eventIterator)->get_start_map();
		receiving_map.insert( scheduled_event_map.begin(),
			scheduled_event_map.end()   );
    }
}






/* 
RHYTHM DETECTION SYSTEM

Rhythm event list is a list of time events.
Next event in rhythm is a time event in the rhythm event list.
Stimulus is an external event, such as the avatar firing.

RHYTHM USAGE

For example suppose there is a drum and guitar beat.
drum:
	light, heavy
guitar measure:
	light note, power chord

Construct and update each rhythm in parallel:

	// During initialization:

	drum_beat_list.clear();
	drum_beat_list.push_back( heavy_drum_beat );
	drum_beat_list.push_back( light_drum_beat );

	guitar_measure.clear();
	guitar_measure.push_back( light_guitar_note );
	guitar_measure.push_back( guitar_power_chord );

	drum_rhythm.construct(...drum_beat_list...);
	guitar_rhythm.construct(...guitar_measure...);

	// During frame update:

	drum_rhythm.update();
	guitar_rhythm.update();

	if ( drum_rhythm.entered() )		
		// ...

	if ( guitar_rhythm.entered() )		
		// ...

By the way, as you code the rhythms, please use descriptive variable names:

   drum_and_bass_list.push_back( heavy_bass_beat );
   guitar_riff_rhythm.update();

Names like track6, track20, are too arbitrary to remember.
You can change the names of the underlying time events in glyrus_music.*
Understanding the rhythm meaning is very helpful for making the rhythm matching fun!

Of course the logic can include different and complex rhythm conditions, 
besides just rhythm.entered();

Further examples of usage are shown in glyrus_content.cpp, 
methods _init_rhythm() and _update_rhythm().



RHYTHM DESIGN

Update a rhythm:
If rhythm not entered:
	If rhythm continued:
		If rhythm not entered:
			Enter rhythm.
	Else (rhythm broken):
		Stop matching rhythm.
Else (rhythm entered):
	If rhythm not continued:
		Exit rhythm.
	Else:
		Continue rhythm.


Rhythm continued
		stimulus
		next event in rhythm
	:
	If stimulus:
		If stimulus in rhythm window:
			Return true.
		Else (stimulus out of rhythm window):
			Return false.
	Else:
		[Missed rhythm?]
		If next event expired:
			Return false.
		Else return true.

Enter:
	Set rhythm entered to true.
	Begin matching effects.

Exit:
	if consecutive rhythm count less than or equal to 0:
		End matching effects.
	else:
		End rhythm effects.
	Set rhythm entered to false.
	Reset next event in rhythm.

Continue:
	Update rhythm effects.
	If simulus in window:
		Increment next event in rhythm.
		If rhythm complete:
			Increment consecutive rhythm count.
			If consecutive count greater than 1:
				Begin in synch effects.
	if consecutive rhythm count less than or equal to 0:
		Update matching effects.
	else:
		Update in synch effects.

In rhythm window
		current event
		target event
		window duration
	:
	If current event is target event:
		If current event started before expire time:
			Return true.
	Return false.
	


RHYTHM ARCHITECTURE DESIGN
	waiting list
	current wait
	current wait expire time
	consecutive count
		
	update
	enter
	continue
	exit

	is entered
	is continued

waiting event
	stimulus condition
	time event

TODO:
Generalize stimulus condition.
Get next start time to allow a window before and after an event's start time.
Enable testing of rhythm event in future or past.  Requires expansion of time event.

Time event section
	Get next start time
			of an event.
*/

#define IS_STIMULUS( _signal_ )		\
	getSimulation()->getAvatar()->event_occurred( _signal_ )

const int UNDEFINED_COUNT = -1;

class rhythm : public simulationObject
{
public:
	rhythm( simulation * new_simulation = NULL, 
		TIME_EVENT_LIST * new_rhythm_event_list = NULL, 
		simulation_event_type new_signal = FIRE_EVENT,
		int new_max_false_positive_count = 2,
		clock_t new_window_duration_msec = 400,
		clock_t new_window_offset_msec = -150
		);
	// Identical to constructor, needed for non-dynamic variable assignment, 
	// since private iterator cannot be passed by copy from a variable that goes out of scope.
	void construct( simulation * new_simulation = NULL, 
		TIME_EVENT_LIST * new_rhythm_event_list = NULL, 
		simulation_event_type new_signal = FIRE_EVENT,
		int new_max_false_positive_count = 2,
		clock_t new_window_duration_msec = 400,
		clock_t new_window_offset_msec = -150
		);

	TIME_EVENT_LIST * rhythm_event_list;
	// Only call reset after populating the event list 
	// or intentionally resetting the rhythm memory.
	void reset();
	// entered get accessor
	bool get_entered();
	// Update once per frame.
	void update();
	bool get_in_window();
	void set_in_window( bool new_in_window );

	int get_consecutive_count();
	bool is_first_signal_in_window();

	inline clock_t get_window_offset_msec() { return window_offset_msec; };
	inline clock_t get_window_duration_msec() { return window_duration_msec; };

	// convenient time values
	absoluteEventTime start_cue_time;
	absoluteEventTime end_cue_time;
	absoluteEventTime enter_cue_time;
	absoluteEventTime exit_cue_time;
	absoluteEventTime consecutive_count_time;
	absoluteEventTime last_cue_time;

private:
	void set_entered( bool new_entered );
	
	void enter();
	void update_continue( bool is_stimulus );
	void exit();

	void increment_event();

	// Only valid during rhythm update.
	bool _update_is_in_window();
	// Update when stimulus occurs.
	inline bool _update_stimulus();
	// Window is still open.
	bool is_still_available();
	// Determine if the rhythm was started or continued.
	bool is_started( bool is_stimulus );
	bool is_continued( bool is_stimulus );
	// Update once per frame before testing time values.
	void update_time();

	bool _entered;
	_TIME_EVENT_ITERATOR next_event_iterator;
	// Window of time for the next event.
	clock_t next_event_start_time;
	clock_t next_event_expire_time;
	int consecutive_count;
	simulation_event_type signal;
	// Whether not the window is currently active.
	bool _in_window;
	// Offset of event, to have buffer on either side of event.
	clock_t window_offset_msec;
	clock_t window_duration_msec;

	// Whether or not stimulus input.  Update each frame.
	bool _is_stimulus;
	// Mistaken stimulus count.  Signals received off the beat.
	int false_positive_count;
	int max_false_positive_count;
	// Missed stimulus count.  Beats missed.
	int false_negative_count;
	int max_false_negative_count;
	// Count of stimulus in the window.
	int count_in_window;
	int max_count_in_window;

};



// File: glyunit.h
// Objective: Implement unit behavior, ships, AI, tactics.
// Author: David Kennerly.
// Date: 2004-05-25
// Version: 0.5.7





// Objective: Define debugging code.
// #define DEBUG_UNIT

// forward declarations
class formation;
class formationManager;


// X and Y axis as constants
const int X_AXIS = 0;
const int Y_AXIS = 1;




const int GAME_OBJECT_MAX = 256;
const int UNIT_COUNT_MAX = 64;
const int FORMATION_COUNT_MAX = 64;
// Define interval between ships being appended to a formation in milliseconds.
const int APPEND_SHIP_DELAY = 500; 

// Objective: Default distance for glyrus to move the right or left.
const glyNormalf GLYRUS_RIGHT_STEP = 2.0f * DEFAULT_SPEED;
// Objective: Define if Glyrus is recovering from a hit or interactive.
enum shipStatus{ STATUS_NORMAL, STATUS_RECOVER, STATUS_CINEMATIC };
const glyNormalf RECOVER_POS_X = 0.5f;
const glyNormalf RECOVER_POS_Y = -0.05f;
// Objecitve: Declare glyrus move types.
enum commandType{ MOVE_LEFT, MOVE_RIGHT, NULL_COMMAND };
// Objective: Define the duration for continuous movement.
const int CONTINUOUS_MOVE_MSEC = 100;
const clock_t RECOVER_MSEC = 2000;

// Firing decision constants.
const glyNormalf FIRE_HEIGHT_MAX = 2.0f;
const glyNormalf FIRE_HEIGHT_MIN = 0.0f;
const glyOrientationf FIRE_ARC_HALF = 60.0f;
const glyOrientationf FIRE_ORI_MIN = 180.0f - FIRE_ARC_HALF;
const glyOrientationf FIRE_ORI_MAX = 180.0f + FIRE_ARC_HALF;
const int FIRE_PERCENT = 15;
const int FIRE_DELAY_MSEC = 250;
// TODO:  Consolidate or organize intensity increase variables.
const float FIRE_INTENSITY_INCREASE_MIN = 0.15f;
// A value above any valid intensity increase.
const float INTENSITY_INCREASE_OVER_MAX = 999.9f;

// Bullet list
#define BULLET_LIST		std::list< bullet * >

// Nonmutating public iterator
#define BULLET_ITERATOR		BULLET_LIST::const_iterator
// Mutating protected iterator
#define _BULLET_ITERATOR	BULLET_LIST::iterator

const float GLYRUS_HEALTH = BASE_HEALTH;
// wide and thin in y
const float GLYRUS_SCALE = 1.25f * DEFAULT_SCALE;
const float GLYRUS_ASPECT_Y = 0.3f;

const float GLYRUS_Y = 0.0f;
const float GLYRUS_RECOVER_SPEED = 0.05f;
const clock_t GLYRUS_RECOVER_MSEC = 6000;
// Do not fire during recover animation.  TODO:  fix in application.
const clock_t GLYRUS_RECOVER_DISABLE_FIRE_MSEC = 2000;
const float GLYRUS_BULLET_SCALE = 0.5f * BULLET_SCALE;

/*
Avatar firing game mechanism

Firing is entertaining for users that prefer to 
	to release the fire button to fire once,
	or hold down fire button to fire continuously.
A user can fire most rapidly by tapping the fire button.
A user can fire continuously at a slower pace by holding the fire button.

During update of the user interface:
	If fire button is pressed during the current frame 
	but not during the previous frame:
		Request avatar to fire once.
	While fire button is held:
		Request avatar to fire continuously.



NPC fire synchronization
Trigger list pointer is a pointer to a list of time events.

For each trigger in trigger list:
	If trigger is starting:
		Fire.
*/

// Avatar fire intervals
const int glyrus_continuous_fire_interval_msec = 250;
const int glyrus_instant_fire_interval_msec = BULLET_DELAY_MSEC;

enum fire_rate_type{ instant_fire, continuous_fire };

//
// Unit declarations
//

class formation;






class ship : public mobileObject
{
	friend formation;
	friend formationManager;
public:
	ship();
	ship( glyPlanePos2fv newPlanePos, glyOrientationf newOrientation, simulation * newSimulation );
	// DEPRECATE:  ship( ship & prototypeShip );

	// Default values
	inline void ship :: initialize_ship( simulation * newSimulation = NULL )
	{
		initialize_mobile( newSimulation );

		setType( SHIP_SIM_TYPE );

		setParentFormation( NULL );

		setFireEnableTime( UNDEFINED_TIME );
		/* Fire only on beat.*/
		setFireIntensityIncreaseMin( FIRE_INTENSITY_INCREASE_MIN );
		_last_bullet = NULL;

		targetPlanePos = planePos;

		_fire_period = 0;
		_fire_modulus = 0;
		_fire_offset_msec = 0;
		_maneuver_period = 0;
		_maneuver_modulus = 0;
		_maneuver_offset_msec = 0;
		_override_cylinder = false;
		_override_cylinder_radius = FLOAT_UNDEFINED;

		_offset_msec = 0;
		_fire_interval_msec = BULLET_DELAY_MSEC;
		update_script = NULL;
		next_formation = NULL;
		fire_trigger_schedule_pointer = NULL;
		maneuver_trigger_schedule_pointer = NULL;
	}
	// Destructor should be virtual to ensure that a base class pointer calls the derived class destructor.
	// All base class destructors are called after the derived class destructor.
    virtual ~ship();
	virtual void destroy();

    // Fire a bullet of some sort.  Return the bullet for potential modification.
    virtual bullet * fire( int fire_interval_msec );
    // Decide to fire or not.  If so, then fire is called within.
    virtual void update_fire();
	virtual bool is_fire_trigger();
	virtual bool is_maneuver_trigger();

	// In range and orientation.
	bool is_in_fire_position();

	clock_t getFireEnableTime();
	void setFireEnableTime( clock_t newFireEnableTime );

    // Objective: Define a new arc motion for the ship to follow.
    // Precondition: relativeOrientation < 360.0, newTurnRadius < 1.0.
    // Postcondition: Target orientation equals orientation at end of arc.
    void setArc( bool newIsRight, 
        glyNormalf newTurnRadius, 
        glyOrientationf relativeOrientation );

    // Objective: Define a new linear motion for the ship to follow.
    // Precondition: distance < 1.0.
    // Postcondition: Target position set.
    void setLine( glyNormalf distance );

    // Objective: Process a frame of arc motion until done.
    // Precondition: Arc has been set.
    // Postcondition: Arc has completed.
    bool isArcDone();

    // Objective: Process a frame of line motion until done.
    // Precondition: Line has been set.
    // Postcondition: Line has completed.
	// TODO:  DEBUG:  Actually overshooting the line is possible and is not corrected during movement.
    bool isLineDone();

	// Move a segment around an arc.
    void arcStep( bool isRight, glyNormalf radius );
    // Objective: Turn left or right around a fixed focus point.

    // Number of ships in reserve.
    int get_life_count();
    void set_life_count( int new_life_count );

    formation * getParentFormation();

    motionType getActiveMotion();
    void setActiveMotion( motionType newActiveMotion );

	// Change formation or set formation.  
	// Add to that formation immediately and to simulation if needed.
	void setFormation( formation * newFormation );


	// The minimum firing intensity.
	float getFireIntensityIncreaseMin();
	void setFireIntensityIncreaseMin( float newfireIntensity );

	// For convenient access by other simulation classes only.
	// TODO: time_cue fire_cue;
	// TODO: time_cue maneuver_cue;
	//time_cue formation_cue;
	absoluteEventTime _formation_transfer_time;
	formation * next_formation;

	// For convenient access by other simulation classes only.
	int _fire_period;
	int _fire_modulus;
	clock_t _fire_offset_msec;

	int _maneuver_period;
	int _maneuver_modulus;
	clock_t	_maneuver_offset_msec;

	// Offset causes fire trigger and maneuver trigger to search ahead or behind (if negative) in time by milliseconds.
	clock_t _offset_msec;
	// Minimum delay of fire interval.
	clock_t _fire_interval_msec;

	// Update script, function pointer executed by simulation once per frame to enable custom behavior.
	void (*update_script)( ship * this_ship );

protected:
  
	// Directly set protected parent formation variable without validation or affecting formations.
    void setParentFormation( formation * newParentFormation );

	// Control the maneuver.
	void setManeuverIndex( int shipIndex, int maneuverIndex );

	// Once per frame decide if the ship will switch formation.
	inline void _update_formation_transfer()
	{
		if (   getSimulation()->getEventSection()->is_time_starting( _formation_transfer_time )   )
		{
			_formation_transfer_time = UNDEFINED_TIME;
			setFormation( next_formation );
			next_formation = NULL;
		}
	}

    motionType activeMotion;
    glyOrientationf targetOrientation;
    glyPlanePos2fv targetPlanePos;
    bool isRight;
    glyNormalf turnRadius;
    formation * parentFormation;
    int life_count;
	float _fireIntensityIncreaseMin;

	// When the next bullet may be fired.
	clock_t _fireEnableTime;
	// Save the last bullet fired for modification by rhythm or other events.
	bullet * _last_bullet;
	start_time_map * fire_trigger_schedule_pointer;
	start_time_map * maneuver_trigger_schedule_pointer;
};



#define SHIP_DECLARATION_BEGIN( _class_ )	\
	class _class_ : public ship\
	{\
	public:\
		/* Default constructor required for ship template that uses dynamic memory allocation.*/\
		_class_() { };\
		_class_( glyPlanePos2fv newPlanePos, glyOrientationf newOrientation, simulation * newSimulation );

#define SHIP_DECLARATION_END( _class_ )	\
	}

#define SHIP_DECLARATION( _class_ )	\
	SHIP_DECLARATION_BEGIN( _class_ )	\
	SHIP_DECLARATION_END( _class_ )


//
// generic enemy subclass ship implementation
//
// This must appear in the header file not the implementation file so that it will be included by other files such as content.
// TODO:  Default parameters with simulation first.
#define SHIP_CONSTRUCTOR_BEGIN( _class_, _CONSTANT_, _score_, _health_, _scale_, _speed_ )	\
	_class_ :: _class_( glyPlanePos2fv newPlanePos, glyOrientationf newOrientation, simulation * newSimulation )\
	{\
		initialize_ship( newSimulation );\
		\
		setType( _CONSTANT_##_SIM_TYPE );\
		set_score( _score_ );\
		set_health( _health_ );\
	\
		setPlanePos( newPlanePos );\
		/* Scale must be set after planePos, to avoid overwrite.*/\
		/* TODO:  Remove this fragility.*/\
		setScale( _scale_ );\
		setOrientation( newOrientation );\
		setSpeed( _speed_ );



#define SHIP_CONSTRUCTOR_END( _class_ )	\
	}


#define SHIP_CONSTRUCTOR( _class_, _CONSTANT_, _score_, _health_, _scale_, _speed_ )	\
	SHIP_CONSTRUCTOR_BEGIN( _class_, _CONSTANT_, _score_, _health_, _scale_, _speed_ )	\
	SHIP_CONSTRUCTOR_END( _class_ )




class glyrus : public ship
{
	friend simulation;
public:

	// Default constructor required for ship template that uses dynamic memory allocation.  
	glyrus() { };			// Base class initializes default values for undefined instance.
	glyrus( simulation * newSimulation );

    // Keyboard must initiate continuous move on Windows or else it's jerky.
    // TODO: Use DirectInput to avoid need for this.
    // Microsoft Windows OS resends keyboard inputs, so this is needed only for keyboard
    // or other inputs.
    void continuousMove();
    void continuousMoveLeft();
    void continuousMoveRight();
    // Mouse and frame update directly calls move methods.
	void moveRight();
	void moveLeft();
	// Request firing, such as through button on an input device.
	void fireRequest( fire_rate_type fire_rate );
    glyNormalf getPlanePosX();
    glyNormalf getPlanePosY();
    inline shipStatus getStatus();
    inline void setStatus( shipStatus newShipStatus );
    inline clock_t getCommandExpireTime();
    inline commandType getCommand();
    inline void setCommand( commandType newCommand );

	bool get_big_bullet();
	void set_big_bullet( bool new_big_bullet );

	bool get_fork_bullet();
	void set_fork_bullet( bool new_fork_bullet );
	// Percent of progress toward incrementing life count.
	inline int get_next_life_percent()
	// Percent of progress to next life.
	{
		return 
			100 *
			( this->get_score() % score_per_extra_life )
					/ 
			score_per_extra_life;
			// 2007-05-16:  incorrect.
			//100 * ( 
			//	( this->get_score() )
			//	- (score_per_extra_life * (extra_life_multiple - 1) ) 
			//)
			//	/ 
			//( score_per_extra_life * extra_life_multiple );
	}

	// Whether or not the object is in front of the glyrus and orientation is counter to glyrus.
	bool is_facing_me( mobileObject * aMobileObject );

protected:
	// Call once per frame, 
	void _update();

	// Permit firing
	void _firePermit( fire_rate_type fire_rate );
	// Toggle continuous fire on or off.
	void _toggle_continuous_fire( bool begin );

	// Modify the last bullet fired.
	void _modify_last_bullet();

	// Wrap around fire(...) call to apply fire effects. 
	// TODO:  Generalize wrapper for any ship?
	void fire_wrapper( eventTime interval_msec );
    void destroy();

    void recover();
	// Award extra life.
	void update_life_count();

	// Reset time values, useful if going backward in time or otherwise adjusting.
	void reset_time();

	// Continuous fire state.
	bool _is_continuous_fire;
	// When the continuous fire can be enabled.
	clock_t _continuous_fire_enable_time;

	shipStatus status;
    commandType command;
    clock_t commandExpireTime;
	clock_t disable_collision_expire_time;
    // fireType fire;

	// Larger, more damaging bullet.
	bool _big_bullet;
	// Three bullets instead of one.
	bool _fork_bullet;
	// Latest bullet pointer.
	bullet * newBullet;
	int extra_life_multiple;
	int score_per_extra_life;

	// When the glyrus has a bullet to shoot.
	bool _fire_request_pending;
	fire_rate_type _fire_rate;

}; // glyrus




//
// Formation global parameters.
//
enum controlType{
	DISTANCE_CONTROL,
	TIME_EVENT_CONTROL
};

// Maneuver parameters for a formation.
const int _DEFAULT_MANEUVER_INDEX = -1;
const int _MIN_MANEUVER_INDEX = 0;

/*
Music-synchronized maneuver.
On the start of the beat, interrupt default motion and execute the next dramatic motion.
Off the start of the beat, continue a default motion.
Start of the beat is defined as sudden increase in intensity,
which exceeds a threshold of minimum increase in intensity.

	default:  straight

	On each beat, increment through maneuver list:
		straight
		arc right, arc left
		arc right, arc right
		straight
		arc right

Maneuver list is composed of copies of maneuvers.
Maneuver list retains a static iterator.
Once maneuver list is exhausted, it loops.

	maneuver_list
		maneuver
		maneuver
		...

Following ships have a delay, so the incrementation is delayed.

Firing also occurs on the beat.

Example of spawning

Simulation spawns on beat through event list:
	null
	spawn
	spawn
	null
	spawn
	null
	null
*/



class formation : public mobileObject
// Objective: Define a formation of ships.
{
    friend formationManager;
public:
	// Only use default constructor for declaration; it is not fully constructed.
	formation();
    formation( glyPlanePos2fv newPlanePos, glyOrientationf newOrientation, 
		simulation * newSimulation );
	// Construct a partially constructed formation.
	void construct( glyPlanePos2fv newPlanePos, glyOrientationf newOrientation, 
					   simulation * newSimulation );
	// The appended ship does not necessarily appear.
	// The appended ship's activities are delayed by delay time msec.
    void appendShip( ship * newShip, clock_t addPendingTime );
    void init();
    int getLength();
    ship * getShip( int shipIndex );
    void insertShip( ship * newShip, bool already_in_simulation = false );
    void removeShip( ship * oldShip );
    void insertManeuver( maneuver * newManeuver );
    // Move the formation as a train or snake, each frame.
    // Determine if members are firing projectiles.
    void update();

	// NPC fire synchronization
	// Trigger list pointer is a pointer to a list of time events.
	// For each trigger in trigger list:
	//		If trigger is starting:
	// 			Fire.
	start_time_map * fire_trigger_schedule_pointer;
	start_time_map * maneuver_trigger_schedule_pointer;

	maneuver * getDefaultManeuver();
	void setDefaultManeuver( maneuver * newManeuver );

	float getIntensityThreshold();

	// Control ships' use of maneuvers by event or distance.
	controlType getManeuverControl();
	void setManeuverControl( controlType newManeuverControl );

	// On entry into formatin, set ship speed to new speed.  Speed is not reset.
	inline void set_overwrite_ship_speed( 
		bool overwrite_or_not,
		float new_ship_speed = FLOAT_UNDEFINED )
	{
		overwrite_ship_speed = overwrite_or_not;
		if ( FLOAT_UNDEFINED != new_ship_speed )
		{
			ship_speed = new_ship_speed;
		}
	};
	// Is the ship speed being overwritten?
	inline bool get_overwrite_ship_speed() { return overwrite_ship_speed; };
	// What is the value of the ship speed being overwritten?
	inline float get_ship_speed() { return ship_speed; };

protected:
	// Control a ship by distance or by timeEvent.
	void moveShipByDistance( int shipIndex );
	void moveShipByTimeEvent( int shipIndex );
	void moveShipByManeuver( int shipIndex, maneuver * currentManeuver );
	maneuver * getManeuver( int shipIndex );
	void resetMotionList( int shipIndex );

	// Set the maneuver.
	void setManeuverIndex( int shipIndex, int maneuverIndex );

	// Advance a ship to the next maneuver.
	void incrementManeuver( int shipIndex );

	bool isManeuverComplete( int shipIndex );

	ship * shipList[ UNIT_COUNT_MAX ];
    int shipList_length;
    // Objective: Declare sequence of maneuvers.
    maneuver * maneuverList[ MOTION_COUNT_MAX ];
    int maneuverList_length;
//    motion * motionList[ MOTION_COUNT_MAX ];
//    int motionList_length;
    // Objective: Declare a current motion index for each ship.
    int mtn_i[ UNIT_COUNT_MAX ];
    // Objective: Declare a current maneuver index for each ship.
    int mnv_i[ UNIT_COUNT_MAX ];
    // Objective: Declare the origin from which ships appear.
	// TODO:  Deprecate or fully implement.
    glyPlanePos2fv originPlanePos;
    // Objective: Declare number of ships waiting to appear onscreen, in the formation.
    ship * pendingList[ UNIT_COUNT_MAX ];
    int pending_length;
    clock_t pendingTime[ UNIT_COUNT_MAX ];

	// Default maneuver used by timeEvent control.
    maneuver * _defaultManeuver;
	controlType maneuverControl;

	// Overwrite ship speed.
	bool overwrite_ship_speed;
	float ship_speed;

	// Whether or not the unit is moving in default maneuver.
	bool default_mnv[ UNIT_COUNT_MAX ];
};



class formationManager : public simulationObject
// Objective: Manage all formations in the game.
{
	friend simulation;
public:
    formationManager();
	~formationManager();

	// Return number of formations.
    int getLength();
	// Access a formation.
    formation * getFormation( int formationIndex );
    void insert( formation * newFormation );
	void remove( formation * old_formation );

    const int getCount_remaining();
    // TODO: Manage removing a formation.

	// Default global triggers for content.
	start_time_map * fire_trigger_schedule_pointer;
	start_time_map * maneuver_trigger_schedule_pointer;
	//DEPRECATE
	//TIME_EVENT_LIST * fire_trigger_list;
	//TIME_EVENT_LIST * maneuver_trigger_list;

protected:
    void init();
    void update();
	inline ship * _get_collision_ship_pointer( mobileObject * player_object )
	{
		int formation_ship_length;
		formation * this_formation;
		//  TODO:  Is it faster if a flat ship list is maintained?
	    for ( int f=0; f<list_length; f++ )
		{
			this_formation = list[f];
			formation_ship_length = this_formation->shipList_length;
			for ( int s=0; s<formation_ship_length; s++ )
			{
				if (   this_formation->shipList[s]->isCollision( player_object )   )
				{
					return this_formation->shipList[s];
				}
			}
		}
		return NULL;
	}

    formation * list[ FORMATION_COUNT_MAX ];
    int list_length;

};






// ********************************************
//
// End glyunit.h (Unit behavior)
//
// ********************************************







#endif // ndef _GLYRUS_BEHAVIOR_H_

