text: #include "SpineEventMonitor.h" 
  
  #include "spine/spine.h"
  #include "KString.h"
  
  #include "KMemory.h" // Last include
  
  SpineEventMonitor::SpineEventMonitor(spAnimationState* _pAnimationState /*= nullptr*/)
  {
  	bLogging = false;
  	RegisterListener(_pAnimationState);
  }
  
  SpineEventMonitor::~SpineEventMonitor()
  {
  	pAnimState = 0;
  }
  
  void SpineEventMonitor::RegisterListener(spAnimationState * _pAnimationState)
  {
  	if (_pAnimationState) {
  		_pAnimationState->rendererObject = this;
  		_pAnimationState->listener = (spAnimationStateListener)&SpineEventMonitor::spineAnimStateHandler;
  	}
  	pAnimState = _pAnimationState;
  }
  
  bool SpineEventMonitor::isAnimationPlaying()
  {
  	if (pAnimState) 
  		return spAnimationState_getCurrent(pAnimState, 0) != 0;
  	return false;
  }
  
  void SpineEventMonitor::spineAnimStateHandler(spAnimationState * state, int type, spTrackEntry * entry, spEvent * event)
  {
  	if (state && state->rendererObject) {
  		SpineEventMonitor* pEventMonitor = (SpineEventMonitor*)state->rendererObject;
  		pEventMonitor->OnSpineAnimationStateEvent(state, type, entry, event);
  	}
  }
  
  void SpineEventMonitor::OnSpineAnimationStateEvent(spAnimationState * state, int type, spTrackEntry * trackEntry, spEvent * event)
  {
  	const char* eventName = 0;
  	if (state == pAnimState) { // only monitor ours
  		switch(type)
  		{
  		case SP_ANIMATION_START: eventName = "SP_ANIMATION_START"; break;
  		case SP_ANIMATION_INTERRUPT: eventName = "SP_ANIMATION_INTERRUPT"; break;
  		case SP_ANIMATION_END: eventName = "SP_ANIMATION_END"; break;
  		case SP_ANIMATION_COMPLETE: eventName = "SP_ANIMATION_COMPLETE"; break;
  		case SP_ANIMATION_DISPOSE: eventName = "SP_ANIMATION_DISPOSE"; break;
  		case SP_ANIMATION_EVENT: eventName = "SP_ANIMATION_EVENT"; break;
  		default:
  			break;
  		}
  
  		if (bLogging && eventName && trackEntry && trackEntry->animation && trackEntry->animation->name)
  			KOutputDebug(DEBUGLVL, "[%s : '%s']\n", eventName,  trackEntry->animation->name);//*/
  	}
  }
  
  
  
  InterruptMonitor::InterruptMonitor(spAnimationState * _pAnimationState):
  	SpineEventMonitor(_pAnimationState)
  {
  	bForceInterrupt = false;
  	mEventStackCursor = 0; // cursor used to track events
  }
  
  bool InterruptMonitor::isAnimationPlaying()
  {
  	return !bForceInterrupt && SpineEventMonitor::isAnimationPlaying();
  }
  
  // Stops the animation on any occurance of the spEventType
  InterruptMonitor& InterruptMonitor::AddInterruptEvent(int theEventType)
  {
  	InterruptEvent ev;
  	ev.mEventType = theEventType;
  	mEventStack.push_back(ev);
  	return *this;
  }
  
  // Stops the animation when the [spEventType : 'animationName'] occurs
  InterruptMonitor& InterruptMonitor::AddInterruptEvent(int theEventType, const std::string & theAnimationName)
  {
  	InterruptEvent ev;
  	ev.mEventType = theEventType;
  	ev.mAnimName = theAnimationName;
  	mEventStack.push_back(ev);
  	return *this;
  }
  
  // stops the first encounter of spEventType on the specified TrackEntry
  InterruptMonitor& InterruptMonitor::AddInterruptEvent(int theEventType, spTrackEntry * theTrackEntry)
  {
  	InterruptEvent ev;
  	ev.mEventType = theEventType;
  	ev.mTrackEntry = theTrackEntry;
  	mEventStack.push_back(ev);
  	return *this;
  }
  
  // Stops on the first SP_ANIMATION_EVENT with the string payload of 'theEventTriggerName'
  InterruptMonitor& InterruptMonitor::AddInterruptEventTrigger(const std::string & theEventTriggerName)
  {
  	InterruptEvent ev;
  	ev.mEventType = SP_ANIMATION_EVENT;
  	ev.mEventName = theEventTriggerName;
  	mEventStack.push_back(ev);
  	return *this;
  }
  
  void InterruptMonitor::OnSpineAnimationStateEvent(spAnimationState * state, int type, spTrackEntry * trackEntry, spEvent * event)
  {
  	SpineEventMonitor::OnSpineAnimationStateEvent(state, type, trackEntry, event);
  
  	if (mEventStackCursor < mEventStack.size()) {
  		if (mEventStack[mEventStackCursor].matches(state, type, trackEntry, event))
  			++mEventStackCursor;
  
  		if (mEventStackCursor >= mEventStack.size()) {
  			bForceInterrupt = true;
  			OnMatchingComplete();
  		}
  	}
  }
  
  inline bool InterruptMonitor::InterruptEvent::matches(spAnimationState * state, int type, spTrackEntry * trackEntry, spEvent * event) {
  
  	// Must match spEventType {SP_ANIMATION_START, SP_ANIMATION_INTERRUPT, SP_ANIMATION_END, SP_ANIMATION_COMPLETE, SP_ANIMATION_DISPOSE, SP_ANIMATION_EVENT }
  	if (mEventType == type) {
  
  		// Looking for specific TrackEntry by pointer
  		if (mTrackEntry != 0) {
  			return mTrackEntry == trackEntry;
  		}
  
  		// looking for Animation Track by name
  		if (!mAnimName.empty()) {
  			if (trackEntry && trackEntry->animation && trackEntry->animation->name) {
  				if (CompareNoCase(trackEntry->animation->name, mAnimName) == 0) {
  					return true;
  				}
  			}
  			return false;
  		}
  
  		// looking for Event String Text
  		if (!mEventName.empty()) {
  			if (event && event->stringValue) {
  				return (CompareNoCase(event->stringValue, mEventName) == 0);
  			}
  			return false;
  		}
  
  		return true; // waiting for ANY spEventType that matches
  	}
  	return false;
  }
  