package jist.swans.radio;

import jist.runtime.JistAPI;
import jist.swans.Node;
import jist.swans.mac.MacInterface;
import jist.swans.misc.Message;
import jist.swans.misc.MessageAnno;
import jist.swans.misc.MessageEvent;
import jist.swans.misc.Event;

public abstract class AbstractRadio {

  public static final long DEFAULT_DROP_MSG_DURATION = 100;

  public static class RadioSendEvent extends MessageEvent {
    public RadioInfo radioInfo;
    public long duration;

    public RadioSendEvent() { super(false); }
    public RadioSendEvent(int nodeId) { super(nodeId); }

    public void handle(RadioInfo radioInfo, Message msg,
                       MessageAnno anno, long duration) {
      this.radioInfo = radioInfo;
      this.duration = duration;
      super.handle(msg, anno);
    }
  }

  public static class RadioDropEvent extends MessageEvent {
    public String reason;
    public RadioInfo radioInfo;
    public double power_mW;
    public long duration;

    public RadioDropEvent() { super(false); }
    public RadioDropEvent(int nodeId) { super(nodeId); }

    public void handle(String reason, RadioInfo radioInfo, Message msg,
                       MessageAnno anno, double power_mW, long duration) {
      this.reason = reason;
      this.radioInfo = radioInfo;
      this.power_mW = power_mW;
      this.duration = duration;
      super.handle(msg, anno);
    }

    public void handle(String reason, RadioInfo radioInfo, Message msg,
                       MessageAnno anno, double power_mW) {
      this.handle(reason, radioInfo, msg, anno, power_mW, DEFAULT_DROP_MSG_DURATION);
    }
  }

  public static class RadioReceiveEvent extends MessageEvent {
    public RadioInfo radioInfo;
    public double power_mW;
    public long start;
    public long end;

    public RadioReceiveEvent() { super(false); }
    public RadioReceiveEvent(int nodeId) { super(nodeId); }

    public void handle(RadioInfo radioInfo, Message msg, MessageAnno anno,
                       double power_mW, long duration) {
      this.radioInfo = radioInfo;
      this.power_mW = power_mW;
      this.start = JistAPI.getTime();
      this.end = this.start + duration;
      super.handle(msg, anno);
    }
    public void handle(RadioInfo radioInfo, Message msg,
        MessageAnno anno, double power_mW, long start, long end) {
      this.radioInfo = radioInfo;
      this.power_mW = power_mW;
      this.start = start;
      this.end = end;
      super.handle(msg, anno);
    }
  }

  public abstract static class RadioChannelSwitchEvent extends Event {
    public RadioInterface.RFChannel oldChannel;
    public RadioInterface.RFChannel newChannel;
    public long delay;

    public RadioChannelSwitchEvent() { super(false); }
    public RadioChannelSwitchEvent(int nodeId) { 
      this.nodeId = nodeId;
    }

    public void handle(RadioInterface.RFChannel oldChannel,
                       RadioInterface.RFChannel newChannel) {
      handle(oldChannel, newChannel, -1);
    }

    public void handle(RadioInterface.RFChannel oldChannel,
                       RadioInterface.RFChannel newChannel, long delay) {
      this.oldChannel = oldChannel;
      this.newChannel = newChannel;
      this.delay      = delay;
      super.handle(nodeId);
    }
  }

  public static class RadioChannelStartSwitchEvent extends RadioChannelSwitchEvent {
    public RadioChannelStartSwitchEvent() { super(); }
    public RadioChannelStartSwitchEvent(int nodeId) { super(nodeId); }
  }

  public static class RadioChannelFinishSwitchEvent extends RadioChannelSwitchEvent {
    public RadioChannelFinishSwitchEvent() { super(); }
    public RadioChannelFinishSwitchEvent(int nodeId) { super(nodeId); }
  }

  public static class RadioRxDiversityEvent extends MessageEvent {
    public long delaySpread;
    public double powerCombined_mW;
    public double powerOld_mW;
    public double powerNew_mW;
    /** whether the diversity is the reason for the reception */
    public boolean causedRecv;
    /** whether the diversity is the reason for the drop */
    public boolean causedDrop;

    public RadioRxDiversityEvent() { super(false); }
    public RadioRxDiversityEvent(int nodeId) { super(nodeId); }

    public void handle(Message msg, long delaySpread,
        double powerOld_mW, double powerNew_mW, double powerCombined_mW,
        boolean causedRecv, boolean causedDrop) {
      this.delaySpread = delaySpread;
      this.powerOld_mW = powerOld_mW;
      this.powerNew_mW = powerNew_mW;
      this.powerCombined_mW = powerCombined_mW;
      this.causedRecv = causedRecv;
      this.causedDrop = causedDrop;
      super.handle(msg, null);
    }
  }


  private Node node;

  protected RadioSendEvent radioSendEvent;

  protected RadioReceiveEvent radioReceiveEvent;

  protected RadioDropEvent radioDropEvent;

  protected RadioChannelStartSwitchEvent radioChannelStartSwitchEvent;

  protected RadioChannelFinishSwitchEvent radioChannelFinishSwitchEvent;

  protected RadioRxDiversityEvent radioRxDiversityEvent;

  public AbstractRadio() {
    radioSendEvent                = new RadioSendEvent();
    radioReceiveEvent             = new RadioReceiveEvent();
    radioDropEvent                = new RadioDropEvent();
    radioChannelStartSwitchEvent  = new RadioChannelStartSwitchEvent();
    radioChannelFinishSwitchEvent = new RadioChannelFinishSwitchEvent();
    radioRxDiversityEvent         = new RadioRxDiversityEvent();
  }
  
  public Node getNode() {
    return node;
  }

  public void setNode(Node node) {
    this.node = node;

    radioSendEvent                = new RadioSendEvent(node.getNodeId());
    radioReceiveEvent             = new RadioReceiveEvent(node.getNodeId());
    radioDropEvent                = new RadioDropEvent(node.getNodeId());
    radioChannelStartSwitchEvent  = new RadioChannelStartSwitchEvent(node.getNodeId());
    radioChannelFinishSwitchEvent = new RadioChannelFinishSwitchEvent(node.getNodeId());
    radioRxDiversityEvent         = new RadioRxDiversityEvent(node.getNodeId());
  }

  public abstract RadioInterface getProxy();

  public abstract void setMacEntity(MacInterface proxy);

  //////////////////////////////////////////////////
  // overwrites
  //

  public abstract RadioInfo getRadioInfo();

}
