/**
 * Stuff that all nodes and branches in the
 * sound tree know.
 **/
abstract public class DLLCollectableNode {
  /**
   * The next branch/node/whatever to process
   **/
  public DLLCollectableNode next;
  
  /**
   * The previous branch/node/whatever to process
   **/
  public DLLCollectableNode previous;
  
  /**
   * Constructor for DLLCollectableNode just sets
   * next to null
   **/
  public DLLCollectableNode(){
    next = null;
    previous = null;
  }
  
  /**
   * Methods to set and get next elements
   * @param nextOne next element in list
   **/
  public void setNext(DLLCollectableNode nextOne){
    this.next = nextOne;
  }
  
  public DLLCollectableNode getNext(){
    return this.next;
  }
  
  /**
   * Methods to set and get previous elements
   * @param prevOne next element in list
   **/
  public void setPrevious(DLLCollectableNode prevOne){
    this.previous = prevOne;
  }
  
  public DLLCollectableNode getPrevious(){
    return this.previous;
  }
  
  /**
   * Play the list of sound elements
   * after me
   **/
  public void playFromMeOn(){
    this.collect().play();
  }
  
  /**
   * Play the list of sound elements
   * from the start
   **/
  public void playFromStart() {
    DLLCollectableNode temp = this;
    
    while(temp.getPrevious() != null) {
      temp = temp.getPrevious();
    }
    temp.collect().play();
  }
  
  /** 
   * Collect all the sounds from me on
   **/
  abstract public Sound collect();
  
  /** 
   * Collect all the sounds from me on,
   * but if there's processing, do it after.
   **/
  abstract public Sound collectAfter();

  /** Method to remove node from list, fixing links appropriately.
   * @param node element to remove from list.
   **/
  public void remove(DLLCollectableNode node){
    DLLCollectableNode current = this;
    while (current.getPrevious() != null) {
      current = current.getPrevious();
    }
    
    if (current==node)
    {
      System.out.println("I can't remove the first node from the list.");
      return;
    };
    
    
    // While there are more nodes to consider
    while (current.getNext() != null)
    {
      if (current.getNext() == node && current.getNext() != this){
        // Set the one after node to be the current
        if (current.getNext().getNext() != null) {
          current.getNext().getNext().setPrevious(current);
        }
        // And simply make node's next be this next
        current.setNext(node.getNext());
        // Make this node point to nothing
        node.setNext(null);
        node.setPrevious(null);
        return;
      }
      current = current.getNext();
    }
    System.out.println("I can't remove this node.");
  }

  /**
   * Insert the input node after this node.
   * @param node element to insert after this.
   **/
  public void insertAfter(DLLCollectableNode node){
    // Save what "this" currently points at
    DLLCollectableNode oldNext = this.getNext();
    
    
    this.setNext(node);
    node.setPrevious(this);
    node.setNext(oldNext);
    if (oldNext != null) {
      oldNext.setPrevious(node);
    }
  }
  
  /**
   * Insert the input node before this node.
   * @param node element to insert before this.
   **/
  public void insertBefore(DLLCollectableNode node){
    // Save what "this" currently points at
    DLLCollectableNode oldPrev = this.getPrevious();
        
    this.setPrevious(node);
    node.setNext(this);
    node.setPrevious(oldPrev);
    if (oldPrev != null) {
      oldPrev.setNext(node);
    }
  }
  
  /**
   * Return the last element in the list
   **/
    public DLLCollectableNode last() {
      DLLCollectableNode current;
    
      current = this;
      while (current.getNext() != null)
      {
        current = current.getNext();
      };
      return current;
  }
  
  /**
   * Return the first element in the list
   **/
    public DLLCollectableNode first() {
      DLLCollectableNode current;
    
      current = this;
      while (current.getPrevious() != null)
      {
        current = current.getPrevious();
      };
      return current;
  }

  /**
   * Add the input node after the last node in this list.
   * @param node element to insert after this.
   **/
  public void addToTail(DLLCollectableNode node){
    this.last().insertAfter(node);
  }
  
  /**
   * Add the input node before the first node in this list.
   * @param node element to insert before first.
   **/
  public void addToHead(DLLCollectableNode node){
    this.first().insertBefore(node);
  }
}
    
