package net.mkonrad.synes.ctrl;

import java.util.Enumeration;

import javax.media.j3d.Behavior;
import javax.media.j3d.WakeupCondition;
import javax.media.j3d.WakeupOnElapsedTime;

import net.mkonrad.synes.gui.elements.BeatGraphLine;
import net.mkonrad.synes.util.DbgTools;

public class BeatTimingBehavior extends Behavior
{
  private WakeupOnElapsedTime _timeOut = null;
  private BeatGraphLine _graphLine;
  private long _lastStimulus = 0;
  private long _lastBeat = 0;

  public BeatTimingBehavior(BeatGraphLine graphLine, long timeDelay)
  {
    _graphLine = graphLine;
    setTimeDelay(timeDelay);
  }

  /**
   * Reinitialize this animation by setting the time-correction values to
   * default values.
   * This is used when a Graph is paused, because there would be incorrect
   * timing. 
   */
  public void reinit()
  {
    _lastStimulus = 0;
    _lastBeat = 0;
  }

  public void setTimeDelay(long timeDelay)
  {
    //DbgTools.print(_graphLine.getGraph().getId() +  " - new time out after: " + timeDelay);
    
    if (timeDelay > 0)
    {
      _timeOut = new WakeupOnElapsedTime(timeDelay);
    }
    else
      _timeOut = null;
  }
  
  public void initialize()
  {
    if (_timeOut != null)
    {
      wakeupOn(_timeOut);
    }
  }

  /**
   * This method is called when the time is up (i.e. the next "beat" shall
   * occur). The BeatGraph then animates the beat and we receive the
   * time for the next beat that shall occur.
   * This function handles also the processing time that can cause a small delay
   * of a few milliseconds, which, added up, can lead to an unsynchronized
   * BeatGraph animation.
   * @see Bachelorthesis 
   */
  public void processStimulus(Enumeration criteria)
  {
    long origTime;
    long nextBeat = origTime = _graphLine.beat();  //make an animation for the next beat, get back the time when the next beat shall occur
    
    if (_lastStimulus > 0 && _lastBeat > 0)
    {
      nextBeat -= (System.currentTimeMillis() - _lastStimulus) - _lastBeat; //correct the beat timing because of other processing time

      if (nextBeat < 0) //this beat is too fast! it should have already appeared...
      {
        nextBeat = origTime + nextBeat; //... so skip this beat and take the next one
      }
    }

    setTimeDelay(nextBeat);
    
    if (_timeOut != null)
    {
      wakeupOn(_timeOut);
    }
    
    _lastBeat = nextBeat;
    _lastStimulus = System.currentTimeMillis();
  }

  /**
   * Resume this animation within a given amount of time
   * @param resumeMs  resume time
   */
  public void resumeIn(long resumeMs)
  {
    setEnable(true);
    setTimeDelay(resumeMs);
  }
}
