package com.artemis.systems;

import com.artemis.Aspect;
import com.artemis.Entity;
import com.artemis.EntitySystem;
import com.artemis.utils.ImmutableBag;

/**
 * The purpose of this class is to allow systems to execute at varying intervals.
 * <p/>
 * An example system would be an ExpirationSystem, that deletes entities after a certain
 * lifetime. Instead of running a system that decrements a timeLeft value for each
 * entity, you can simply use this system to execute in a future at a time of the shortest
 * lived entity, and then reset the system to run at a time in a future at a time of the
 * shortest lived entity, etc.
 * <p/>
 * Another example system would be an AnimationSystem. You know when you have to animate
 * a certain entity, e.g. in 300 milliseconds. So you can set the system to run in 300 ms.
 * to perform the animation.
 * <p/>
 * This will save CPU cycles in some scenarios.
 * <p/>
 * Implementation notes:
 * In order to start the system you need to override the inserted(Entity e) method,
 * look up the delay time from that entity and offer it to the system by using the
 * offerDelay(float delay) method.
 * Also, when processing the entities you must also call offerDelay(float delay)
 * for all valid entities.
 *
 * @author Arni Arent
 */
public abstract class DelayedEntityProcessingSystem extends EntitySystem
{
  private float delay;
  private boolean running;
  private float acc;

  public DelayedEntityProcessingSystem( Aspect aspect )
  {
    super( aspect );
  }

  @Override
  protected final void processEntities( ImmutableBag<Entity> entities )
  {
    for( int i = 0, s = entities.size(); s > i; i++ )
    {
      Entity entity = entities.get( i );
      processDelta( entity, acc );
      float remaining = getRemainingDelay( entity );
      if( remaining <= 0 )
      {
        processExpired( entity );
      }
      else
      {
        offerDelay( remaining );
      }
    }
    stop();
  }

  @Override
  protected void inserted( Entity e )
  {
    float delay = getRemainingDelay( e );
    if( delay > 0 )
    {
      offerDelay( delay );
    }
  }

  /**
   * Return the delay until this entity should be processed.
   *
   * @param e entity
   * @return delay
   */
  protected abstract float getRemainingDelay( Entity e );

  @Override
  protected final boolean checkProcessing()
  {
    if( running )
    {
      acc += world.getDelta();

      if( acc >= delay )
      {
        return true;
      }
    }
    return false;
  }

  /**
   * Process a entity this system is interested in. Substract the accumulatedDelta
   * from the entities defined delay.
   *
   * @param e the entity to process.
   * @param accumulatedDelta the delta time since this system was last executed.
   */
  protected abstract void processDelta( Entity e, float accumulatedDelta );

  protected abstract void processExpired( Entity e );

  /**
   * Start processing of entities after a certain amount of delta time.
   * <p/>
   * Cancels current delayed run and starts a new one.
   *
   * @param delta time delay until processing starts.
   */
  public void restart( float delay )
  {
    this.delay = delay;
    this.acc = 0;
    running = true;
  }

  /**
   * Restarts the system only if the delay offered is shorter than the
   * time that the system is currently scheduled to execute at.
   * <p/>
   * If the system is already stopped (not running) then the offered
   * delay will be used to restart the system with no matter its value.
   * <p/>
   * If the system is already counting down, and the offered delay is
   * larger than the time remaining, the system will ignore it. If the
   * offered delay is shorter than the time remaining, the system will
   * restart itself to run at the offered delay.
   */
  public void offerDelay( float delay )
  {
    if( !running || delay < getRemainingTimeUntilProcessing() )
    {
      restart( delay );
    }
  }

  /**
   * Get the initial delay that the system was ordered to process entities after.
   *
   * @return the originally set delay.
   */
  public float getInitialTimeDelay()
  {
    return delay;
  }

  /**
   * Get the time until the system is scheduled to run at.
   * Returns zero (0) if the system is not running.
   * Use isRunning() before checking this value.
   *
   * @return time when system will run at.
   */
  public float getRemainingTimeUntilProcessing()
  {
    if( running )
    {
      return delay - acc;
    }
    return 0;
  }

  /**
   * Check if the system is counting down towards processing.
   *
   * @return true if it's counting down, false if it's not running.
   */
  public boolean isRunning()
  {
    return running;
  }

  /**
   * Stops the system from running, aborts current countdown.
   * Call offerDelay or restart to run it again.
   */
  public void stop()
  {
    this.running = false;
    this.acc = 0;
  }
}

@@ -2547,17 +2547,16 @@
  in. Sub
-s
 tract th
@@ -3032,26 +3032,26 @@
 * @param del
-t
 a
+y
  time delay 

Fix some javadoc errors