#ifndef _GLYRUS_BEHAVIOR_CPP_
#define _GLYRUS_BEHAVIOR_CPP_

#include "glyrus_behavior.h"
 
#include <assert.h>		// Using assert(...)

// Always return false from is_fire_trigger
//#define DISABLE_FIRE_TRIGGER
// Always return false from is_maneuver_trigger
//#define DISABLE_MANEUVER_TRIGGER
     
motion :: motion( motionType newType, glyNormalf newDistance )
// Objective: Define a line.
{
    type = newType;
    distance = newDistance;
}



motion :: motion( motionType newType, bool newIsRight, 
        glyNormalf newRadius, glyOrientationf newOrientation )
// Objective: Define an arc.
{
    type = newType;
    isRight = newIsRight;
    radius = newRadius;
    orientation = newOrientation;
}



maneuver :: maneuver()
{
    motionList_length = 0;
}


void maneuver :: clear()
{
	motionList_length = 0;
}



inline const int maneuver :: getLength()
// Objective: Return the length of a maneuver.
{
    return motionList_length;
}


motion * maneuver :: getMotion( int motion_i )
// Objective: Return the motion type at the index location.
{
    return motionList[ motion_i ];
}



void maneuver :: insert( motion * newMotion )
// Objective: Insert a motion at the end of a maneuver.
{
    motionList[ motionList_length ] = newMotion;
    motionList_length++;
    if ( motionList_length == MANEUVER_COUNT_MAX ) motionList_length--;
}




//
// class timeEvent implementation
//

// An event in time, which may contain a list of sub-events.

// Construct an event.
timeEvent :: timeEvent( eventTime newDurationMsec, float newIntensity )
{
	setDuration( newDurationMsec );
	setIntensity( newIntensity );
	_subEventList.clear();
	// DEPRECATE:  Replaced by start map paradigm.
	//setStartCount( 0 );
	//setStartTime( 0 );

}


timeEvent :: ~timeEvent()
// Because a time event points to component events that may be used elsewhere, sub-events are destructed.
{
	// Destroy each sub event.
	// TODO:  DEBUG:  Dangling pointers:  Multiple pointers to the same time event.
//	verify_and_delete_list( & _subEventList );
}


eventTime timeEvent :: getDuration()
// If a time event has a defined duration, then any sub-events are truncated to this duration.
// If this event has a list of subevents, then the end time is the end of those subevents.
{
    if ( DEFINED_DURATION_MIN <= _duration )
    {
        return _duration;
    }
    else if ( ! _getSubEventList()->empty() )
    {
        // Sum duration of the list.
        eventTime duration_sum = 0;
        // WARNING:  To access a method with an iterator, "const_iterator" cannot be used.
        FOR_ITER_POINTER( _TIME_EVENT_ITERATOR, eventIterator, _getSubEventList() )
        {
            if ( DEFINED_DURATION_MIN <= (*eventIterator)->getDuration() )
            {
                duration_sum += (*eventIterator)->getDuration();
            }
        }

		// Cache duration.
		setDuration( duration_sum );
        return duration_sum;
    }
    else
    {
        return UNDEFINED_DURATION;
    }
}



void timeEvent :: setDuration( eventTime newDurationMsec )
// The incremental time, in milliseconds seconds, when an event ends.
// Any duration less than 0.0f is considered undefined.
// 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.
{
	if ( VALID_DURATION_MIN <= newDurationMsec 
		|| UNDEFINED_DURATION == newDurationMsec )
	{
		_duration = newDurationMsec;
	}
	else
	{
#ifdef ASSERT_TIME_EVENT_DURATION
		assert( 0 );
		// DEPRECATE:  logMessage( "! timeEvent :: setDuration:  newDurationMsec less than VALID_DURATION_MIN." );
#endif // def ASSERT_TIME_EVENT_DURATION
		_duration = newDurationMsec;
	}
}



TIME_EVENT_LIST * timeEvent :: _getSubEventList()
// 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.
{
    return & _subEventList;
}



float timeEvent :: getIntensity()
// Intensity of an event.  For a musical beat, this can be the volume and aftershock sensation.
// Typical scale is from 0 to 1.
{
    return _intensity;
}



void timeEvent :: setIntensity( float newIntensity )
{
	_intensity = newIntensity;
}



void timeEvent :: push_back( timeEvent * newTimeEvent )
// Push an event onto the sub event list.
{
	// TODO:  Verify that event does not already exist by recursive search of sub event tree.
	// This can be done by find algorithm adapted to this structure.
    _subEventList.push_back( newTimeEvent );
}



void timeEvent :: recursivelyReset()
// Reset start count for this time event and each of its children.
{
	_start_map.clear();
	// DEPRECATE:  Replaced by start map paradigm.
	//setStartCount( 0 );
	//setStartTime( 0 );
	FOR_ITER_OBJECT( _TIME_EVENT_ITERATOR, eventIterator, _subEventList )
	{
		(*eventIterator)->recursivelyReset();
	}
}



start_iterator timeEvent :: _get_start_iterator( eventTime previous_time, eventTime current_time )
// 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.
{
	/*
	Procedure:  Is this event starting,
			after the previous time,
			and before or during the current time?
		Set lower bound time to previous time + 1.
		Set upper bound time to current time + 1.
		Set the lower bound at lower bound time.
		If the lower bound is not defined:
			Return the end of the start map.
		Set the upper bound at upper bound time.
		If the upper bound is not defined:
			Return the lower bound.
		If the start time of the pair in the lower bound
		is less than or equal to the start time of the pair in the upper bound at upper bound time:
			Return the lower bound.
		Otherwise:
			Return the end of the start map.
	*/

	// 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
	start_iterator upper_iterator = _start_map.upper_bound( previous_time );
	if ( _start_map.end() == upper_iterator )
	{
		return _start_map.end();
	}

	// Precondition:  Upper iterator points to a member of the map.
	if ( (*upper_iterator).first <= current_time )
	{
		return upper_iterator;
	}
	else
	{
		return _start_map.end();
	}
}


/*
abstract preorder
	if this is defined:
		operate on this
		for children:
			preorder child

abstract postorder
	if this is defined:
		for children:
			postorder child
		operate on this

Recursively schedule the event,
		at the next time
		:
	Assert the event is defined.
	Set current schedule time to next schedule time.
	For each child in the sub event list of the event:
		Recursively schedule the event at the next time.
		Increment next time by duration of the child.

	Schedule the event at current schedule time.
	Set duration of the event to next time - current schedule time.
*/

/** Schedule the event and its children.
Also populate duration data.
Precondition:  Schedule was cleared or otherwise prepared for new additions.
*/
void timeEvent :: _recursively_schedule( absoluteEventTime next_time, 
			timeEvent_schedule & existing_schedule )
{
	assert( this && NULL != this 
		&& "timeEvent :: _recursively_schedule:  event not defined." );

	absoluteEventTime current_time = next_time;

	FOR_ITER_OBJECT( _TIME_EVENT_ITERATOR, event_iterator, _subEventList )
	{
		(*event_iterator)->_recursively_schedule( next_time, existing_schedule );
		next_time += (*event_iterator)->getDuration();
	}

	// Schedule this event
	existing_schedule.insert(   schedule_pair( current_time, this )   );
	// For each event pushed onto the stack:  Increment that event's start count.
	int start_count = _get_max_start_count();
	this->_start_map.insert( start_pair( current_time, start_count ) );
	// Burn duration of containing events.
	if ( UNDEFINED_DURATION == this->_duration )
	{
		this->setDuration( next_time - current_time );
	}
}



#ifdef DEBUG_TIME_EVENT

timeEvent * timeEvent :: getUnitTest()
// Construct a sequence in a musical measure from time events.
// Return a pointer to the track constructed by the test.
{
	// TODO:  "delete" after each "new"
	printf( "timeEvent * timeEvent :: getUnitTest:\n" );
    timeEvent * beat0 = new timeEvent( SEC2MSEC( 0.25f ), 0.0f );
    timeEvent * beat00 = new timeEvent( SEC2MSEC( 1.0f ), 0.0f );
    timeEvent * beat1 = new timeEvent( SEC2MSEC( (0.25f + 0.25f) ), 0.75f );
    timeEvent * beat2 = new timeEvent( SEC2MSEC( 0.5f ), 0.6f );
    timeEvent * beat3 = new timeEvent( SEC2MSEC( 0.5f ), 1.0f );
    printf( "beat1->_getSubEventList().empty() = %d\n", beat1->_getSubEventList()->empty() );
    if ( true != beat1->_getSubEventList()->empty() ) { return NULL; }
    timeEvent * measureEnd = new timeEvent(   SEC2MSEC( (2.6666f - (2.3333f - 0.6666f)) )   );

    timeEvent * musicMeasure = new timeEvent();
    musicMeasure->push_back( beat0 );
    musicMeasure->push_back( beat1 );
    musicMeasure->push_back( beat0 );
    musicMeasure->push_back( beat2 );
    musicMeasure->push_back( beat00 );
    musicMeasure->push_back( beat3 );
    musicMeasure->push_back( measureEnd );
    // INVALID:  The event will not be added with this syntax:
    //! musicMeasure->_getSubEventList().push_back( beat0 );
    //! musicMeasure->_getSubEventList().push_back( beat1 );
    //! musicMeasure->_getSubEventList().push_back( measureEnd );
    printf( "musicMeasure->_getSubEventList().empty() = %d\n", musicMeasure->_getSubEventList()->empty() );
    if ( false != musicMeasure->_getSubEventList()->empty() ) { return NULL; }

    timeEvent * musicPhrase = new timeEvent();
    musicPhrase->push_back( musicMeasure );
    musicPhrase->push_back( musicMeasure );
    musicPhrase->push_back( musicMeasure );
    printf( "musicPhrase->_getSubEventList().empty() = %d\n", musicPhrase->_getSubEventList()->empty() );
    if ( false != musicPhrase->_getSubEventList()->empty() ) { return NULL; }

    printf( "beat0->getDuration() = %d\n", beat0->getDuration() );
    printf( "beat1->getDuration() = %d\n", beat1->getDuration() );
    printf( "musicMeasure->getDuration() = %d\n", musicMeasure->getDuration() );
    printf( "3 * musicMeasure->getDuration() = %d\n", (3 * musicMeasure->getDuration()) );
    printf( "musicPhrase->getDuration() = %d\n", musicPhrase->getDuration() );
    if ( musicPhrase->getDuration() != (3 * musicMeasure->getDuration()) ) { return NULL; }

    return musicPhrase;
}

#endif // def DEBUG_TIME_EVENT

// 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

timeEventTrack :: timeEventTrack( trackType newTrack, eventTime newOriginTime )
{
    setTrack( newTrack );
    setOriginTime( newOriginTime );
	_subEventList.clear();

	_reset();
}



void timeEventTrack :: _reset()
// Reset current event start time and current event.
{
	_start_map.clear();

	// DEPRECATE:  Replaced by start time map paradigm.
	//_intensityIncrease = 0.0f;
	//_oldIntensity = DEFINED_INTENSITY_MIN;
	//_currentEventStartTime = UNDEFINED_TIME;
	//_currentEventStartTime_status = UNDEFINED_DATA;
	setHasEnded( false );

	//if ( ! _eventIteratorStack.empty() )
	//{
	//	_eventIteratorStack.clear();
	//}
}



timeEventTrack :: ~timeEventTrack()
{
	// TODO:  DEBUG delete conflict:  _subEventList is destroyed in base class?
	// Destroy each sub event.
	// verify_and_delete_list( & _subEventList );
}



trackType timeEventTrack :: getTrack()
// Each instrument, such as guitar, or bass, has a unique track.  
{
    return _track;
}


void timeEventTrack :: setTrack( trackType newTrack )
{
    _track = newTrack;
}



bool timeEventTrack :: getHasEnded( eventTime current_time )
{
	if ( _hasEnded )
	{
		return true;
	}
	else
	{
		return (   getDuration() < ( current_time - getOriginTime() )   );
	}
}



void timeEventTrack :: setHasEnded( bool newHasEnded )
{
	_hasEnded = newHasEnded;
	// DEPRECATE:  
	//if ( _hasEnded )
	//{
	//	_currentEventStartTime_status = UNDEFINED_DATA;
	//}
}



void timeEventTrack :: start( eventTime newOriginTime )
// Restart the track and assign an origin time.
{
	setOriginTime( newOriginTime );
	_reset();
	// For each event, reset start count.
	recursivelyReset();
}



void timeEventTrack :: _insert_into_schedule( timeEvent_schedule & existing_schedule )
// Construct schedule from events in the track.
{
	// If the child list is not empty:
	if ( ! _getSubEventList()->empty() )
	{
		absoluteEventTime absolute_time = _originTime;
		_TIME_EVENT_ITERATOR_LIST schedule_event_iterator_stack;
		schedule_event_iterator_stack.push_front( _getSubEventList()->begin() );
		// While the current event has expired but this track is not the current event,
		// and the track has not ended:
		while ( ! schedule_event_iterator_stack.empty() )
		{
			// If the next event has a child list, then push an iterator to the child onto the stack.
			_insert_each_sub_event_into_schedule( *(schedule_event_iterator_stack.front()), 
				& schedule_event_iterator_stack,
				existing_schedule, absolute_time );

			// Increment the current event.
			_incrementEvent( & schedule_event_iterator_stack );
		}
	}
}



void timeEventTrack :: _insert_each_sub_event_into_schedule( timeEvent * rootEvent, 
	   _TIME_EVENT_ITERATOR_LIST * event_iterator_stack_pointer,
		timeEvent_schedule & existing_schedule, 
		absoluteEventTime & absolute_time )
// Search for terminal sub event, pushing each onto the stack.
// Root event is defined to avoid infinite loop (and its stack overflow) 
// which may be caused when an event calls for subevents from itself.
// Increment absolute time based on terminal event duration.
{
	timeEvent * childEvent = rootEvent;
	// Map the root event.
	// Precondition:  Current event was already incremented and its start time was set.
	existing_schedule.insert(   std::pair< absoluteEventTime, timeEvent * > 
		( absolute_time, childEvent )   );

	// It is necessary to point to the subevent list directly, not get a copy.
	// This is necessary to recursively obtain component lists.
	// Get sub events from the current event.
    TIME_EVENT_LIST * eventList = rootEvent->_getSubEventList();
    while ( ! eventList->empty() )
    {
		// to retain recursive position, push front of sub events onto iterator stack.
        event_iterator_stack_pointer->push_front( eventList->begin() );

		// For each child event, schedule the child event.
		// Problem:  How should recursion be implemented?  Bottom-up, top-down?  
		childEvent = * eventList->begin();

		existing_schedule.insert(   schedule_pair( absolute_time, childEvent )   );
		// For each event pushed onto the stack:  Increment that event's start count.
		int start_count = childEvent->_get_max_start_count();
		childEvent->_start_map.insert( start_pair( absolute_time, start_count ) );

		// Iteratively search children events.
        eventList = childEvent->_getSubEventList();
    }

	// Increment absolute time from terminal event.
	if ( 0 <= childEvent->getDuration() )
	{
		absolute_time += childEvent->getDuration();
	}
	else
	{
		// Child event has invalid duration.
		assert( 0 <= childEvent->getDuration() );
	}
}


eventTime timeEventTrack :: getOriginTime()
// Origin time is when the track begins.  All time references are offset by this amount.
{
    return _originTime;
}



void timeEventTrack :: setOriginTime( eventTime newOriginTime )
{
    _originTime = newOriginTime;
}







void timeEventTrack :: _incrementEvent( _TIME_EVENT_ITERATOR_LIST * event_iterator_stack_pointer )
{
	// If the event iterator stack is not empty:
	if ( ! event_iterator_stack_pointer->empty() )
	{
		// While the event iterator stack is not empty and has a parent,
		// and the current event iterator is at the end of its parent list:
			// Pop the event iterator stack.
			// Get the current event iterator.
			// Get the parent list the iterator resides in.
		_TIME_EVENT_ITERATOR endIterator;
		_TIME_EVENT_ITERATOR eventIterator;
		do 
		{
			// Get the current event iterator.
			eventIterator = event_iterator_stack_pointer->front();
			timeEvent * parentEvent;

			// Get the parent list the iterator resides in.
			// The parent iterator is also the target of the iterator next in the stack.
			// If there is no parent, then this is the container.
			if ( 2 <= event_iterator_stack_pointer->size() )
			{
				_TIME_EVENT_ITERATOR_ITERATOR parentIterator = event_iterator_stack_pointer->begin();
				parentIterator ++;
				parentEvent = (*(*parentIterator));
				endIterator = parentEvent->_getSubEventList()->end();
			}
			else
			{
				parentEvent = this;
				endIterator = parentEvent->_getSubEventList()->end();
			}

			// If the event iterator stack is not empty.
			if (! event_iterator_stack_pointer->empty() )
			{
				// Get the current event iterator.  
				// Increment the current event iterator.
				eventIterator ++;

				// If the event iterator has reached the end of the list, pop the stack.
				if ( eventIterator ==  endIterator )
				{
					event_iterator_stack_pointer->pop_front();
				}
				// Otherwise, assign this new iterator to the top of the stack.
				else
				{
					event_iterator_stack_pointer->pop_front();
					event_iterator_stack_pointer->push_front( eventIterator );
				}
			}

		}
		while (   (! event_iterator_stack_pointer->empty() ) 
			&& ( eventIterator ==  endIterator )   );
	}
}




#ifdef DEBUG_TIME_EVENT

timeEventTrack * timeEventTrack :: getUnitTest()
// Construct a sequence in a musical measure from time events.
// TODO:  Reuse code from unit test of timeEvent.
{
	printf( "timeEventTrack * timeEventTrack :: getUnitTest:\n" );
	// Construct and test a time event.
    timeEvent * testEvent = new timeEvent();
    timeEvent * bassPhrase = testEvent->getUnitTest();

	// Construct and test a time event track.
	eventTime testTime = clock();
	timeEventTrack * bassTrack = new timeEventTrack( BASS_TRACK, testTime );
	bassTrack->push_back( bassPhrase );
	bassTrack->push_back( bassPhrase );
	bassTrack->push_back( bassPhrase );

    printf( "bassTrack->_getSubEventList().empty() = %d\n", bassTrack->_getSubEventList()->empty() );
    printf( "3 * bassPhrase->getDuration() = %d\n", (3 * bassPhrase->getDuration()) );
    printf( "bassTrack->getDuration() = %d\n", bassTrack->getDuration() );
    printf( "bassTrack->getOriginTime() = %d\n", bassTrack->getOriginTime() );

	// TODO:  Consolidate and loop repetitive tests.
	bassTrack->update( testTime );
    printf( "testTime = %d\n", testTime );
    printf( "bassTrack->getCurrentEvent()->getDuration() = %d\n", bassTrack->getCurrentEvent()->getDuration() );
    printf( "bassTrack->getCurrentEventStartTime() = %d\n", bassTrack->getCurrentEventStartTime() );
    printf( "bassTrack->getCurrentEventEndTime() = %d\n", bassTrack->getCurrentEventEndTime() );
	eventTime testTimeLater = testTime + 3000;
	bassTrack->update( testTimeLater );
    printf( "testTimeLater = %d\n", testTimeLater );
    printf( "bassTrack->getCurrentEvent()->getDuration() = %d\n", bassTrack->getCurrentEvent()->getDuration() );
    printf( "bassTrack->getCurrentEventStartTime() = %d\n", bassTrack->getCurrentEventStartTime() );
    printf( "bassTrack->getCurrentEventEndTime() = %d\n", bassTrack->getCurrentEventEndTime() );
	eventTime testTimeLater2 = testTimeLater + 3000;
	bassTrack->update( testTimeLater2 );
    printf( "testTimeLater2 = %d\n", testTimeLater2 );
    printf( "bassTrack->getCurrentEvent()->getDuration() = %d\n", bassTrack->getCurrentEvent()->getDuration() );
    printf( "bassTrack->getCurrentEventStartTime() = %d\n", bassTrack->getCurrentEventStartTime() );
    printf( "bassTrack->getCurrentEventEndTime() = %d\n", bassTrack->getCurrentEventEndTime() );
	eventTime testTimeEnd = testTimeLater + 900000;
	bassTrack->update( testTimeEnd );
    printf( "testTimeEnd = %d\n", testTimeEnd );
    printf( "bassTrack->getCurrentEvent() = %d\n", (int)bassTrack->getCurrentEvent() );
    printf( "bassTrack->getCurrentEventStartTime() = %d\n", bassTrack->getCurrentEventStartTime() );
    printf( "bassTrack->getCurrentEventEndTime() = %d\n", bassTrack->getCurrentEventEndTime() );

	// Reset hasEnded value so the track may restart.
	bassTrack->setHasEnded( false );

	start( clock() );
    return bassTrack;
}

#endif // def DEBUG_TIME_EVENT



#ifdef DEBUG_TIME_EVENT

timeEvent * get_test_music_track0_phrase()
// Construct a sequence in a musical measure from time events.
// Return a pointer to the track constructed by the test.
{
	timeEvent *track0_beat0 = new timeEvent(SEC2MSEC (0.5f),1.0f);
	timeEvent *track0_beat1 = new timeEvent(SEC2MSEC (1.5f),0.0f);
	timeEvent *track0_beat2 = new timeEvent(SEC2MSEC (2.0f),0.0f);

    timeEvent * track0_measureEnd = new timeEvent(   SEC2MSEC( 2.0f ), 0.0f   );

    timeEvent * track0_measure0 = new timeEvent();
    track0_measure0->push_back(track0_beat0);
	track0_measure0->push_back(track0_beat1);
	track0_measure0->push_back(track0_measureEnd);


	timeEvent * track0_measure1 = new timeEvent();
	track0_measure1->push_back(track0_beat2);
	track0_measure1->push_back(track0_beat2);
	track0_measure1->push_back(track0_beat2);
	track0_measure1->push_back(track0_beat2);
	track0_measure1->push_back(track0_beat2);
	track0_measure1->push_back(track0_beat2);
	track0_measure1->push_back(track0_beat2);

	timeEvent * test0 = new timeEvent();
	
   	timeEvent * track0_phrase = new timeEvent();
	track0_phrase->push_back(track0_measure0);
	track0_measure1->push_back(track0_beat2);
	track0_measure1->push_back(track0_beat2);
	track0_phrase->push_back(track0_measure0);
	track0_measure1->push_back(track0_beat2);
	track0_measure1->push_back(track0_beat2);
	track0_phrase->push_back(track0_measure0);
	track0_phrase->push_back(track0_measure0);
	track0_measure1->push_back(track0_beat2);
	track0_measure1->push_back(track0_beat2);
	track0_measure1->push_back(track0_beat2);
	track0_measure1->push_back(track0_beat2);


    return track0_phrase;
}



timeEventTrack * get_test_music_track0_track()
// Construct a sequence in a musical measure from time events.
// TODO:  Reuse code from unit test of timeEvent.
{
	const int track0 = 0;

	// Construct time event.
    timeEvent * track0_phrase = get_test_music_track0_phrase();
	
	// Construct track.
	clock_t now = clock();
	timeEventTrack * track0_track = new timeEventTrack( track0, now );
	track0_track->push_back( track0_phrase );
	track0_track->push_back( track0_phrase );
	
    return track0_track;
}




timeEventSection * get_test_music_section()
{
	timeEventSection * music_section = new timeEventSection();
	timeEventTrack * track0_track = get_test_music_track0_track();
	music_section->push_back( track0_track );

	clock_t now = clock();
	music_section->start( now );

	return music_section;
}


#endif // def DEBUG_TIME_EVENT
//
// class timeEventSection implementation
//

timeEventSection :: timeEventSection()
{
	setDuration( UNDEFINED_DURATION );
	setLoop( DEFAULT_LOOP );
	_previous_time = UNDEFINED_TIME;
	_current_time = UNDEFINED_TIME;
	setCycleDuration( UNDEFINED_TIME );
	_trackList.clear();
}




timeEventSection :: ~timeEventSection()
// Destruct each component track.
{
	// Do not delete component tracks, because these are in statically declared memory.
	// verify_and_delete_list( & _trackList );
}






eventTime timeEventSection :: getDuration()
// Get maximum duration from all tracks.
// If duration is predetermined, then truncate.
{
    if ( DEFINED_DURATION_MIN <= _duration )
    {
        return _duration;
    }
	else
	{
		eventTime maxDuration = 0;
		FOR_ITER_POINTER( TIME_EVENT_TRACK_ITERATOR, trackIterator, getTrackList() )
		{
			eventTime trackDuration = (*trackIterator)->getDuration();
			if ( DEFINED_DURATION_MIN <= trackDuration 
				&& maxDuration < trackDuration )
			{
				maxDuration = trackDuration;
			}
		}
		// Cache duration
		setDuration( maxDuration );

		return maxDuration;
	}
}



void timeEventSection :: setDuration( eventTime newDuration )
// Manually override aggregate duration.
{
	_duration = newDuration;
}



void timeEventSection :: start( eventTime newOriginTime )
// Start or restart all the tracks and assign them an origin time.
// Precondition:  Tracks are defined.
{	
	FOR_ITER_POINTER( _TIME_EVENT_TRACK_ITERATOR, trackIterator, getTrackList() )
	{
		timeEventTrack * track = (*trackIterator);
		track->start( newOriginTime );
	}
	// This enables update of frame to proceed efficiently.
	_previous_time = newOriginTime - 1;
	_current_time = newOriginTime - 1;
	_originTime = newOriginTime;

	_build_schedule();
}



eventTime timeEventSection :: getOriginTime()
// Of all tracks, get the soonest origin time.
{	
	if ( UNDEFINED_TIME != _originTime )
	{
		return _originTime;
	}
	else
	{
		// Undefined time must be greater than minimum time.
		eventTime minimumOriginTime = 999999;
		bool minimumOriginTimeDefined = false;
		FOR_ITER_POINTER( _TIME_EVENT_TRACK_ITERATOR, trackIterator, getTrackList() )
		{
			timeEventTrack * track = (*trackIterator);
			eventTime trackOriginTime = track->getOriginTime();
			if (    ( ! minimumOriginTimeDefined )
				||   minimumOriginTime > trackOriginTime   )
			{
				minimumOriginTime = trackOriginTime;
				minimumOriginTimeDefined = true;
			}
		}
		if ( minimumOriginTimeDefined )
		{
			// Cache origin time.
			_originTime = minimumOriginTime;
			return minimumOriginTime;
		}
		else
		{
			return UNDEFINED_TIME;
		}
	}
}



bool timeEventSection :: getHasEnded( eventTime current_time )
{
	return ( getDuration() < current_time );

}



void timeEventSection :: update( eventTime currentTime )
// Each frame of event scheduling, update with the current time.
{
	// DEPRECATE:  Replaced by start time map paradigm.
	//FOR_ITER_POINTER( _TIME_EVENT_TRACK_ITERATOR, trackIterator, getTrackList() )
	//{
	//	timeEventTrack * track = (*trackIterator);
	//	track->update( currentTime );
	//}

	// Previous time assigned.
	_previous_time = _current_time;
	// Store previous time at end of update for reference in a future frame.
	_current_time = currentTime;

	if (   getHasEnded( currentTime )   )
	{
		if (  getLoop()  )
		{
			// Restart the section.
			start( currentTime );
		}
	}
}



void timeEventSection :: push_back( timeEventTrack * newTrack )
{
	getTrackList()->push_back( newTrack );
}



TIME_EVENT_TRACK_LIST * timeEventSection :: getTrackList()
// Tracks are processed in parallel.
{
	return & _trackList;
}


timeEventTrack * timeEventSection :: getTrack( trackType targetTrackType )
// 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.
{
	FOR_ITER_POINTER( _TIME_EVENT_TRACK_ITERATOR, trackIterator, getTrackList() )
	{
		trackType thisTrackType = (*trackIterator)->getTrack();
		if ( targetTrackType == thisTrackType )
		{
			return (*trackIterator);
		}
	}
	return NULL;
}


bool timeEventSection :: getLoop()
{
	return _loop;
}



void timeEventSection :: setLoop( bool enableLoop )
// Repeat the section after it ends.
{
	_loop = enableLoop;
}






bool timeEventSection :: is_event_occurring( timeEvent * event_pointer )
// Return true if the event is active during this frame, otherwise return false.
// Precondition:  The time events were updated this frame.
{
	assert( NULL != event_pointer && event_pointer 
		&& "timeEventSection :: is_event_starting:  Time event is not defined." );

	start_iterator the_start_iterator = event_pointer->_get_start_iterator( 
		_previous_time - event_pointer->getDuration(), _current_time );
	if ( event_pointer->_start_map.end() != the_start_iterator )
	{
		return true;
	}
	return false;
}











void timeEventSection :: setCycleDuration( eventTime newDuration )
{
	_cycleDuration = newDuration;
}



eventTime timeEventSection :: getCycleDuration()
// Get the number of cycles that have been completed.
{
	return _cycleDuration;
}



int timeEventSection :: getCycleCount( eventTime currentTime )
{
	// Count the number of cycles completed since start of section.
	if (   UNDEFINED_TIME != getOriginTime() 
		&&   0 < getCycleDuration()   )
	{
		int cycleCount = ( currentTime - getOriginTime() ) / getCycleDuration();
		return cycleCount;
	}
	else
	{
		return UNDEFINED_TIME;
	}
}



bool timeEventSection :: getCycleIncremented( eventTime currentTime )
// Get if the cycle count incremented this frame and only this frame.
{
	if ( getCycleCount( _previous_time ) == getCycleCount( currentTime ) )
	{
		return false;
	}
	else
	{
		return true;
	}
}


void timeEventSection :: _build_schedule()
{
	_schedule.clear();
	FOR_ITER_POINTER( _TIME_EVENT_TRACK_ITERATOR, trackIterator, getTrackList() )
	{
		timeEventTrack * track = (*trackIterator);
		track->_recursively_schedule( track->getOriginTime(), _schedule );
		// DEPRECATE:  track->_insert_into_schedule( _schedule );
	}
}



#ifdef DEBUG_TIME_EVENT

timeEventSection * timeEventSection :: getUnitTest()
// Test some necessary functionality of time event section 
// and its component tracks and events.
{
	printf( "timeEventSection * timeEventSection :: getUnitTest:\n" );
	// Construct and test a track.
    timeEventTrack * testTrack = new timeEventTrack( BASS_TRACK, clock() );
    timeEventTrack * bassTrack = testTrack->getUnitTest();
	bassTrack->setTrack( BASS_TRACK );
    timeEventTrack * drumTrack = testTrack->getUnitTest();
	drumTrack->setTrack( DRUM_TRACK );
    timeEventTrack * guitarTrack = testTrack->getUnitTest();
	guitarTrack->setTrack( GUITAR_TRACK );

	// Construct and test a time event section.
	eventTime testTime = clock();
	timeEventSection * songSection = new timeEventSection();
	songSection->push_back( bassTrack );
	songSection->push_back( drumTrack );
	songSection->push_back( guitarTrack );

    printf( "songSection->getDuration() = %d\n", songSection->getDuration() );

	songSection->update( testTime );
    printf( "testTime = %d\n", testTime );
    printf( "songSection->getTrack( BASS_TRACK )->getCurrentEvent()->getDuration() = %d\n", 
		songSection->getTrack( BASS_TRACK )->getCurrentEvent()->getDuration() );
    printf( "songSection->getTrack( DRUM_TRACK )->getCurrentEvent()->getDuration() = %d\n", 
		songSection->getTrack( DRUM_TRACK )->getCurrentEvent()->getDuration() );
    printf( "songSection->getTrack( GUITAR_TRACK )->getCurrentEvent()->getDuration() = %d\n", 
		songSection->getTrack( GUITAR_TRACK )->getCurrentEvent()->getDuration() );

	eventTime testTimeLater = testTime + 3000;
	songSection->update( testTimeLater );
    printf( "testTimeLater = %d\n", testTimeLater );
    printf( "songSection->getTrack( BASS_TRACK )->getCurrentEvent()->getDuration() = %d\n", 
		songSection->getTrack( BASS_TRACK )->getCurrentEvent()->getDuration() );
    printf( "songSection->getTrack( DRUM_TRACK )->getCurrentEvent()->getDuration() = %d\n", 
		songSection->getTrack( DRUM_TRACK )->getCurrentEvent()->getDuration() );
    printf( "songSection->getTrack( GUITAR_TRACK )->getCurrentEvent()->getDuration() = %d\n", 
		songSection->getTrack( GUITAR_TRACK )->getCurrentEvent()->getDuration() );

	eventTime testTimeLater2 = testTimeLater + 3000;
	songSection->update( testTimeLater2 );
    printf( "testTimeLater2 = %d\n", testTimeLater2 );
    printf( "songSection->getTrack( BASS_TRACK )->getCurrentEvent()->getDuration() = %d\n", 
		songSection->getTrack( BASS_TRACK )->getCurrentEvent()->getDuration() );
    printf( "songSection->getTrack( DRUM_TRACK )->getCurrentEvent()->getDuration() = %d\n", 
		songSection->getTrack( DRUM_TRACK )->getCurrentEvent()->getDuration() );
    printf( "songSection->getTrack( GUITAR_TRACK )->getCurrentEvent()->getDuration() = %d\n", 
		songSection->getTrack( GUITAR_TRACK )->getCurrentEvent()->getDuration() );
	eventTime testTimeEnd = testTimeLater + 900000;

	songSection->update( testTimeEnd );
    printf( "testTimeEnd = %d\n", testTimeEnd );
    printf( "songSection->getTrack( BASS_TRACK )->getCurrentEvent()->getDuration() = %d\n", 
		songSection->getTrack( BASS_TRACK )->getCurrentEvent()->getDuration() );
    printf( "songSection->getTrack( DRUM_TRACK )->getCurrentEvent()->getDuration() = %d\n", 
		songSection->getTrack( DRUM_TRACK )->getCurrentEvent()->getDuration() );
    printf( "songSection->getTrack( GUITAR_TRACK )->getCurrentEvent()->getDuration() = %d\n", 
		songSection->getTrack( GUITAR_TRACK )->getCurrentEvent()->getDuration() );

	start( clock() );

    return songSection;
}

#endif // def DEBUG_TIME_EVENT


//
// end class timeEvent implementation
//




//
// class rhythm implementation
//

/*
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.

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
	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.

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




rhythm :: rhythm( simulation * new_simulation, 
				TIME_EVENT_LIST * new_rhythm_event_list, 
				simulation_event_type new_signal,
				int new_max_false_positive_count,
				clock_t new_window_duration_msec,
				clock_t new_window_offset_msec
				 )
{
	construct(    new_simulation, 
			new_rhythm_event_list, new_signal, new_max_false_positive_count,
			new_window_duration_msec, new_window_offset_msec		);
}


void rhythm :: construct( simulation * new_simulation, 
				TIME_EVENT_LIST * new_rhythm_event_list, 
				simulation_event_type new_signal,
				int new_max_false_positive_count,
				clock_t new_window_duration_msec,
				clock_t new_window_offset_msec
				)
{
	setSimulation( new_simulation ); 
	rhythm_event_list = new_rhythm_event_list;
	signal = new_signal;
	max_false_positive_count = new_max_false_positive_count;
	window_duration_msec = new_window_duration_msec;
	window_offset_msec = new_window_offset_msec;
	max_count_in_window = 1;
	max_false_negative_count = 1;

	// Initialize
	start_cue_time = UNDEFINED_TIME;
	end_cue_time = UNDEFINED_TIME;
	enter_cue_time = UNDEFINED_TIME;
	exit_cue_time = UNDEFINED_TIME;
	consecutive_count_time = UNDEFINED_TIME;
	last_cue_time = UNDEFINED_TIME;


	reset();
}


void rhythm :: reset()
{
	if (   IS_POINTER_INITIALIZED( rhythm_event_list )   )
	{
		next_event_iterator = rhythm_event_list->begin();
	}
	next_event_start_time = UNDEFINED_TIME;
	next_event_expire_time = UNDEFINED_TIME;
	consecutive_count = 0;
	false_positive_count = 0;
	set_entered( false );
	_in_window = false;
	count_in_window = 0;
	false_negative_count = 0;

}



bool rhythm :: _update_is_in_window()
{
	if ( UNDEFINED_TIME != next_event_start_time
		&& UNDEFINED_TIME != next_event_expire_time )
	{
		return (   
				next_event_start_time <= getSimulation()->get_time()
				&& getSimulation()->get_time() < next_event_expire_time
				);
	}
	else
	{
		return false;
	}
}



bool rhythm :: is_still_available()
{
	if ( UNDEFINED_TIME != next_event_expire_time )
	{
		if (   _simulation->get_time() < next_event_expire_time   ) 
		{
			return true;
		}
		else
		{
			++ false_negative_count;
			return ( false_negative_count <= max_false_negative_count );
		}
	}
	else
	{
		return true;
	}
}


/*
Update a rhythm:
If rhythm not entered:
	If rhythm started:
		Enter rhythm.
Else (rhythm entered):
	If rhythm not continued:
		Exit rhythm.
	Else:
		Continue rhythm.

*/
void rhythm :: update()
{
	// Clear last frame's events.
	event_list.clear();

	_is_stimulus = IS_STIMULUS(signal);

	update_time();

	if ( ! get_entered() )
	{
		if (   is_started( _is_stimulus )   )
		{
			// Increment event in order to reset expiration and ready next event.
			increment_event();
			enter();
		}
	}
	else // entered
	{
		if (   is_continued( _is_stimulus )   )
		{
			update_continue( _is_stimulus );
		}
		else
		{
			exit();
		}
	}
}


/*
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.
*/
bool rhythm :: is_continued( bool is_stimulus )
{
	if ( is_stimulus )
	{
		return _update_stimulus();
	}
	else
	{
		return is_still_available();
	}
}



bool rhythm :: is_started( bool is_stimulus )
{
	if ( is_stimulus )
	{
		if ( _update_is_in_window() )
		{
			return ( ++ count_in_window ) <= max_count_in_window;
		}
		else
		{
			return false;
		}
	}
	else
	{
		return false;
	}
}



inline bool rhythm :: _update_stimulus()
{
	if ( _update_is_in_window() )
	{
		return ( ++ count_in_window ) <= max_count_in_window;
	}
	else
	{
		return ( ++ false_positive_count ) <= max_false_positive_count;
	}
}


void rhythm :: update_time()
// Update once per frame before testing time values.
// Precondition:  Simulation time equals the event start time.
// Postcondition:  Time values for this frame are correct.
{
	clock_t now = getSimulation()->get_time();

	if (   IS_LIST_ITERATOR_VALID( rhythm_event_list, next_event_iterator )   )
	{
		// If time window is already defined and available, do not redefine.
		if (   UNDEFINED_TIME == next_event_expire_time 
			|| next_event_expire_time <= now   )
		{
			set_in_window( false );
			timeEvent * next_event_pointer = (*next_event_iterator);
			if (   getSimulation()->getEventSection()
					->is_event_starting( next_event_pointer, 0, 0, window_offset_msec )  
				)
			{
				next_event_start_time = now;
				next_event_expire_time = now + window_duration_msec;
				set_in_window( true );
			}
		}
	}
}



bool rhythm :: get_in_window()
{
	return _in_window;
}




void rhythm :: set_in_window( bool new_in_window )
{
	// Only trigger events if being set to something different.
	if ( new_in_window != _in_window )
	{
		_in_window = new_in_window;
		if ( new_in_window )
		{
			count_in_window = 0;
			event_list.push_back( START_CUE_EVENT );
			start_cue_time = getSimulation()->get_time();
		}
		else
		{
			event_list.push_back( END_CUE_EVENT );
			end_cue_time = getSimulation()->get_time();
		}
	}
}



/*
Enter:
	Set rhythm entered to true.
	Begin matching effects.
*/
void rhythm :: enter()
{
	set_entered( true );
	if (   IS_POINTER_INITIALIZED( rhythm_event_list )   )
	{
		next_event_iterator = rhythm_event_list->begin();
	}
	false_positive_count = 0;
	event_list.push_back( ENTER_CUE_EVENT );
	enter_cue_time = getSimulation()->get_time();

}



/*
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.
*/
void rhythm :: exit()
{
	reset();
	event_list.push_back( EXIT_CUE_EVENT );
	exit_cue_time = getSimulation()->get_time();
}


/*
Continue:
	Update rhythm effects.
	If simulus in window:
		Increment next event in rhythm.
	if consecutive rhythm count less than or equal to 0:
		Update matching effects.
	else:
		Update in synch effects.
*/
void rhythm :: update_continue( bool is_stimulus )
{
	if ( is_stimulus && _update_is_in_window() )
	{
		increment_event();
	}
}


/*
	Increment next event in rhythm.
	If rhythm complete:
		Increment consecutive rhythm count.
		If consecutive count greater than 1:
			Begin in synch effects.
*/
void rhythm :: increment_event()
// Postcondition:  Iterator is not at the end of the list.
//		event start and expire time are undefined, which invalidates _update_is_in_window()
{
	// Reset time.
	next_event_start_time = UNDEFINED_TIME;
	next_event_expire_time = UNDEFINED_TIME;

	// Cycle
	if (   IS_POINTER_INITIALIZED( rhythm_event_list )   )
	{
		if (   rhythm_event_list->end() != next_event_iterator   )
		{
			next_event_iterator ++;
		}

		if ( rhythm_event_list->end() == next_event_iterator )
		{
			if ( UNDEFINED_COUNT != consecutive_count )
			{
				consecutive_count ++;
			}
			else
			{
				consecutive_count = 0;
			}
			consecutive_count_time = getSimulation()->get_time();
			event_list.push_back( CONSECUTIVE_CUE_EVENT );
			next_event_iterator = rhythm_event_list->begin();
		}
		// Postcondition:  Iterator is not at the end of the list.

		// Precondition:  Iterator is not at the end of the list.
		if ( rhythm_event_list->back() == (*next_event_iterator) )
		{
			event_list.push_back( LAST_CUE_EVENT );
			last_cue_time = getSimulation()->get_time();
		}
	}
}


//
// entered get/set accessor implementation
//

bool rhythm :: get_entered()
{
	return this->_entered;
}



void rhythm :: set_entered( bool new_entered )
{
	this->_entered = new_entered;
}

int rhythm :: get_consecutive_count()
{
	return this->consecutive_count;
}


bool rhythm :: is_first_signal_in_window()
// Precondition:  Rhythm update completed this frame.
{
	return ( 1 == count_in_window && get_in_window() );
}


//
// end class rhythm implementation
//




//
// class ship implementations
//


ship :: ship()
{
	initialize_ship();

}



SHIP_CONSTRUCTOR( ship, SHIP, 0, BASE_HEALTH, DEFAULT_SCALE, DEFAULT_SPEED )





ship :: ~ship()
{
    // Remove itself from a formation.
    if ( parentFormation != NULL && parentFormation )
    {
        parentFormation->removeShip( this );
    }
}


void ship :: destroy()
{
    // Remove itself from a formation.
    if ( parentFormation != NULL && parentFormation )
    {
        parentFormation->removeShip( this );
    }
	// Remove itself from the simulation.
    getSimulation()->destroyMobileObject( this );
}



void ship :: setArc( 
    bool newIsRight, 
    glyNormalf newTurnRadius, 
    glyOrientationf relativeOrientation )
// 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.
{
    activeMotion = M_ARC;
    turnRadius = newTurnRadius;
    isRight = newIsRight;

    if ( isRight )
    {
        targetOrientation = orientation + relativeOrientation;
        while ( targetOrientation >= DEGREES_PER_REVOLUTION )
            targetOrientation -= DEGREES_PER_REVOLUTION;
    }
    else
    {
        targetOrientation = orientation - relativeOrientation;
        while ( targetOrientation < 0.0f )
            targetOrientation += DEGREES_PER_REVOLUTION;
    }
}



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

	targetPlanePos.move(   distance, MATH_ORI2RAD( orientation )   );
}



bool ship :: isArcDone()
// Objective: Process a frame of arc motion until done.
// Precondition: Arc has been set.
// Postcondition: Arc has completed.
{
    // TODO: Eliminate 0, 360 error. Example: 0.5 is only 1.0 from 359.5.
    // TODO: Eliminate overshooting angle error, especially at low frame rate.
    if (   (targetOrientation - orientation) < FLOAT_MARGIN 
        && (targetOrientation - orientation) > -FLOAT_MARGIN )
	{
        return true;
	}
    else 
    {
        arcStep( isRight, turnRadius );
        return false;
    }
}



bool ship :: isLineDone()
// 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.
{
    // TODO: Eliminate 0, 1 error. Example: 0.05 is only 0.1 from 0.95.
    //  Floating point needs a margin.
    // TODO: Eliminate error when frame rate is too gross to capture float margin.
    if (   (planePos.get_x() - targetPlanePos.get_x() ) < FLOAT_MARGIN 
        && (planePos.get_x() - targetPlanePos.get_x() ) > -FLOAT_MARGIN
        && (planePos.get_y() - targetPlanePos.get_y() ) < FLOAT_MARGIN 
        && (planePos.get_y() - targetPlanePos.get_y() ) > -FLOAT_MARGIN )
	{
        return true;
	}
    else 
    {
        return false;
    }
}



// TODO:  Merge with mobile object turn() method?
void ship :: arcStep( bool isRight, glyNormalf radius )
// Objective: Turn left or right around a fixed focus point.
//   TODO: Prevent overshooting a directed turn.
//   TODO: DEBUG: turn is faster than straight movement.
{
    if ( radius > 0.0f )
    {
        const float distance = getSpeed() * getSimulation()->getFrameInterval();
		// TODO: Actual arc distance?  
		// Isn't orientation delta actually a straight-line segment, not a calcuation of an arc?
        glyOrientationf orientationDelta = RAD2DEG( distance / radius );
        glyOrientationf newOrientation = orientation;

        // Objective: Rotate orientation for arc motion.
        if ( isRight )
        {
			event_list.push_back( MOVE_RIGHT_EVENT );
            // Objective: Protect against overshooting a target orientation.
            newOrientation += orientationDelta;

            if ( newOrientation < DEGREES_PER_REVOLUTION )
            // Precondition: No revolutions.
            {
                if ( newOrientation >= targetOrientation
                    && orientation < targetOrientation )
                    setOrientation( targetOrientation );
                else 
                    setOrientation( newOrientation );
            }
            else 
            // Precondition: One or more revolutions.
            {
                while ( newOrientation >= DEGREES_PER_REVOLUTION )
                    newOrientation -= DEGREES_PER_REVOLUTION;
                if ( newOrientation > targetOrientation )
                    setOrientation( targetOrientation );
                else if ( orientation < targetOrientation )
                    setOrientation( targetOrientation );
                else
                    setOrientation( newOrientation );
            }
        }
        else // ( isLeft )
        {
			event_list.push_back( MOVE_LEFT_EVENT );

            // Objective: Protect against overshooting a target orientation.
            newOrientation -= orientationDelta;

            if ( newOrientation >= 0.0f )
            // Precondition: No revolutions.
            {
                if ( newOrientation < targetOrientation
                    && orientation > targetOrientation )
                    setOrientation( targetOrientation );
                else 
                    setOrientation( newOrientation );
            }
            else // ( newOrientation < 0 )
            // Precondition: One or more revolutions.
            {
                while ( newOrientation < 0.0f )
                    newOrientation += DEGREES_PER_REVOLUTION;
                if ( newOrientation < targetOrientation )
                    setOrientation( targetOrientation );
                else if ( orientation > targetOrientation )
                    setOrientation( targetOrientation );
                else
                    setOrientation( newOrientation );
            }
        }
    }

}



int ship :: get_life_count()
{
    return life_count;
}



void ship :: set_life_count( int new_life_count )
{
    life_count = new_life_count;
}


motionType ship :: getActiveMotion()
{
    return activeMotion;
}



void ship :: setActiveMotion( motionType newActiveMotion )
{
    activeMotion = newActiveMotion;
}


void ship :: setParentFormation( formation * newParentFormation )
{
    parentFormation = newParentFormation;
}

formation * ship :: getParentFormation()
{
    return parentFormation;
}


bullet * ship :: fire( int fire_interval_msec )
// Fire a projectile.
{
	/*
		Fire at interval:
			If fire expire time is less than or equal to now:
				Create a bullet.
				Set fire expire time to interval + now.
	*/

    // Create expiration time for bullet delay.
	clock_t now = getSimulation()->get_time();
	if ( getFireEnableTime() <= now ) {
	    setFireEnableTime( now + fire_interval_msec );

		// Create a bullet.
		// TODO:  Consider generalizing by create projectile method which sub-classes may overwrite.
		bullet * newBullet = new bullet( getPlanePos(), getOrientation(), getSimulation() );
		// Set bullet to same team as the shooting ship.
		newBullet->setTeam( getTeam() );
		// Assign an owner, so it may be queried, to do things like have a different model, explosion, or effects.
		newBullet->set_owner( this );
		getSimulation()->insertMobileObject( newBullet );
		// Place fire event on event list.
		event_list.push_back( FIRE_EVENT );

		// Save the last bullet fired for modification by rhythm or other events.
		_last_bullet = newBullet;

		return newBullet;
	}
	return NULL;
}

bool ship :: is_in_fire_position()
{
	if ( FIRE_HEIGHT_MIN <= planePos.get_y() && planePos.get_y() <= FIRE_HEIGHT_MAX 
		 && FIRE_ORI_MIN <= getOrientation() && getOrientation() <= FIRE_ORI_MAX     )
	{
		return true;
	}
	else
	{
		return false;
	}
}



void ship :: update_fire()
// Decide to fire or not.
{
	// 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.

	if ( is_in_fire_position() )
	{
		if ( is_fire_trigger() )
		{
			fire( _fire_interval_msec );
		}
	}
}


#define FIRE_TRAVEL_DURATION_MSEC	\
	get_travel_duration_msec( getOrientation(), \
	getPlanePos().get_y(), BULLET_SPEED )


inline bool ship :: is_fire_trigger()
{
#ifndef DISABLE_FIRE_TRIGGER
	if (   IS_POINTER_INITIALIZED( fire_trigger_schedule_pointer )   )
	{
	// 2007-05-20 : Is arrive on event contributing to overly dense bullets?
	//#define FIRE_ARRIVES_ON_EVENT
	#ifndef FIRE_ARRIVES_ON_EVENT
		return getSimulation()->getEventSection()
			-> is_event_starting( fire_trigger_schedule_pointer, 
				_fire_period, _fire_modulus, 
				_fire_offset_msec + _offset_msec );
	#else
		//
		return getSimulation()->getEventSection()
			-> is_event_starting( fire_trigger_schedule_pointer, 
				_fire_period, _fire_modulus, 
				FIRE_TRAVEL_DURATION_MSEC + _offset_msec );
		// 2007-05-16 feedback:  all tentacle bullets arrived at once.
		//		FIRE_TRAVEL_DURATION_MSEC );
	#endif // def FIRE_ARRIVES_ON_EVENT
	}
#endif // def DISABLE_FIRE_TRIGGER

	return false;
}



inline bool ship :: is_maneuver_trigger()
{
#ifndef DISABLE_MANEUVER_TRIGGER
	if (   IS_POINTER_INITIALIZED( maneuver_trigger_schedule_pointer )   )
	{
		return getSimulation()->getEventSection()
			-> is_event_starting( maneuver_trigger_schedule_pointer, 
				_maneuver_period, _maneuver_modulus, 
				_maneuver_offset_msec + _offset_msec );
	}
#endif // ndef DISABLE_FIRE_TRIGGER

	return false;
}


clock_t ship :: getFireEnableTime()
{
	return _fireEnableTime;
}



void ship :: setFireEnableTime( clock_t newFireEnableTime )
{
	_fireEnableTime = newFireEnableTime;
}



float ship :: getFireIntensityIncreaseMin()
{
	return _fireIntensityIncreaseMin;
}



void ship :: setFormation( formation * newFormation )
// Change formation or set formation.  Add to that formation immediately and to simulation if needed.
{
	if ( parentFormation != newFormation )
	{
		if ( parentFormation && NULL != parentFormation )
		{
			getParentFormation()->removeShip( this );
		}

		if ( NULL != newFormation )
		{
			newFormation->insertShip( this, 
				(NULL != getSimulation()) );
		}
		else
		{
			setParentFormation( newFormation );
		}
	}
}



void ship :: setFireIntensityIncreaseMin( float newfireIntensity )
{
	_fireIntensityIncreaseMin = newfireIntensity;
}





//
// class glyrus implementations
//

glyrus :: glyrus( simulation * newSimulation )
{
	addToSimulation( newSimulation );

	setType( GLYRUS_SIM_TYPE );
	setTeam( PLAYER );
	set_health( BASE_HEALTH );
	_full_health = get_health();
	_damage_this_frame = 0.0f;

	set_owner( NULL );

	setCollisionDamage( BASE_DAMAGE_RATE );

    planePos.set_x( 0.5f );
    planePos.set_y( RECOVER_POS_Y );
    setOrientation( 0.0f );
    setScale( DEFAULT_SCALE, GLYRUS_ASPECT_Y );
	// TODO:  Refactor fragility.
	// Plane position is affected by orientation and scale.
	targetPlanePos = planePos;
    setSpeed( 0.0f );

    set_score( 0 );

	_is_continuous_fire = false;
	_fire_request_pending = false;
	_last_bullet = NULL;

	set_big_bullet( false );
	set_fork_bullet( false );
	extra_life_multiple = 1;
	score_per_extra_life = 0;

    setStatus( STATUS_RECOVER );
	reset_time();

	getSimulation()->insertMobileObject( this );
}


void glyrus :: _update()
{
	// Update avatar
	if ( _fire_request_pending )
	{
		_firePermit( _fire_rate );
	}

	// Player control override.
    if ( getStatus() == STATUS_RECOVER )
        recover();
    else
		// TODO:  can continuousMove cause repeated input?
        continuousMove();

	update_life_count();

	// Precondition:  position was already updated this frame.
	// Cancel movement until next move request.
	_velocity_x = 0.0f;
}



void glyrus :: reset_time()
{
	commandExpireTime = UNDEFINED_TIME;
	_continuous_fire_enable_time = UNDEFINED_TIME;
	setFireEnableTime( UNDEFINED_TIME );
}



void glyrus :: continuousMove()
// Objective: Move continuously until command expires.
{
    if ( getSimulation()->get_time() < commandExpireTime )
        // TODO: Optimization: Replace switch with pointer to function?
        switch ( command )
        {
        case MOVE_LEFT:
            moveLeft();
            break;
        case MOVE_RIGHT:
            moveRight();
            break;
        case NULL_COMMAND:
        default:
            break;
        }
    else
    {
        // TODO:  Export tilt to graphics.  Simulation signals event.
        // neutralizeTilt();
    }
}

void glyrus :: continuousMoveLeft()
// Objective: Initiate a moveLeft from keyboard, which is resent about 2-4 times per second.
{
    setCommand( MOVE_LEFT );
}



void glyrus :: continuousMoveRight()
// Objective: Initiate a moveRight from keyboard, which is resent about 2-4 times per second.
{
    setCommand( MOVE_RIGHT );
}



void glyrus :: moveRight()
{
    switch ( status )
    {
    case STATUS_NORMAL:
        command = MOVE_RIGHT;
		event_list.push_back( MOVE_RIGHT_EVENT );
        // Objective: Tilt right.
        // TODO:  Export tilt to graphics.  Provide event signal.
        // addTilt( MOVE_RIGHT );
		_velocity_x = GLYRUS_RIGHT_STEP;
   //     planePos.set_x( planePos.get_x()  
			//+ GLYRUS_RIGHT_STEP * getSimulation()->getFrameInterval() );
        break;
    case STATUS_RECOVER:
    // Objective: Don't move when recovering.
    // TODO: Disable in glyui.h instead of here.
        // Objective: Interrupt continuous movement.
        command = MOVE_RIGHT;
		event_list.push_back( MOVE_RIGHT_EVENT );
        if ( planePos.get_y() < FLOAT_MARGIN && planePos.get_y() > -FLOAT_MARGIN )
        {
			if ( disable_collision_expire_time < getSimulation()->get_time() )
			{
	            setStatus( STATUS_NORMAL );
			}

			_velocity_x = GLYRUS_RIGHT_STEP;
			//planePos.set_x( planePos.get_x()  
			//	+ GLYRUS_RIGHT_STEP * getSimulation()->getFrameInterval() );
        }
        break;
    case STATUS_CINEMATIC:
    // Objective: Don't move when in a noninteractive sequence.
    default:
        break;
    }
}



void glyrus :: moveLeft()
{

    switch ( status )
    {
    case STATUS_NORMAL:
        command = MOVE_LEFT;
		event_list.push_back( MOVE_LEFT_EVENT );
        // Objective: Tilt left.
        // TODO:  Export tilt to graphics.  Provide event signal.
        // addTilt( MOVE_LEFT );
		_velocity_x = 0.0f - GLYRUS_RIGHT_STEP;
   //     planePos.set_x( planePos.get_x()  
			//- GLYRUS_RIGHT_STEP * getSimulation()->getFrameInterval() );
        break;
    case STATUS_RECOVER:
    // Objective: Don't move when recovering.
    // TODO: Disable in glyui.h instead of here.
        // Objective: Interrupt continuous movement.
        command = MOVE_LEFT;
		event_list.push_back( MOVE_LEFT_EVENT );
        if ( planePos.get_y() < FLOAT_MARGIN && planePos.get_y() > -FLOAT_MARGIN )
        {
			if ( disable_collision_expire_time < getSimulation()->get_time() )
			{
	            setStatus( STATUS_NORMAL );
			}
			_velocity_x = 0.0f - GLYRUS_RIGHT_STEP;

			//planePos.set_x( planePos.get_x()  
			//	- GLYRUS_RIGHT_STEP * getSimulation()->getFrameInterval() );
        }
        break;
    case STATUS_CINEMATIC:
    // Objective: Don't move when in a noninteractive sequence.
    default:
        break;
    }
}




void glyrus :: fireRequest( fire_rate_type fire_rate )
{
	_fire_request_pending = true;
	_fire_rate = fire_rate;
}

// #define BIG_BULLET_UNDER_CONSTRUCTION

void glyrus :: fire_wrapper( eventTime interval_msec )
{
	_last_bullet = fire( interval_msec );
	if ( NULL != _last_bullet && _last_bullet )
	{
		_last_bullet->setScale( GLYRUS_BULLET_SCALE );
	}
	// MOVE TO _update_rhythm():   _modify_last_bullet();
	// To enable accurate rhythm shot.
}


void glyrus :: _modify_last_bullet()
{

	// Modify bullet if just firing.
	if ( event_occurred( FIRE_EVENT ) )
	{
		if ( _last_bullet != NULL && _last_bullet )
		{
			// Bullet upgrades

			//		big
			if ( get_big_bullet() )
			{
				event_list.push_back( SPECIAL_FIRE_EVENT );
				_last_bullet->setScale( BIG_BULLET_SCALE, BULLET_ASPECT_Y );
				_last_bullet->setCollisionDamage( BIG_BULLET_DAMAGE );
			}

			//		fork
			// Fork creates 3 bullets, so this operation should be done last, 
			// or list of bullets should be returned for further modification.
			if ( get_fork_bullet() )
			{
				event_list.push_back( SPECIAL_FIRE_EVENT );
  				bullet * left_bullet = new bullet( getPlanePos(), getOrientation(), getSimulation() );
				getSimulation()->insertMobileObject( left_bullet );
				// Copy contents.
				(*left_bullet) = (*_last_bullet);
				left_bullet->setOrientation( _last_bullet->getOrientation() - FORK_ANGLE );

				bullet * right_bullet = new bullet( getPlanePos(), getOrientation(), getSimulation() );
				getSimulation()->insertMobileObject( right_bullet );
				// Copy contents.
				(*right_bullet) = (*_last_bullet);
				right_bullet->setOrientation( _last_bullet->getOrientation() + FORK_ANGLE );
			}
		}
	}
}



void glyrus :: _firePermit( fire_rate_type fire_rate )
{
	/*
	Request avatar to fire
			once
			or continuously:
		If avatar is not recovering:
			Permit avatar to fire.
	*/

	if ( STATUS_RECOVER == getStatus() )
	{
        // Objective: Don't end recovery on shot.  Just let time go.  Don't shoot when recovering.
		if ( disable_collision_expire_time < getSimulation()->get_time() )
		{
		//	if ( planePos.y < FLOAT_MARGIN && planePos.y > -FLOAT_MARGIN )
		//	{
				setStatus( STATUS_NORMAL );
		//	}
		}
	}

    // Else don't move when in a noninteractive sequence.
	/*
	If once:
		If avatar is not firing continuously:
			Fire at once interval.
		Otherwise:
			End firing continuously.
	Otherwise (continuously):
		If avatar is not firing continuously:
			Begin firing continuously.
		Otherwise:
			Fire at continuous interval.
	*/

	if ( instant_fire == fire_rate )
	{
		if ( _is_continuous_fire )
		{
			_toggle_continuous_fire( false );
		}

		
		fire_wrapper( glyrus_instant_fire_interval_msec );
	}
	else if ( continuous_fire == fire_rate )
	{
		if ( ! _is_continuous_fire )
		{
			_toggle_continuous_fire( true );
		}
		else
		{
			fire_wrapper( glyrus_continuous_fire_interval_msec );
		}
	}
	else // ( undefined == fire_request_type )
	{
	}

	_fire_request_pending = false;
}



void glyrus :: _toggle_continuous_fire( bool begin )
{
	/*
	Begin or end
	firing continuously:
		If beginning:
			If fire continuously enable time is undefined:
				Set fire continuously enable time to now + fire continuous interval.
			Otherwise if now is less than or equal to fire continuously enable time:
				Set continuous fire state to true.
		Otherwise:
			Set fire continuously enable time to undefined.
	*/
	if ( begin )
	{
		clock_t now = getSimulation()->get_time();
		if ( UNDEFINED_TIME == _continuous_fire_enable_time )
		{
			_continuous_fire_enable_time = now + glyrus_continuous_fire_interval_msec;
		}
		else if ( _continuous_fire_enable_time < now )
		{
			_is_continuous_fire = true;
		}
	}
	else
	{
		_continuous_fire_enable_time = UNDEFINED_TIME;
		_is_continuous_fire = false;
	}
}



void glyrus :: destroy()
// Objective: Respond to being fatally hit.
{
    if ( status != STATUS_RECOVER )
    {
		// Mobile object "destroy()"
		// Destroying glyrus causes error from deletion.
		//? getSimulation()->destroyMobileObject( this );

        // Explode.
        // TODO:  Export sound play to audio.  Provide event signal.
        // sounds[SND_EXPLODE].Play();
        // TODO: Decrement ship count.
        life_count--;
#ifdef DEBUG_PRINT
        printf( "glyrus.hit() life_count = %d\n", life_count );
#endif
        // TODO: Replace with a new glyrus. 
        setStatus( STATUS_RECOVER );
		// Do not reset to bottom center of cylinder.  Come back where killed.
        // planePos.set_y( RECOVER_POS_Y );
		disable_collision_expire_time = GLYRUS_RECOVER_MSEC + getSimulation()->get_time();
		setFireEnableTime( GLYRUS_RECOVER_DISABLE_FIRE_MSEC + getSimulation()->get_time() );
		// After destruction reset health.
		set_health( BASE_HEALTH );

        // TODO:  Export tilt to graphics.  Simulation signals event.
        // tilt = 0.0f;
    }
}


void glyrus :: recover()
// Objective: Slowly place the player ship back on track.
{
    if ( planePos.get_y() < GLYRUS_Y ) 
	{
		_velocity_y = GLYRUS_RECOVER_SPEED;
	}
    else
	{
		_velocity_y = 0.0f;
        planePos.set_y( GLYRUS_Y );
	}

	if ( disable_collision_expire_time < getSimulation()->get_time() )
	{
		setStatus( STATUS_NORMAL );
	}

    // TODO: Pulse or otherwise visually signal recover status.
}


inline shipStatus glyrus :: getStatus()
// Objective: Find out the status of glyrus.
{
    return status;
}



void glyrus :: setStatus( shipStatus newStatus )
// Objective: Set the status of glyrus.
{
    status = newStatus;
	if ( STATUS_RECOVER == newStatus )
	{
		setCollisionActive( false );
		disable_collision_expire_time = GLYRUS_RECOVER_MSEC + getSimulation()->get_time();
	}
	else if ( STATUS_NORMAL == newStatus )
	{
		setCollisionActive( true );
	}
}



glyNormalf glyrus :: getPlanePosX()
{
    return planePos.get_x();
}


glyNormalf glyrus :: getPlanePosY()
{
    return planePos.get_y();
}



inline clock_t glyrus :: getCommandExpireTime() 
{ 
    return commandExpireTime; 
}



inline commandType glyrus :: getCommand() 
{ 
    return command; 
}



inline void glyrus :: setCommand( commandType newCommand ) 
{ 
    command = newCommand; 
	commandExpireTime = getSimulation()->get_time() + CONTINUOUS_MOVE_MSEC;
    continuousMove();
}

bool glyrus :: get_big_bullet()
{
	return _big_bullet;
}



void glyrus :: set_big_bullet( bool new_big_bullet )
{
	// Idempotent.  Do not double assign.
	if ( new_big_bullet != _big_bullet )
	{
		_big_bullet = new_big_bullet;
	}
}



bool glyrus :: get_fork_bullet()
{
	return _fork_bullet;
}



void glyrus :: set_fork_bullet( bool new_fork_bullet )
{
	// Idempotent.  Do not double assign.
	if ( new_fork_bullet != _fork_bullet )
	{
		_fork_bullet = new_fork_bullet;
	}
}



void glyrus :: update_life_count()
{
	// Give extra life for high score.
	// TODO:  Export to glyrus class.
	// next life percent never goes to 100.
	//if ( 100 <= get_next_life_percent() )
	if ( extra_life_multiple < (get_score() / score_per_extra_life) )
	{
		// Increment life.
		event_list.push_back( INCREMENT_LIFE_EVENT );
		set_life_count( get_life_count() + 1 );
		extra_life_multiple ++;
	}
}





bool glyrus :: is_facing_me( mobileObject * aMobileObject )
{
	// Special case:  Do not collide glyrus if facing similar orientation as glyrus.
	// This prevents obnoxious slaying from side by side objects returning to view.
	// Optimized by exporting this special case of collision to separate function.

	// Prevent slaying from objects behind glyrus.
	if ( aMobileObject->getPlanePos().get_y() < GLYRUS_Y )
		return false;

#ifdef GLYRUS_ORIENTATION_EQUALS_0
	assert( -FLOAT_MARGIN < getOrientation() && getOrientation() < FLOAT_MARGIN 
		&& "Glyrus should have orientation of 0.0f." );

	// Precondition:  Glyrus orientation is approximately 0.0f.
	if ( aMobileObject->getOrientation() < min_avatar_collision_orientation )
		return false;

	if ( max_avatar_collision_orientation < aMobileObject->getOrientation() )
		return false;

#else // GLYRUS_ORIENTATION_EQUALS_0
	// inline bool glyrus :: is_facing_me( aMobileObject )
	glyOrientationf different_orientation = 
		aMobileObject->getOrientation() - getOrientation();
	// Modulo to [ 0.0f, 360.0f )
	MODULO_FLOAT( different_orientation, DEGREES_PER_REVOLUTION );

	// Maximum difference is 180 degrees.  
	// Further quantities converge toward the other direction.
	if ( HALF_DEGREES_PER_REVOLUTION < different_orientation ) 
		different_orientation = DEGREES_PER_REVOLUTION - different_orientation;

	if ( different_orientation < min_avatar_collision_angle )
		return false;
#endif

	return true;
}



//
// class formation implementations
//

formation :: formation()
{
	setManeuverControl( TIME_EVENT_CONTROL );
    init();
}

formation :: formation( glyPlanePos2fv newPlanePos, glyOrientationf newOrientation, 
					   simulation * newSimulation )
{
	construct( newPlanePos, newOrientation, newSimulation );
}


void formation :: construct( glyPlanePos2fv newPlanePos, glyOrientationf newOrientation, 
					   simulation * newSimulation )
{
	setSimulation( newSimulation );

	setManeuverControl( TIME_EVENT_CONTROL );
    originPlanePos = planePos = newPlanePos;
    setOrientation( newOrientation );

    init();
}

void formation :: init()
// Initialize formation.
{
	setDefaultManeuver( & defaultManeuver );
	fire_trigger_schedule_pointer = NULL;
	maneuver_trigger_schedule_pointer = NULL;

    for(int i=0; i<UNIT_COUNT_MAX; i++)
    {
        mtn_i[ i ] = 0;
        mnv_i[ i ] = 0;
		default_mnv[ i ] = false;
    }

    shipList_length = 0;
    maneuverList_length = 0;
    pending_length = 0;
	overwrite_ship_speed = false;
	ship_speed = DEFAULT_SPEED;
}


int formation :: getLength()
{
    return shipList_length;
}


ship * formation :: getShip( int shipIndex )
{
    if ( 0 <= shipIndex && shipIndex < shipList_length ) 
    {
        return shipList[ shipIndex ];
    }
    else
    {
        return NULL;
    }
}


void formation :: insertManeuver( maneuver * newManeuver )
{
    maneuverList[ maneuverList_length ] = newManeuver;
    maneuverList_length++;
    if ( maneuverList_length == MANEUVER_COUNT_MAX ) maneuverList_length--;
}



void formation :: insertShip( ship * newShip, bool already_in_simulation )
// Objective: Place a ship in a formation.
{
    // TODO: Prevent double insertion.  But allow taking from another formation?
    //if ( NULL == newShip->getParentFormation() )
    // Precondition: Ship does not belong to another formation.
    //{
		// Insert this formation into the manager when first ship is spawned inserted.
		if ( 0 == shipList_length )
		{
		    getSimulation()->getFormationManager()->insert( this );
		}

        shipList[ shipList_length ] = newShip;
        // Objective: Assign ship its formation, 
        //   so that it may remove itself on destruction; else game crashes.
        shipList[ shipList_length ]->parentFormation = this;
		// Reset active motion, so a new one will be assigned immediately.
		shipList[ shipList_length ]->setActiveMotion( M_NULL );
		resetMotionList( shipList_length );
		// Set fire trigger and maneuver trigger.
		shipList[ shipList_length ]->fire_trigger_schedule_pointer = 
			fire_trigger_schedule_pointer;
		shipList[ shipList_length ]->maneuver_trigger_schedule_pointer = 
			maneuver_trigger_schedule_pointer;
		


		// Optionally overwrite ship speed.
		// Reset maneuver
		if ( 1 <= maneuverList_length )
		{
			setManeuverIndex( shipList_length, 0 );
		}
		else
		{
			setManeuverIndex( shipList_length, _DEFAULT_MANEUVER_INDEX );
		}

		if ( overwrite_ship_speed )
		{
			shipList[ shipList_length ]->setSpeed( this->ship_speed );
		}

		if ( ! already_in_simulation )
		{
			getSimulation()->insertMobileObject( shipList[ shipList_length ] );
		}

		// Do not increment until operations on the ship are complete.
        shipList_length++;
        if ( shipList_length == UNIT_COUNT_MAX ) shipList_length--;
    //}
    //else
    //{
	//	assert( NULL == newShip->getParentFormation() );
    //}
}



void formation :: appendShip( ship * newShip, clock_t addPendingTime )
// Objective: Place a ship behind the last ship in the formation, following it's maneuver.
// The appended ship does not necessarily appear.
// The appended ship's activities are delayed by delay time msec.
{
    // Objective: Delay the ship's appearance.
    pendingList[ pending_length ] = newShip;
    // Objective: Prevent double insertion.
    //pendingList[ pending_length ]->formation = this;

	pendingTime[ pending_length ] = getSimulation()->get_time() + addPendingTime;
	newShip->_offset_msec = 0 - addPendingTime;

    pending_length++;
    if ( pending_length == UNIT_COUNT_MAX ) pending_length--;
}



void formation :: removeShip( ship * oldShip )
// Objective: Remove a ship from the formation.
{
    int i=0;
    while ( i < shipList_length && shipList[i] != oldShip )
        i++;
    // Guarantee no double deletions.
    if ( i < shipList_length && shipList[i] == oldShip )
    {
        shipList[i]->parentFormation = NULL;
        shipList_length--;
        for (; i < shipList_length; i++)
            shipList[i] = shipList[i+1];
    }
	// Remove this formation from the manager when last ship is removed.
	if ( 0 == shipList_length )
	{
	    getSimulation()->getFormationManager()->remove( this );
	}
}





maneuver * formation :: getManeuver( int shipIndex )
{
	if ( default_mnv[ shipIndex ] 
		|| maneuverList_length <= 0 
		|| mnv_i[ shipIndex ] < _MIN_MANEUVER_INDEX
		|| maneuverList_length <= mnv_i[ shipIndex ] )
	{
		return getDefaultManeuver();
	}
	else
	{
		return maneuverList[ mnv_i[ shipIndex ] ];
	}
}


void formation :: setManeuverIndex( int shipIndex, int maneuverIndex )
{
	if ( 0 <= maneuverIndex && maneuverIndex < maneuverList_length )
	{
		mnv_i[ shipIndex ] = maneuverIndex;
		default_mnv[ shipIndex ] = false;
	}
	else if ( _DEFAULT_MANEUVER_INDEX == maneuverIndex )
	{
		default_mnv[ shipIndex ] = true;
	}
}



maneuver * formation :: getDefaultManeuver()
{
	return _defaultManeuver;
}



void formation :: setDefaultManeuver( maneuver * newManeuver )
{
	_defaultManeuver = newManeuver;
}



void formation :: update()
// Objective: Move the formation as a train or snake, each frame.
// TODO: Move the formation and not just the ships.
//   Yet be flexible for ships that are destroyed.
{
    int i;
    clock_t currentTime = getSimulation()->get_time();

    // Objective: Place pending ships.
    // TODO: optimize code with pending head, or use STL queue.
    for (i=0; i<pending_length; i++)
    {
        if (currentTime >= pendingTime[ i ])
        {
			// DEPRECATE
			// TODO:  Spawn on the beat event.  Not on intensity increase.
			//// DEPRECATE and replace with pending spawn event list.
			//float intensityIncrease = getSimulation()
			//	->getEventSection()
			//		->getIntensityIncrease();
			//if ( getIntensityThreshold() < intensityIncrease )
			//{
				// Objective: Insert into ship list.
				insertShip( pendingList[ i ] );
				// Objective: Remove from pending list.
				// Example:  abcd#, bbcd#, bcdd#, bcd#
				for (int j = i; j<=pending_length-2; j++)
				{
					pendingList[ j ] = pendingList[ j+1 ];
					pendingTime[ j ] = pendingTime[ j+1 ];
				}
				pending_length--;
			//}
        }
    }

    // Objective: Update each ship's position.
    for (i=0; i<shipList_length; i++)
    {

		// Decide if ship will fire.
		shipList[ i ]->update_fire();

		// If using maneuver controlled by timeEvent:
			// If timeEvent triggered, then increment maneuver.  
			// Otherwise go to default maneuver.
		// Otherwise, use maneuver controlled by distance.
		if ( TIME_EVENT_CONTROL == maneuverControl )
		{
			moveShipByTimeEvent( i );
		}
		else // distanceControl
		{
			moveShipByDistance( i );
		}
	
		// Update script.  
		// Because of this location and clearing of event_list, 
		// all events after this in the simulation frame are not accessible during script update.
		if ( NULL != shipList[ i ]->update_script 
			&& shipList[ i ]->update_script )
		{
			shipList[ i ]->update_script( shipList[ i ] );
		}
    }
}


controlType formation :: getManeuverControl()
{
	return maneuverControl;
}



void formation :: setManeuverControl( controlType newManeuverControl )
{
	maneuverControl = newManeuverControl;
}


float formation :: getIntensityThreshold()
// Return the threshhold value for intensity.
{
	// TODO:  Make it dynamic.
	return 0.05f;
}



bool formation :: isManeuverComplete( int shipIndex )
{
	if (mtn_i[shipIndex] < getManeuver( shipIndex )->getLength() )
	{
		return false;
	}
	else
	{
		return true;
	}
}


void formation :: moveShipByTimeEvent( int shipIndex )
{
	// If maneuver complete, then go to default maneuver.
	if (  isManeuverComplete( shipIndex )   )
	{
		resetMotionList( shipIndex );
		setManeuverIndex( shipIndex, _DEFAULT_MANEUVER_INDEX );
	}

	// TODO:  Extend so that many formation scripts may exist.
	if ( shipList[ shipIndex ]->is_maneuver_trigger() )
	{
		// Increment maneuver.  
		incrementManeuver( shipIndex );
	}

	// Move ship by current maneuver.
	moveShipByManeuver( shipIndex, getManeuver( shipIndex ) );
}


void formation :: resetMotionList( int shipIndex )
{
	mtn_i[ shipIndex ] = 0;
}





void formation :: moveShipByManeuver( int shipIndex, maneuver * currentManeuver )
{
    bool isMotionDone;

    switch ( shipList[ shipIndex ]->getActiveMotion() )
    // Postcondition: Ship moved or has not been assigned any motion.
    {
    case M_LINE:
        isMotionDone = shipList[ shipIndex ]->isLineDone();
        break;
    case M_ARC:
        isMotionDone = shipList[ shipIndex ]->isArcDone();
        break;
    default:
        isMotionDone = 1;
        break;
    }

    if ( isMotionDone )
    // Objective: Assign first or next motion.
    {
        motion * nextMotion;
		if ( NULL != currentManeuver )
		{
	        nextMotion = currentManeuver->getMotion( mtn_i[shipIndex] );
		}
		else
		{
			nextMotion = & Line;
		}

        if ( NULL != nextMotion )
        {
            switch ( nextMotion->type )
            {
            case M_LINE:
                shipList[ shipIndex ]->setLine( nextMotion->distance );
                break;
            case M_ARC:
                shipList[ shipIndex ]->setArc( nextMotion->isRight, 
                    nextMotion->radius, 
                    nextMotion->orientation );
                break;
            default:
                break;
            }
        }

        // Objective: Increment motion address in circular queue.
        mtn_i[shipIndex] ++;
    }
}



void formation :: moveShipByDistance( int shipIndex )
{
    if (   isManeuverComplete( shipIndex )   ) 
    {
		incrementManeuver( shipIndex );
    }

	moveShipByManeuver( shipIndex, getManeuver( shipIndex ) );
}



void formation :: incrementManeuver( int shipIndex )
{
    resetMotionList( shipIndex );
    // Increment maneuver address in circular queue.
    if ( mnv_i[ shipIndex ] < maneuverList_length - 1 ) 
		setManeuverIndex( shipIndex, mnv_i[ shipIndex ] + 1 );
	else
		setManeuverIndex( shipIndex, 0 );
}





formationManager :: formationManager()
{
    list_length = 0;
	fire_trigger_schedule_pointer = NULL;
	maneuver_trigger_schedule_pointer = NULL;

}


formationManager :: ~formationManager()
// No need to delete component ships, because these are recorded mobile object list.
{
}


inline int formationManager :: getLength()
// Objective:  Return number of formations alive.
{
    return list_length;
}


formation * formationManager :: getFormation( int formationIndex )
// Objective:  Return number of formations alive.
{
    if ( 0 <= formationIndex && formationIndex < list_length ) 
    {
        return list[ formationIndex ];
    }
    else
    {
#ifdef DEBUG_FORMATION
        printf( "formationManager.getFormation:  out of bounds, formationIndex=", formationIndex );
#endif
        return NULL;
    }
}


const int formationManager :: getCount_remaining()
// Objective: Return a count of all the units in existence.
{
    int count = 0;

    // Objective: Count those in existence.
    // Objective: Count those pending existence.
    for ( int i = 0; i < list_length; i++ )
        count += list[i]->shipList_length;
    return count;
}


void formationManager :: init()
// Objective: Initialize the formation manager.
{
    for ( int i = 0; i < list_length; i++ )
	{
        verify_and_delete( list[i] );
	}
    list_length = 0;
}



void formationManager :: insert( formation * newFormation )
// Objective: Inserte a new formation into the game.
{
    bool isDuplicate = false;
    for ( int i = 0; i < list_length; i++ )
        if ( list[i] == newFormation )
            isDuplicate = true;
    if ( ! isDuplicate )
	{
        list[ list_length++ ] = newFormation;
		newFormation->setSimulation( getSimulation() );
	}
}



void formationManager :: remove( formation * old_formation )
// Remove a old formation from the active simulation.
{
	int i = 0;
    for ( ; i < list_length; i++ )
	{
        if ( list[i] == old_formation )
		{
			// remove old formation
			list_length --;
			for ( ; i < list_length; i++ )
			{
				list[ i ] = list[ i+1 ];
			}
			break;
		}
	}
}





void formationManager :: update()
// Objective: Move ships in the game.
{
	/*
	Update formation membership
	[[Before formation update]]
	for each formation in this formation manager:
		for each ship in this formation:
			if formation transfer cue exists:
				if formation transfer cue triggered:
					transfer formation
					erase cue
	*/
    for ( int f=0; f<list_length; f++ )
	{
		// HACK:  Iterate backwards so that removal of ship does not skip subsequent ships.
		for ( int s=list[f]->shipList_length-1; s>=0; s-- )
		{
			list[f]->shipList[s]->_update_formation_transfer();
		}
	}

	// Update each formation
    for ( int f=0; f<list_length; f++ )
        list[f]->update();
}




#endif // ndef _GLYRUS_BEHAVIOR_CPP_

