// ============================================================================
// File:               $File$
//
// Project:            
//
// Purpose:            
//
// Author:             Rammi
//
// Copyright Notice:   (c) 2008  Rammi (rammi@caff.de)
//                     This code is in the public domain.
//                     Use at own risk.
//                     No guarantees given.
//
// Latest change:      $Date$
//
// History:	       $Log$
//=============================================================================
package de.caff.asteroid.rammi;

import de.caff.asteroid.Asteroid;
import de.caff.asteroid.FrameInfo;
import de.caff.asteroid.MovingGameObject;

import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.LinkedBlockingQueue;

/**
 *  Trying to see the future.
 */
public class ThreadingFuturologist
        extends AbstractBasicFuturologist
        implements Runnable
{
  /** Frames collected by FrameReceiver interface, and analyzed in other thread. */
  private LinkedBlockingQueue<FrameInfo> incomingFrames = new LinkedBlockingQueue<FrameInfo>();

  /** Precalculated hit frames. */
  private ConcurrentHashMap<Integer, Integer> asteroidHits = new ConcurrentHashMap<Integer, Integer>();


  /**
   * Called each time a frame is received.
   * <p/>
   * <b>ATTENTION:</b> this is called from the communication thread!
   * Implementing classes must be aware of this and take care by synchronization or similar!
   *
   * @param frame the received frame
   */
  public void frameReceived(FrameInfo frame)
  {
    while (true) {
      try {
        incomingFrames.put(frame);
        return;
      } catch (InterruptedException e) {
        e.printStackTrace();
      }
    }
  }

  /**
   * When an object implementing interface <code>Runnable</code> is used
   * to create a thread, starting the thread causes the object's
   * <code>run</code> method to be called in that separately executing
   * thread.
   * <p/>
   * The general contract of the method <code>run</code> is that it may
   * take any action whatsoever.
   *
   * @see Thread#run()
   */
  public void run()
  {
    while (true) {
      try {
        FrameInfo next = incomingFrames.take();
        FrameInfo info;
        do {
          info = next;
          next = incomingFrames.poll();
        } while (next != null);

        handleFrameInfo(info);
      } catch (InterruptedException e) {
        e.printStackTrace();
      }
    }
  }

  /**
   *  Add a known hit for an asteroid.
   *  @param astIdent      asteroid ID
   *  @param frameCounter  frame counter when hit happens
   */
  protected void addHit(Integer astIdent, int frameCounter)
  {
    Integer currentCounter = asteroidHits.get(astIdent);
    if (currentCounter == null  ||  currentCounter > frameCounter) {
      asteroidHits.put(astIdent, frameCounter);
    }
  }

  /**
   *  Forget all knowledge about any asteroids.
   */
  protected void forgetEverything()
  {
    asteroidHits.clear();
  }

  /**
   * Get the number of frames until a game object is destroyed if it doesn't alter its movements.
   *
   * @param ident     ident of game object
   * @param frameInfo frame info of game object
   * @return number of frames till destruction or less than zero if frame is unknown
   */
  protected int getFramesTillDestroyed(Integer ident, FrameInfo frameInfo)
  {
    Integer hitFrame = asteroidHits.get(ident);
    if (hitFrame != null) {
      return hitFrame.intValue() - frameInfo.getIndex();
    }
    return HIT_FRAME_UNKNOWN;
  }

  static Futurologist createBackgroundFuturologist()
  {
    ThreadingFuturologist f = new ThreadingFuturologist();
    new Thread(f).start();
    return f;
  }
}
