var DEBUG;

var ANIMATION_MANAGER = {
  
  animations: new Array(),
  activeSlideshows: new Array(),
  MAX_FRAMES: 128,
  
  getAnimation: function( animClass ){
    
    /* Find the animation with the given class */
    for(i = 0; i < ANIMATION_MANAGER.animations.length; i++){
      if( ANIMATION_MANAGER.animations[i].animationClass == animClass ){
        return ANIMATION_MANAGER.animations[i];
      }
    }
    
    /* The animation wasn't found ... */
    if(DEBUG) DEBUG.printError("getAnimation( animClass ): Animation with class " + animClass + " doesn't exist or has not yet been parsed.");
    return null;
  },
  
  parseRelData: function( animContainer, frameRangeArray, animTimeArray, animPauseArray ){
   
// ?? NEED MORE CHECKS!
    
    /* Check that parameters are valid... */
    if( animContainer == null){
          if(DEBUG) DEBUG.printError("parseRelData( animContainer, frameRangeArray, animTimeArray, animPauseArray ): animContainer was null.");
          return false;
    }
    if( frameRangeArray == null){
          if(DEBUG) DEBUG.printError("parseRelData( animContainer, frameRangeArray, animTimeArray, animPauseArray ): frameRangeArray was null.");
          return false;
    }
    if( animTimeArray == null){
          if(DEBUG) DEBUG.printError("parseRelData( animContainer, frameRangeArray, animTimeArray, animPauseArray ): animTimeArray was null.");
          return false;
    }
    if( animPauseArray == null){
          if(DEBUG) DEBUG.printError("parseRelData( animContainer, frameRangeArray, animTimeArray, animPauseArray ): animPauseArray was null.");
          return false;
    }

    /* Get the frame data contained in the rel attribute ... */
    var animRelData = $(animContainer).attr("rel");
    
    /* Remove first char '[' */
    if( animRelData.charAt(0) != '['){
          if(DEBUG) DEBUG.printError("parseRelData( animContainer, frameRangeArray, animTimeArray, animPauseArray ): Data sets must be enclosed in square braces '[ ]'.");
          return false;
    }
    animRelData = animRelData.slice(1, animRelData.length);
    
    /* Separate data sets */
    animRelData = animRelData.split('[');
    
    /* Check that the correct number of data sets were provided */
    if(animRelData.length != 3){
          if(DEBUG) DEBUG.printError("parseRelData( animContainer, frameRangeArray, animTimeArray, animPauseArray ): Rel attribute did not contain exactly 3 data sets.");
          return;
    }
    
    /* Separate the data sets */
    var frameRangeData = animRelData[0].slice(0, animRelData[0].length-1).split(','); // Remove last char because it is ']'
    var timeData = animRelData[1].slice(0, animRelData[1].length-1).split(','); // Remove last char because it is ']'
    var pauseData = animRelData[2].slice(0, animRelData[2].length-1).split(','); // Remove last char because it is ']'
    
    /* Store data in arrays */
    for(i = 0; i < frameRangeData.length; i++){
      frameRangeArray.push(parseInt(frameRangeData[i]));
    }
    for(i = 0; i < timeData.length; i++){
      animTimeArray.push(parseInt(timeData[i]));
    }
    for(i = 0; i < pauseData.length; i++){
      animPauseArray.push(parseInt(pauseData[i]));
    }
    
    return;
  },

// ?? Need to rethink the required parameters...
  parseAnimation: function( anim, animClass ){

        /* Ensure animContainer is a container */
        if( anim == null || anim.length <= 0 ){
          if(DEBUG) DEBUG.printError("parseAnimation( anim, animClass ): anim was null or empty.");
          return false;
        }
        
// ?? NEED MORE ERROR CHECKING!
        
        /* Get the link to the next animation (if any!) */
        var animLink = anim.find(".nextAnim");
        
        /* Get animation data from rel attribute */
        var frameRangeData = new Array();
        var timeData = new Array();
        var delayData = new Array();
        ANIMATION_MANAGER.parseRelData( anim, frameRangeData, timeData, delayData );
        
        /* Get frame range information */
        var fromFrame = frameRangeData[0];
        var toFrame = frameRangeData[1];
      
        /* Ensure appropriate animation range is specified */
        if( fromFrame == null || toFrame == null || fromFrame < 1 || fromFrame > toFrame ){
          if(DEBUG) DEBUG.printError("parseAnimation( anim, animClass ): Invalid animation range specified.");
          return false;
        }
        if( toFrame > ANIMATION_MANAGER.MAX_FRAMES ){
          if(DEBUG) DEBUG.printError("parseAnimation( anim, animClass ): Animation range exceeds the maximum frame " + ANIMATION_MANAGER.MAX_FRAMES + ".");
          return false;
        }
      
        /* Set animation to the first frame to be parsed */
        var animatedFrame = anim.find(".frame");
        $(animatedFrame).attr("class","frame frame-" + fromFrame);

        /* Object to hold frame records */
        var animation = {
          animationClass: animClass,
          frames: new Array( ANIMATION_MANAGER.MAX_FRAMES ),
          movies: new Array( ANIMATION_MANAGER.MAX_FRAMES ),
          frameTimes: new Array( ANIMATION_MANAGER.MAX_FRAMES ),
          frameDelays: new Array( ANIMATION_MANAGER.MAX_FRAMES ),
          frameContainer: animatedFrame,
          currentFrame: fromFrame,
          firstFrame: fromFrame,
          lastFrame: toFrame,
          nextAnimURL: null
        };
        
        /* If there exists a link to another animation, record it */
        if( $(animLink).length != 0 ){
          animation.nextAnimURL = $(animLink).attr("href");
        }
        
        /* Record movie data */
        var movies = anim.find(".movie");
        $(movies).each(function(){
          /* get data from rel attribute */
          var movieRelData = $(this).attr("rel");
          movieRelData = movieRelData.split(" ");
          /* Should be exactly two tokens */
          if(movieRelData.length == 2){
            var movieName = movieRelData[0];
            var movieFrame = parseInt(movieRelData[1]);
            var movieElement = $(this);
            
            if(animation.movies[movieFrame-1] == null){
              animation.movies[movieFrame-1] = new Array();
            }
            animation.movies[movieFrame-1].push({ name: movieName,
                                               element: movieElement,
                                             elementID: $(movieElement).attr("id")});
          }

        });

        /* For each frame... */
        var frameCount = 0;
        var dataCount = 0;
        for(frameCount = fromFrame; frameCount <= toFrame; frameCount ++){
          
          /* Get the elements in this frame */
          animatedFrame = anim.find(".frame-" + frameCount);
          
          /* Create array to hold animation records for each element in the frame */
          var frameAnimationData = new Array();
        
          /* Record element properties useful for animations */
          $(animatedFrame).find(".element").each( function( i ){
            var record = {
              width : $(this).width(),
              height : $(this).height(),
              top : $(this).position().top,
              left : $(this).position().left,
              opacity : $(this).css("opacity"),
              element : $(this)
            };

            frameAnimationData.push(record);
          });
          
          /* Add frame data to animation */
          animation.frames[frameCount - 1] = frameAnimationData;
          
          /* Retrieve and store animation data related to timing and delays */
          if( dataCount < timeData.length ){
            animation.frameTimes[frameCount - 1] = timeData[dataCount];
          }
          if( dataCount < delayData.length ){
            animation.frameDelays[frameCount - 1] = delayData[dataCount];
          }
          
          /* Advance elements to next frame */
          $(animatedFrame).removeClass("frame-" + frameCount);
          $(animatedFrame).addClass("frame-" + (frameCount + 1));
          
          dataCount ++;
        }
        
      /* If the animation is already in the list, replace it */
      var animExists = false
      for(i = 0; i < ANIMATION_MANAGER.animations.length; i++){
        if( ANIMATION_MANAGER.animations[i].animationClass == animClass ){
          ANIMATION_MANAGER.animations[i] = animation;
          animExists = true;
        }
      }
        
      /* Otherwise add it to the end */
      if(!animExists){
        ANIMATION_MANAGER.animations.push(animation);
      }
      
      /* Reset animation back to current frame */
      $(animatedFrame).removeClass("frame-" + frameCount);
      $(animatedFrame).addClass("frame-" + fromFrame);
 
  },

  animateFrame: function( anim, toFrame, callback ){
    
    if( anim == null ){
      if(DEBUG) DEBUG.printError("animateFrame( anim, toFrame, callback ): Animation is null.");
      return false;
    }
    
    if(toFrame == null || typeof(toFrame) != 'number' || toFrame < 1){
      toFrame = 1;
    }
    
    if(toFrame > ANIMATION_MANAGER.MAX_FRAMES || anim.frames == null ||anim.frames[toFrame - 1] == null){
      if(DEBUG) DEBUG.printError("animateFrame( anim, toFrame, callback ): Frame " + toFrame + " doesn't exist in the animation.");
      return;
    }
    
// ?? Need more boundary checks...
    
    /* Calculate number of elements in this frame of this animation */
    var numberOfAnimatedElements = anim.frames[toFrame - 1].length;
    
    /* Get animation time and delay */
    var animTime = anim.frameTimes[toFrame - 1];
    var animDelay = anim.frameDelays[toFrame - 1];
    
    /* Only animate if there is something to animate */
    if(numberOfAnimatedElements <= 0){
      /* Wait the given delay before doing callback */
      if( callback != null && typeof( callback ) == "function"){
        setTimeout( callback, animDelay );
      }
      return;
    }

    /* Create barrier to wait for animations to complete */
    var barrier = new BarrierSync( numberOfAnimatedElements );

    /* Animate elements to the next frame */
    for(i = 0; i < numberOfAnimatedElements; i++){

      /* Get the style attributes this element will be animated upon */
      var animWidth = anim.frames[toFrame - 1][i].width;
      var animHeight = anim.frames[toFrame - 1][i].height;
      var animLeft = anim.frames[toFrame - 1][i].left;
      var animTop = anim.frames[toFrame - 1][i].top;
      var animOpacity = anim.frames[toFrame - 1][i].opacity;

      $(anim.frames[toFrame - 1][i].element).stop().animate(
          {width : animWidth,
           height : animHeight,
           left : animLeft,
           top : animTop}, animTime, "linear", barrier.getNotifier( i ) );
      //?? NOTE: We no longer animate opacity because it causes problems in IE with transparent png
    }
    
    /* Wait for animated elements to reach their positions */
    barrier.wait( 200, function(){
      
      /* Update current frame */
      $(anim.frameContainer).removeClass("frame-" + anim.currentFrame);
      $(anim.frameContainer).addClass("frame-" + toFrame);
      anim.currentFrame = toFrame;
      
      /* play any movies that exist in this frame...*/
      ANIMATION_MANAGER.playMovies( anim, function(){
        /* Do callback after delay */
        if( callback != null && typeof(callback) == "function" ){
          setTimeout(callback,animDelay);
        }
      });
    });
  
  },
  
  playMovies: function( anim, callback ){
    
    // Don't play movies if we don't have flash
    if(!flash){
      callback();
      return;
    }
        
    if( callback != null && typeof(callback) != 'function' ){
      if(DEBUG) DEBUG.printError("playMovies( anim, callback ): callback was specified but was not a function.");
      return;
    }
    
    if( anim == null ){
      if( callback != null ){
        callback();
      }
      return;
    }
    
    /* Check if there is a movie to be played on this frame */
    var movies = anim.movies[anim.currentFrame - 1];
    var movieBarrier;
    if( movies != null && movies.length > 0){
      
      /* Disable sound controls and fade out music */
      flashSound.disable();
      
      /* Create barrier to wait for movies to finish */
      movieBarrier = new BarrierSync(movies.length);
      
        $(movies).each(function(i){
          /* Get movie attributes */
          var movieName = movies[i].name;
          var movieElement = $(movies[i].element);
          var movieElementWidth = $(movieElement).width();
          var movieElementID = $(movies[i].element).attr("id");
          var movieClass = $(movies[i].element).attr("class");
          
          /* Clone the element and hide it behind the original */
          $(movieElement).attr("id","");
          var playerElement = $(movieElement).clone();
          $(playerElement).insertBefore(movieElement)
          $(playerElement).attr("id",movieElementID);
          
          /* Hide original element so we can click on movie player buttons */
          $(movieElement).css("width","0px");
          
          /* Add and play movie */
          var player = MOVIE_MANAGER.addPlayer(movieElementID, movieClass);
          player.loadMovie(movieName);
          player.waitForComplete( function(){
            /* Re-show the original snapshot */
            $(movieElement).css("width", movieElementWidth + "px");

            /* Remove the movie */
            $("#" + movieElementID).remove();
            $(movieElement).attr("id",movieElementID);
            /* Notify barrier that movie has finished */
            var callbk = movieBarrier.getNotifier(i);
            callbk();
          });
          player.play();
        });
    }
    
    /* If there are movies, wait for them to finish */
    if(movieBarrier != null){
      movieBarrier.wait(1000, function(){
        
        /* Re-enable sound controls */
        flashSound.enable();
        
        /* Do callbacks */
        if( callback != null && typeof(callback) == "function" ){
          callback();
        }
      });
    }else{
      /* Otherwise do callbacks immediately */
      if( callback != null && typeof(callback) == "function" ){
        callback();
      }
    }
  },
  
  animateSlideshow: function( anim, fromFrame ){
    
    if( anim == null ){
      if(DEBUG) DEBUG.printError("animateSlideshow( anim, fromFrame ): Animation is null.");
      return;
    }
    
    if( fromFrame != null && typeof(fromFrame) != 'number' ){
      if(DEBUG) DEBUG.printError("animateSlideshow( anim, fromFrame ): fromFrame was specified but was not a number.");
      return;
    }
    
    if( fromFrame < anim.firstFrame || fromFrame > anim.lastFrame ){
      if(DEBUG) DEBUG.printError("animateSlideshow( anim, fromFrame ): fromFrame was not in range [" + anim.firstFrame + "," + anim.lastFrame + "].");
      return;
    }
    
    if( fromFrame == null ){
      fromFrame = anim.firstFrame;
    }
        
    var slideshow = {
      
      /* Recursively animate the slideshow throughout all of its frames */
      animateNext : function( nextFrame ){
        return function(){
          
          if(slideshow.terminate == true){
            return;
          }

          if(nextFrame > anim.lastFrame){
            /* Check if this animation links to another... */
            if( anim.nextAnimURL != null){
              CONTENT_MANAGER.changeContent( anim.nextAnimURL, null,
                /* If we failed to load the next animation ... */
                function(){
                    if(DEBUG) DEBUG.printError("animateSlideshow( anim, fromFrame ): A link to another slideshow existed but failed to load.");
                    return;
                });
            }

            return;
            
          }else{
            ANIMATION_MANAGER.animateFrame( anim, nextFrame, slideshow.animateNext( nextFrame + 1 ) );
          }
        };
      },
      
      /* terminate the animation? */
      terminate : false,
      
      /* stop the animation */
      stop : function(){
        slideshow.terminate = true;
      }
    }
    
    /* Start the animation */
    slideshow.animateNext(fromFrame)();
    
    /* Record this aniamtion as active */
    ANIMATION_MANAGER.activeSlideshows.push(slideshow);
  },
  
  terminateActiveSlideshows: function(){
    for(i = 0; i < ANIMATION_MANAGER.activeSlideshows.length; i++){
      ANIMATION_MANAGER.activeSlideshows[i].stop();
    }
  }
};