/**
 * @author Frank Zeyda, Kun Wei
 */
package cdx;

import javax.realtime.PeriodicParameters;
import javax.realtime.PriorityParameters;
import javax.realtime.PriorityScheduler;
import javax.realtime.RelativeTime;

import javax.safetycritical.AperiodicEvent;
import javax.safetycritical.PeriodicEventHandler;
import javax.safetycritical.StorageConfigurationParameters;

import java.util.Iterator;
import java.util.Set;

/**
 * InputFrameHandler is a periodic handler that reads and stores radar frames
 * as they arrive. It also updates the shared variable "state" for previous
 * aircraft positions.
 */
public class InputFrameHandler extends PeriodicEventHandler {
  public final CDxMission mission;
  public final AperiodicEvent reduce;

  public InputFrameHandler(CDxMission mission, AperiodicEvent event) {
    super(
      new PriorityParameters(
        PriorityScheduler.instance().getMaxPriority()),
        new PeriodicParameters(null,
          new RelativeTime(Constants.DETECTOR_PERIOD, 0)),
        new StorageConfigurationParameters(32768, 4096, 4096),
        65536, "InputHandler");
    this.mission = mission;
    reduce = event;
  }

  public void handleEvent() {
    System.out.println("[InputHandler] called");

    /* Terminate mission when enough frames have been processes. */
    if ((mission.simulator.framesProcessed +
         mission.simulator.droppedFrames) == Constants.MAX_FRAMES) {
      mission.requestSequenceTermination();
      mission.dumpResults();
      return ;
    }

    /* Performs device access to read the next radar frame. */
    mission.simulator.frameBuffer.readFrame();

    if (mission.simulator.detectorReady) {
      RawFrame frame = mission.simulator.frameBuffer.getFrame();

      /* Store the frame and update previous positions. */
      StoreFrame(frame);

      /* Increment the number of processed frames. */
      mission.simulator.framesProcessed++;

      /* Release ReducerHandler to perform the voxel hashing. */
      reduce.fire();
    }
    else {
      /* If the detector is not ready, read the next frame and drop it. */
      RawFrame drop = mission.simulator.frameBuffer.getFrame();

      System.out.println("A frame has been dropped.");

      /* Increment the number of dropped frames. */
      mission.simulator.droppedFrames++;
    }
  }

  /* This method correspond to the StoreFrame action in the S anchor. */

  public void StoreFrame(RawFrame frame) {
      /* Update shared data that holds previous aircraft positions. */
      updateState();

      /* Read the frame into the shared variable. */
      mission.getFrame().copy(frame);

      System.out.println("A new frame has been read.");
  }

  /**
   * This method records the current positions of all aircrafts in the
   * shared state variable of type StateTable.
   */
  public void updateState() {
    RawFrame frame = mission.getFrame();

    /* Used to determine disappeared aircrafts in the state table. */
    Set vanished = mission.getState().getCallSigns();

    for (int i = 0, pos = 0; i < frame.planeCnt; i++) {
      /* Get the current position of the next aircraft. */
      final float x = frame.positions[3 * i];
      final float y = frame.positions[3 * i + 1];
      final float z = frame.positions[3 * i + 2];

      /* Get the call sign of the next aircraft. */
      final byte[] cs = new byte[Constants.LENGTH_OF_CALLSIGN];
      for (int j = 0; j < cs.length; j++) {
        cs[j] = frame.callsigns[pos + j];
      }

      /* Advance index for call sign. */
      pos += cs.length;

      /* Create a new call sign. */
      CallSign callsign = new CallSign(cs);

      /* The aircrafts is in view; remove from the vanished set. */
      vanished.remove(callsign);

      /* Get old position from the state */
      final Vector3d old_pos = mission.getState().get(new CallSign(cs));

      if (old_pos == null) {
        /* We have detected a new aircraft. */
        /* Note that the callsign object is not actually inserted into the
         * StateTable but duplicated by the logic of the put method below. */
        mission.getState().put(callsign, x, y, z);
      }
      else {
        /* If the aircraft is already recorded in the stable table simply
         * update its position. */
        old_pos.set(x, y, z);
      }
    }

    StateTable state = mission.getState();

    /* Finally remove all aircrafts from the state table that have disappeared
     * from the radar in the current frame. This is important in order to give
     * them a zero velocity when they re-enter, a requirement of our model. */
    for (Iterator iter = vanished.iterator(); iter.hasNext(); ) {
      state.remove((CallSign) iter.next());
    }
  }
}
