//  (c) 2010 Thomas A. Alspaugh.  All rights reserved.
package oo;


/**
  A flip (a pitch, roll, yaw, or null flip having no effect)
  used by <tt>Mattress3</tt>'s implementation. &nbsp;
  <p>
  Each of its four subclasses implements its
  {@link #composeWith(Flip)} method identically,
  by returning <tt>_other.composedWith(this)</tt>.&nbsp;
  This selects (at compile time)
  the appropriate <tt>composedWith</tt> method
  for <tt>composedWith</tt>'s run-time argument:&nbsp;

  <ol>
    <li> {@link #composedWith(FlipNULL)} </li>
    <li> {@link #composedWith(FlipPITCH)} </li>
    <li> {@link #composedWith(FlipROLL)} </li>
    <li> {@link #composedWith(FlipYAW)} </li>
  </ol>

  <p>
  Each subclass's four <tt>composedWith</tt> methods
  then simply returns the single possible result
  for the parameter type.&nbsp;

  <p>
  The result of composing non-{@link #NULL} flips A and B
  follows a simple pattern
  that can be verified
  by examining <q>OO Examples</q>
  <a href='../../oo-example.html#Figure2'>Figure&nbsp;2</a>:
  </p>

  <table frame='box' rules='all'>
  <tbody>
    <tr>
      <th> The two cases </th>
      <th> A composed with B </th>
    </tr>

    <tr>
      <td> A == B </td>
      <td> {@link #NULL} </td>
    </tr>

    <tr>
      <td> A != B </td>
      <td> The other non-{@link #NULL} flip </td>
    </tr>

  </tbody>
  </table>

  <p>
  The figure also shows that composition of flips is
  <a href='../../operation.html#commutative'>commutative</a>.

  <p>
  Of course,
  the {@link #NULL} flip when composed with any other flip C
  is that same flip C
  (including the case where C is itself {@link #NULL}).&nbsp;
  </p>

*/
abstract public class Flip {

  /**
    A <q>null flip</q> that does nothing to an orientation.
  */
  static public final FlipNULL  NULL  = new FlipNULL();

  /**
    A pitch through a half turn (180&deg;)
  */
  static public final FlipPITCH PITCH = new FlipPITCH();

  /**
    A roll through a half turn (180&deg;)
  */
  static public final FlipROLL  ROLL  = new FlipROLL();

  /**
    A yaw through a half turn (180&deg;)
  */
  static public final FlipYAW   YAW   = new FlipYAW();

  /**
    Protected default constructor,
    can only be called by subclasses of Flip.
  */
  protected Flip() { }

  /**
    The flip's name
    (<q>null</q>, <q>pitch</q>, <q>roll</q>, or <q>yaw</q>).
  */
  abstract public String toString();

  /**
    Package-private method composing this flip with a null flip.
    @throws NullPointerException  If _other is <tt>null</tt>.
    @return This flip.
  */
  abstract Flip composedWith(FlipNULL  _other);

  /**
    Package-private method composing this flip with a FlipPITCH.
    @throws NullPointerException  If _other is <tt>null</tt>.
  */
  abstract Flip composedWith(FlipPITCH _other);

  /**
    Package-private method composing this flip with a FlipROLL.
    @throws NullPointerException  If _other is <tt>null</tt>.
  */
  abstract Flip composedWith(FlipROLL  _other);

  /**
    Package-private method composing this flip with a FlipYAW.
    @throws NullPointerException  If _other is <tt>null</tt>.
  */
  abstract Flip composedWith(FlipYAW   _other);

  /**
    Composes this flip with _other,
    resulting in a flip.&nbsp;
    @throws NullPointerException  If _other is <tt>null</tt>.
  */
  abstract public Flip composeWith(Flip _other);

  /**
    Applies this flip to an orientation,
    producing an orientation.&nbsp;
    @throws NullPointerException  If _initial is <tt>null</tt>.
  */
  abstract public Orientation flip(Orientation _initial);

}

/**
  Package-private singleton class
  for the null flip that has no effect when applied to an orientation.
*/
class FlipNULL extends Flip {

  /**  Package private constructor for null flip.  */
  FlipNULL() {}

  public String toString() {  return "null";  }

  /**
    @return {@link #NULL}.
  */
  Flip composedWith(FlipNULL  _other) {
    if (null == _other) {  throw new NullPointerException("_other");  }
    return _other;
  }
  /**
    @return <tt>_other</tt>.
  */
  Flip composedWith(FlipPITCH _other) {
    if (null == _other) {  throw new NullPointerException("_other");  }
    return _other;
  }
  /**
    @return <tt>_other</tt>.
  */
  Flip composedWith(FlipROLL  _other) {
    if (null == _other) {  throw new NullPointerException("_other");  }
    return _other;
  }
  /**
    @return <tt>_other</tt>.
  */
  Flip composedWith(FlipYAW   _other) {
    if (null == _other) {  throw new NullPointerException("_other");  }
    return _other;
  }

  public Flip composeWith(Flip _other) {
    if (null == _other) {  throw new NullPointerException("_other");  }
    return _other.composedWith(this);
  }

  public Orientation flip(Orientation _initial) {
    if (null == _initial) {  throw new NullPointerException("_initial");  }
    return _initial;
  }

}

/**
  Package-private singleton class
  for a pitch.
*/
class FlipPITCH extends Flip {

  /**  Package private constructor for pitch.  */
  FlipPITCH() {}

  public String toString() {  return "pitch";  }

  Flip composedWith(FlipNULL  _other) {
    if (null == _other) {  throw new NullPointerException("_other");  }
    return PITCH;
  }
  /**
    @return {@link #NULL}.
  */
  Flip composedWith(FlipPITCH _other) {
    if (null == _other) {  throw new NullPointerException("_other");  }
    return NULL;
  }
  /**
    @return {@link #YAW}.
  */
  Flip composedWith(FlipROLL  _other) {
    if (null == _other) {  throw new NullPointerException("_other");  }
    return YAW;
  }
  /**
    @return {@link #ROLL}.
  */
  Flip composedWith(FlipYAW   _other) {
    if (null == _other) {  throw new NullPointerException("_other");  }
    return ROLL;
  }

  public Flip composeWith(Flip _other) {
    if (null == _other) {  throw new NullPointerException("_other");  }
    return _other.composedWith(this);
  }

  public Orientation flip(Orientation _initial) {
    if (null == _initial) {  throw new NullPointerException("_initial");  }
    return (Orientation) _initial.pitch();
  }

}

/**
  Package-private singleton class for a roll.
*/
class FlipROLL extends Flip {

  /**  Package private constructor for roll.  */
  FlipROLL() {}

  public String toString() {  return "roll";  }

  Flip composedWith(FlipNULL  _other) {
    if (null == _other) {  throw new NullPointerException("_other");  }
    return ROLL;
  }
  /**
    @return {@link #YAW}.
  */
  Flip composedWith(FlipPITCH _other) {
    if (null == _other) {  throw new NullPointerException("_other");  }
    return YAW;
  }
  /**
    @return {@link #NULL}.
  */
  Flip composedWith(FlipROLL  _other) {
    if (null == _other) {  throw new NullPointerException("_other");  }
    return NULL;
  }
  /**
    @return {@link #PITCH}.
  */
  Flip composedWith(FlipYAW   _other) {
    if (null == _other) {  throw new NullPointerException("_other");  }
    return PITCH;
  }

  public Flip composeWith(Flip _other) {
    if (null == _other) {  throw new NullPointerException("_other");  }
    return _other.composedWith(this);
  }

  public Orientation flip(Orientation _initial) {
    if (null == _initial) {  throw new NullPointerException("_initial");  }
    return (Orientation) _initial.roll();
  }

}

/**
  Package-private singleton class for a yaw.
*/
class FlipYAW extends Flip {

  /**  Package private constructor for yaw.  */
  FlipYAW() {}

  public String toString() {  return "yaw";  }

  Flip composedWith(FlipNULL  _other) {
    if (null == _other) {  throw new NullPointerException("_other");  }
    return YAW;
  }
  /**
    @return {@link #ROLL}.
  */
  Flip composedWith(FlipPITCH _other) {
    if (null == _other) {  throw new NullPointerException("_other");  }
    return ROLL;
  }
  /**
    @return {@link #PITCH}.
  */
  Flip composedWith(FlipROLL  _other) {
    if (null == _other) {  throw new NullPointerException("_other");  }
    return PITCH;
  }
  /**
    @return {@link #NULL}.
  */
  Flip composedWith(FlipYAW   _other) {
    if (null == _other) {  throw new NullPointerException("_other");  }
    return NULL;
  }

  public Flip composeWith(Flip _other) {
    if (null == _other) {  throw new NullPointerException("_other");  }
    return _other.composedWith(this);
  }

  public Orientation flip(Orientation _initial) {
    if (null == _initial) {  throw new NullPointerException("_initial");  }
    return (Orientation) _initial.yaw();
  }

}


