text: /* libs/graphics/animator/SkAnimateMaker.cpp
  **
  ** Copyright 2006, Google Inc.
  **
  ** Licensed under the Apache License, Version 2.0 (the "License"); 
  ** you may not use this file except in compliance with the License. 
  ** You may obtain a copy of the License at 
  **
  **     http://www.apache.org/licenses/LICENSE-2.0 
  **
  ** Unless required by applicable law or agreed to in writing, software 
  ** distributed under the License is distributed on an "AS IS" BASIS, 
  ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 
  ** See the License for the specific language governing permissions and 
  ** limitations under the License.
  */
  
  #include "SkAnimateMaker.h"
  #include "SkAnimator.h"
  #include "SkAnimatorScript.h"
  #include "SkDisplayable.h"
  #include "SkDisplayApply.h"
  #include "SkDisplayList.h"
  #include "SkDisplayMovie.h"
  #include "SkDisplayType.h"
  #include "SkExtras.h"
  #include "SkMemberInfo.h"
  #include "SkStream.h"
  #include "SkSystemEventTypes.h"
  #include "SkTime.h"
  
  class DefaultTimeline : public SkAnimator::Timeline {
      virtual SkMSec getMSecs() const {
          return SkTime::GetMSecs();
      }
  } gDefaultTimeline;
  
  SkAnimateMaker::SkAnimateMaker(SkAnimator* animator, SkCanvas* canvas, SkPaint* paint)
      : fActiveEvent(NULL), fAdjustedStart(0), fCanvas(canvas), fEnableTime(0), 
          fHostEventSinkID(0), fMinimumInterval((SkMSec) -1), fPaint(paint), fParentMaker(NULL),
          fTimeline(&gDefaultTimeline), fInInclude(false), fInMovie(false),
          fFirstScriptError(false), fLoaded(false), fIDs(256), fAnimator(animator)
  {
      fScreenplay.time = 0;
  #if defined SK_DEBUG && defined SK_DEBUG_ANIMATION_TIMING
      fDebugTimeBase = (SkMSec) -1;
  #endif
  #ifdef SK_DUMP_ENABLED
      fDumpEvents = fDumpGConditions = fDumpPosts = false;
  #endif
  }
  
  SkAnimateMaker::~SkAnimateMaker() {
      deleteMembers();
  }
  
  #if 0
  SkMSec SkAnimateMaker::adjustDelay(SkMSec expectedBase, SkMSec delay) {
      SkMSec appTime = (*fTimeCallBack)();
      if (appTime)
          delay -= appTime - expectedBase;
      if (delay < 0)
          delay = 0;
      return delay;
  }
  #endif
  
  void SkAnimateMaker::appendActive(SkActive* active) {
      fDisplayList.append(active);
  }
  
  void SkAnimateMaker::clearExtraPropertyCallBack(SkDisplayTypes type) {
      SkExtras** end = fExtras.end();
      for (SkExtras** extraPtr = fExtras.begin(); extraPtr < end; extraPtr++) {
          SkExtras* extra = *extraPtr;
          if (extra->definesType(type)) {
              extra->fExtraCallBack = NULL;
              extra->fExtraStorage = NULL;
              break;
          }
      }
  }
  
  bool SkAnimateMaker::computeID(SkDisplayable* displayable, SkDisplayable* parent, SkString* newID) {
      const char* script;
    if (findKey(displayable, &script) == false)
          return true;
      return SkAnimatorScript::EvaluateString(*this, displayable, parent, script, newID);
  }
  
  SkDisplayable* SkAnimateMaker::createInstance(const char name[], size_t len) {
      SkDisplayTypes type = SkDisplayType::GetType(this, name, len );
      if ((int)type >= 0) 
          return SkDisplayType::CreateInstance(this, type);
      return NULL;
  }
  
  // differs from SkAnimator::decodeStream in that it does not reset error state
  bool SkAnimateMaker::decodeStream(SkStream* stream)
  {
      SkDisplayXMLParser parser(*this);
      return parser.parse(*stream);
  }
  
  // differs from SkAnimator::decodeURI in that it does not set URI base
  bool SkAnimateMaker::decodeURI(const char uri[]) {
  //  SkDebugf("animator decode %s\n", uri);
  
  //    SkStream* stream = SkStream::GetURIStream(fPrefix.c_str(), uri);
      SkStream* stream = new SkFILEStream(uri);
  
      SkAutoTDelete<SkStream> autoDel(stream);
      bool success = decodeStream(stream);
      if (hasError() && fError.hasNoun() == false)
          fError.setNoun(uri);
      return success;
  }
  
  #if defined SK_DEBUG && 0
  //used for the if'd out section of deleteMembers
  #include "SkTSearch.h"
  
  extern "C" {
      int compare_disp(const void* a, const void* b) {
          return *(const SkDisplayable**)a - *(const SkDisplayable**)b;
      }
  }
  #endif
  
  void SkAnimateMaker::delayEnable(SkApply* apply, SkMSec time) {
      int index = fDelayed.find(apply);
      if (index < 0)
          *fDelayed.append() = apply;
      (new SkEvent(SK_EventType_Delay))->postTime(fAnimator->getSinkID(), time);
  }
  
  void SkAnimateMaker::deleteMembers() {
      int index;
  #if defined SK_DEBUG && 0
      //this code checks to see if helpers are among the children, but it is not complete -
      //it should check the children of the children
      int result;
      SkTDArray<SkDisplayable*> children(fChildren.begin(), fChildren.count());
      SkQSort(children.begin(), children.count(), sizeof(SkDisplayable*),compare_disp);
      for (index = 0; index < fHelpers.count(); index++) {
          SkDisplayable* helper = fHelpers[index];
          result = SkTSearch(children.begin(), children.count(), helper, sizeof(SkDisplayable*));
          SkASSERT(result < 0);
      }
  #endif
      for (index = 0; index < fChildren.count(); index++) {
          SkDisplayable* child = fChildren[index];
          delete child;
      }
      for (index = 0; index < fHelpers.count(); index++) {
          SkDisplayable* helper = fHelpers[index];
          delete helper;
      }
      for (index = 0; index < fExtras.count(); index++) {
          SkExtras* extras = fExtras[index];
          delete extras;
      }
  }
  
  void SkAnimateMaker::doDelayedEvent() {
      fEnableTime = getAppTime();
      for (int index = 0; index < fDelayed.count(); ) {
          SkDisplayable* child = fDelayed[index];
          SkASSERT(child->isApply());
          SkApply* apply = (SkApply*) child;
          apply->interpolate(*this, fEnableTime);
          if (apply->hasDelayedAnimator())
              index++;
          else
              fDelayed.remove(index);
      }
  }
  
  bool SkAnimateMaker::doEvent(const SkEvent& event) {
      return (!fInMovie || fLoaded) && fAnimator->doEvent(event);
  }
  
  #ifdef SK_DUMP_ENABLED
  void SkAnimateMaker::dump(const char* match) {
          SkTDict<SkDisplayable*>::Iter iter(fIDs);
          const char* name;
          SkDisplayable* result;
          while ((name = iter.next(&result)) != NULL) {
              if (strcmp(match,name) == 0)
                  result->dump(this);
          }
  }
  #endif
  
  int SkAnimateMaker::dynamicProperty(SkString& nameStr, SkDisplayable** displayablePtr ) {
      const char* name = nameStr.c_str();
      const char* dot = strchr(name, '.');
      SkASSERT(dot);
      SkDisplayable* displayable;
      if (find(name, dot - name, &displayable) == false) {
          SkASSERT(0);
          return 0;
      }
      const char* fieldName = dot + 1;
      const SkMemberInfo* memberInfo = displayable->getMember(fieldName);
      *displayablePtr = displayable;
      return (int) memberInfo->fOffset;
  }
  
  SkMSec SkAnimateMaker::getAppTime() const {
      return fTimeline->getMSecs();
  }
  
  #ifdef SK_DEBUG
  SkAnimator* SkAnimateMaker::getRoot()
  {
      SkAnimateMaker* maker = this;
      while (maker->fParentMaker)
          maker = maker->fParentMaker;
      return maker == this ? NULL : maker->fAnimator;
  }
  #endif
  
  void SkAnimateMaker::helperAdd(SkDisplayable* trackMe) {
      SkASSERT(fHelpers.find(trackMe) < 0);
      *fHelpers.append() = trackMe;
  }
  
  void SkAnimateMaker::helperRemove(SkDisplayable* alreadyTracked) {
      int helperIndex = fHelpers.find(alreadyTracked);
      if (helperIndex >= 0)
          fHelpers.remove(helperIndex);
  }
  
  #if 0
  void SkAnimateMaker::loadMovies() {
      for (SkDisplayable** dispPtr = fMovies.begin(); dispPtr < fMovies.end(); dispPtr++) {
          SkDisplayable* displayable = *dispPtr;
          SkASSERT(displayable->getType() == SkType_Movie);
          SkDisplayMovie* movie = (SkDisplayMovie*) displayable;
          SkAnimateMaker* movieMaker = movie->fMovie.fMaker;
          movieMaker->fEvents.doEvent(*movieMaker, SkDisplayEvent::kOnload, NULL);
          movieMaker->fEvents.removeEvent(SkDisplayEvent::kOnload, NULL);
          movieMaker->loadMovies();
      }
  }
  #endif
  
  void SkAnimateMaker::notifyInval() {
      if (fHostEventSinkID)
          fAnimator->onEventPost(new SkEvent(SK_EventType_Inval), fHostEventSinkID);
  }
  
  void SkAnimateMaker::notifyInvalTime(SkMSec time) {
      if (fHostEventSinkID)
          fAnimator->onEventPostTime(new SkEvent(SK_EventType_Inval), fHostEventSinkID, time);
  }
  
  void SkAnimateMaker::postOnEnd(SkAnimateBase* animate, SkMSec end) {
          SkEvent evt;
          evt.setS32("time", animate->getStart() + end);
          evt.setPtr("anim", animate);
          evt.setType(SK_EventType_OnEnd);
          SkEventSinkID sinkID = fAnimator->getSinkID();
          fAnimator->onEventPost(new SkEvent(evt), sinkID);
  }
  
  void SkAnimateMaker::reset() {
      deleteMembers();
      fChildren.reset();
      fHelpers.reset();
      fIDs.reset();
      fEvents.reset();
      fDisplayList.hardReset();
  }
  
  void SkAnimateMaker::removeActive(SkActive* active) {
      if (active == NULL)
          return;
      fDisplayList.remove(active);
  }
  
  bool SkAnimateMaker::resolveID(SkDisplayable* displayable, SkDisplayable* original) {
      SkString newID;
      bool success = computeID(original, NULL, &newID);
      if (success)
          setID(displayable, newID);
      return success;
  }
  
  void SkAnimateMaker::setErrorString() {
      fErrorString.reset();
      if (fError.hasError()) {
          SkString err;
          if (fFileName.size() > 0)
              fErrorString.set(fFileName.c_str());
          else
              fErrorString.set("screenplay error");
          int line = fError.getLineNumber();
          if (line >= 0) {
              fErrorString.append(", ");
              fErrorString.append("line ");
              fErrorString.appendS32(line);
          }
          fErrorString.append(": ");
          fError.getErrorString(&err);
          fErrorString.append(err);
  #if defined SK_DEBUG
          SkDebugf("%s\n", fErrorString.c_str());
  #endif
      } 
  }
  
  void SkAnimateMaker::setEnableTime(SkMSec appTime, SkMSec expectedTime) {
  #if defined SK_DEBUG && defined SK_DEBUG_ANIMATION_TIMING
      SkString debugOut;
      SkMSec time = getAppTime();
      debugOut.appendS32(time - fDebugTimeBase);
      debugOut.append(" set enable old enable=");
      debugOut.appendS32(fEnableTime - fDebugTimeBase);
      debugOut.append(" old adjust=");
      debugOut.appendS32(fAdjustedStart);
      debugOut.append(" new enable=");
      debugOut.appendS32(expectedTime - fDebugTimeBase);
      debugOut.append(" new adjust=");
      debugOut.appendS32(appTime - expectedTime);
      SkDebugf("%s\n", debugOut.c_str());
  #endif
      fAdjustedStart = appTime - expectedTime;
      fEnableTime = expectedTime;
      SkDisplayable** firstMovie = fMovies.begin();
      SkDisplayable** endMovie = fMovies.end();
      for (SkDisplayable** ptr = firstMovie; ptr < endMovie; ptr++) {
          SkDisplayMovie* movie = (SkDisplayMovie*) *ptr;
          movie->fMovie.fMaker->setEnableTime(appTime, expectedTime);
      }
  }
  
  void SkAnimateMaker::setExtraPropertyCallBack(SkDisplayTypes type, 
          SkScriptEngine::_propertyCallBack callBack, void* userStorage) {
      SkExtras** end = fExtras.end();
      for (SkExtras** extraPtr = fExtras.begin(); extraPtr < end; extraPtr++) {
          SkExtras* extra = *extraPtr;
          if (extra->definesType(type)) {
              extra->fExtraCallBack = callBack;
              extra->fExtraStorage = userStorage;
              break;
          }
      }
  }
  
  void SkAnimateMaker::setID(SkDisplayable* displayable, const SkString& newID) {
      fIDs.set(newID.c_str(), displayable);
  #ifdef SK_DEBUG
      displayable->_id.set(newID);
      displayable->id = displayable->_id.c_str();
  #endif
  }
  
  void SkAnimateMaker::setScriptError(const SkScriptEngine& engine) {
      SkString errorString;
  #ifdef SK_DEBUG
      engine.getErrorString(&errorString);
  #endif
      setErrorNoun(errorString);
      setErrorCode(SkDisplayXMLParserError::kErrorInScript);
  }
  
  bool SkAnimateMaker::GetStep(const char* token, size_t len, void* stepPtr, SkScriptValue* value) {
      if (SK_LITERAL_STR_EQUAL("step", token, len)) {
          value->fOperand.fS32 = *(int32_t*) stepPtr;
          value->fType = SkType_Int;
          return true;
      }
      return false;
  }
  