package data;

/**
  * A QuoteValue consists of two values: A {@link #getBid bid} and an {@link #getOffer offer}.
  *
  * <p>QuoteValues usually represent prices. The bid is the price to be paid if someone wants to buy something
  * from the Shop. The offer is the price paid when the Shop bought the product.</p>
  *
  * <p>QuoteValues have two more attributes: the {@link #getSpread spread} and the {@link #getMid mid}. While
  * the mid is the mean value of bid and offer, the spread is defined as <code>bid - offer</code>.</p>
  *
  * @author Steffen Zschaler
  * @version 2.0 19/08/1999
  * @since v2.0
  */
public class QuoteValue extends Object implements Value {

  /**
    * The bid.
    *
    * @serial
    */
  protected Value m_vBid;

  /**
    * The offer.
    *
    * @serial
    */
  protected Value m_vOffer;

  /**
    * Create a new QuoteValue.
    *
    * @param vBid the bid
    * @param vOffer the offer
    *
    * @exception IllegalArgumentException if <code>(vBid.getClass() != vOffer.getClass())</code>.
    */
  public QuoteValue (Value vBid,
                     Value vOffer) {
    super();

    if (vBid.getClass() != vOffer.getClass()) {
      throw new IllegalArgumentException ("QuoteValue: Classes of bid and offer must be identical.");
    }

    m_vBid = vBid;
    m_vOffer = vOffer;
  }

  /**
    * Get the current bid of this QuoteValue.
    *
    * @override Never
    */
  public Value getBid() {
    return m_vBid;
  }

  /**
    * Get the current offer of this QuoteValue.
    *
    * @override Never
    */
  public Value getOffer() {
    return m_vOffer;
  }

  /**
    * Create and return a deep clone of the QuoteValue. {@link #getBid Bid} and {@link #getOffer offer} are
    * cloned and the clones become bid and offer of the new Value.
    *
    * @override Never
    */
  public Object clone() {
    return new QuoteValue ((Value) m_vBid.clone(), (Value) m_vOffer.clone());
  }

  /**
    * If the given value is a QuoteValue, its bid and offer get added to this value's bid and offer, resp.
    * Otherwise, the value is added to both, bid and offer. An analogous algorithm is used for all other
    * operations.
    *
    * @param v the value to be added.
    *
    * @override Never
    */
  public void addAccumulating (Value v) {
    Value vBidAdd;
    Value vOfferAdd;

    if (v instanceof QuoteValue) {
      QuoteValue qv = (QuoteValue) v;

      vBidAdd = qv.getBid();
      vOfferAdd = qv.getOffer();
    }
    else {
      vBidAdd = v;
      vOfferAdd = v;
    }

    getBid().addAccumulating (vBidAdd);
    getOffer().addAccumulating (vOfferAdd);
  }

  /**
    * If the given value is a QuoteValue, its bid and offer get subtracted from this value's bid and offer,
    * resp. Otherwise, the value is subtracted from both, bid and offer. An analogous algorithm is used for all
    * other operations.
    *
    * @param v the value to be subtracted.
    *
    * @override Never
    */
  public void subtractAccumulating (Value v) {
    Value vBidSub;
    Value vOfferSub;

    if (v instanceof QuoteValue) {
      QuoteValue qv = (QuoteValue) v;

      vBidSub = qv.getBid();
      vOfferSub = qv.getOffer();
    }
    else {
      vBidSub = v;
      vOfferSub = v;
    }

    getBid().subtractAccumulating (vBidSub);
    getOffer().subtractAccumulating (vOfferSub);
  }

  /**
    * If the given value is a QuoteValue, its bid and offer get multiplied by this value's bid and offer, resp.
    * Otherwise, both bid and offer are multiplied by the given value. An analogous algorithm is used for all
    * other operations.
    *
    * @param v the value to multiply by.
    *
    * @override Never
    */
  public void multiplyAccumulating (Value v) {
    Value vBidMul;
    Value vOfferMul;

    if (v instanceof QuoteValue) {
      QuoteValue qv = (QuoteValue) v;

      vBidMul = qv.getBid();
      vOfferMul = qv.getOffer();
    }
    else {
      vBidMul = v;
      vOfferMul = v;
    }

    getBid().multiplyAccumulating (vBidMul);
    getOffer().multiplyAccumulating (vOfferMul);
  }

  /**
    * Both bid and offer get multiplied by the given 'scalar'.
    *
    * @param dl the 'scalar' to multiply by.
    *
    * @override Never
    */
  public void multiplyAccumulating (double dl) {
    getBid().multiplyAccumulating (dl);
    getOffer().multiplyAccumulating (dl);
  }

  /**
    * Both bid and offer get multiplied by the given 'scalar'.
    *
    * @param fl the 'scalar' to multiply by.
    *
    * @override Never
    */
  public void multiplyAccumulating (float fl) {
    getBid().multiplyAccumulating (fl);
    getOffer().multiplyAccumulating (fl);
  }

  /**
    * Both bid and offer get multiplied by the given 'scalar'.
    *
    * @param l the 'scalar' to multiply by.
    *
    * @override Never
    */
  public void multiplyAccumulating (long l) {
    getBid().multiplyAccumulating (l);
    getOffer().multiplyAccumulating (l);
  }

  /**
    * Both bid and offer get multiplied by the given 'scalar'.
    *
    * @param n the 'scalar' to multiply by.
    *
    * @override Never
    */
  public void multiplyAccumulating (int n) {
    getBid().multiplyAccumulating (n);
    getOffer().multiplyAccumulating (n);
  }

  /**
    * If the given value is a QuoteValue, this value's bid and offer get divided by its bid and offer, resp.
    * Otherwise, both bid and offer are divided by the given value. An analogous algorithm is used for all
    * other operations.
    *
    * @param v the value to divide by.
    *
    * @override Never
    */
  public void divideAccumulating (Value v) {
    Value vBidDiv;
    Value vOfferDiv;

    if (v instanceof QuoteValue) {
      QuoteValue qv = (QuoteValue) v;

      vBidDiv = qv.getBid();
      vOfferDiv = qv.getOffer();
    }
    else {
      vBidDiv = v;
      vOfferDiv = v;
    }

    getBid().divideAccumulating (vBidDiv);
    getOffer().divideAccumulating (vOfferDiv);
  }

  /**
    * @see #addAccumulating
    *
    * @override Never
    */
  public Value add (Value v) {
    Value vBidAdd;
    Value vOfferAdd;

    if (v instanceof QuoteValue) {
      QuoteValue qv = (QuoteValue) v;

      vBidAdd = qv.getBid();
      vOfferAdd = qv.getOffer();
    }
    else {
      vBidAdd = v;
      vOfferAdd = v;
    }

    return new QuoteValue (getBid().add (vBidAdd), getOffer().add (vOfferAdd));
  }

  /**
    * @see #subtractAccumulating
    *
    * @override Never
    */
  public Value subtract (Value v) {
    Value vBidSub;
    Value vOfferSub;

    if (v instanceof QuoteValue) {
      QuoteValue qv = (QuoteValue) v;

      vBidSub = qv.getBid();
      vOfferSub = qv.getOffer();
    }
    else {
      vBidSub = v;
      vOfferSub = v;
    }

    return new QuoteValue (getBid().subtract (vBidSub), getOffer().subtract (vOfferSub));
  }

  /**
    * @see #multiplyAccumulating(data.Value)
    *
    * @override Never
    */
  public Value multiply (Value v) {
    Value vBidMul;
    Value vOfferMul;

    if (v instanceof QuoteValue) {
      QuoteValue qv = (QuoteValue) v;

      vBidMul = qv.getBid();
      vOfferMul = qv.getOffer();
    }
    else {
      vBidMul = v;
      vOfferMul = v;
    }

    return new QuoteValue (getBid().multiply (vBidMul), getOffer().multiply (vOfferMul));
  }

  /**
    * @see #multiplyAccumulating(double)
    *
    * @override Never
    */
  public Value multiply (double dl) {
    return new QuoteValue (getBid().multiply (dl), getOffer().multiply (dl));
  }

  /**
    * @see #multiplyAccumulating(float)
    *
    * @override Never
    */
  public Value multiply (float fl) {
    return new QuoteValue (getBid().multiply (fl), getOffer().multiply (fl));
  }

  /**
    * @see #multiplyAccumulating(long)
    *
    * @override Never
    */
  public Value multiply (long l) {
    return new QuoteValue (getBid().multiply (l), getOffer().multiply (l));
  }

  /**
    * @see #multiplyAccumulating(int)
    *
    * @override Never
    */
  public Value multiply (int n) {
    return new QuoteValue (getBid().multiply (n), getOffer().multiply (n));
  }

  /**
    * @see #divideAccumulating
    *
    * @override Never
    */
  public Value divide (Value v) {
    Value vBidDiv;
    Value vOfferDiv;

    if (v instanceof QuoteValue) {
      QuoteValue qv = (QuoteValue) v;

      vBidDiv = qv.getBid();
      vOfferDiv = qv.getOffer();
    }
    else {
      vBidDiv = v;
      vOfferDiv = v;
    }

    return new QuoteValue (getBid().divide (vBidDiv), getOffer().divide (vOfferDiv));
  }

  /**
    * Get the spread of this value. The spread is defined as
    * <code>{@link #getBid bid} - {@link #getOffer offer}</code>.
    *
    * @override Never
    */
  public Value getSpread() {
    return getBid().subtract (getOffer());
  }

  /**
    * Get the mid of this value. The mid is defined as
    * <code>({@link #getBid bid} + {@link #getOffer offer}) / 2</code>.
    *
    * @override Never
    */
  public Value getMid() {
    Value vReturn = getBid().add (getOffer());
    vReturn.divideAccumulating (new IntegerValue (2));
    return vReturn;
  }

  /**
    * Compare this value to the given one. This will compare the {@link #getMid mids}.
    *
    * @override Sometimes
    *
    * @exception ClassCastException if the given object could not be cast into a QuoteValue.
    */
  public int compareTo (Object o) {
    QuoteValue qvCompare = (QuoteValue) o;

    return getMid().compareTo (qvCompare.getMid());
  }

  /**
    * True, iff both {@link #getBid bid} and {@link #getOffer offer} are zero elements with respect to addition.
    *
    * @override Sometimes
    */
  public boolean isAddZero() {
    return ((getBid().isAddZero()) &&
            (getOffer().isAddZero()));
  }

  /**
    * True, iff both {@link #getBid bid} and {@link #getOffer offer} are zero elements with respect to
    * multiplication.
    *
    * @override Sometimes
    */
  public boolean isMulZero() {
    return ((getBid().isMulZero()) &&
            (getOffer().isMulZero()));
  }

  /**
    * True, iff both {@link #getBid bid} and {@link #getOffer offer} are one elements with respect to
    * multiplication.
    *
    * @override Sometimes
    */
  public boolean isMulOne() {
    return ((getBid().isMulOne()) &&
            (getOffer().isMulOne()));
  }
}