/**
 * Sounds for a linked list
 **/
public class SoundElement {
  
  /**
   * The sound this element is associated with
   **/
  Sound mySound;
  
  /**
   * The next element to process
   **/
  public SoundElement next;
  
  /**
   * Constructor sets next to null
   * and references the input sound.
   **/
  public SoundElement(Sound aSound){
    next = null;
    mySound = aSound;
  }
  
  /**
   * Provide a printable representation of me
   **/
  public String toString(){
    return "SoundElement with sound: "+mySound+" (and next: "+next+").";
  }
  
  /**
   * Methods to set and get next elements
   * @param nextOne next element in list
   **/
  public void setNext(SoundElement nextOne){
    this.next = nextOne;
  }
  
  public SoundElement getNext(){
    return this.next;
  }
  
  /**
   * Play the list of sound elements
   * after me
   **/
  public void playFromMeOn(){
    this.collect().play();
  }
  
  /** 
   * Collect all the sounds from me on,
   * recursively.
   **/
  public Sound collect(){
    if (this.getNext() == null)
    {return mySound;}
    else
    {return mySound.append(this.getNext().collect());}
  }
      
  /** Method to remove node from list, fixing links appropriately.
   * @param node element to remove from list.
   **/
  public void remove(SoundElement node){
    if (node==this)
    {
      System.out.println("I can't remove the first node from the list.");
      return;
    };
    
    SoundElement current = this;
    // While there are more nodes to consider
    while (current.getNext() != null)
    {
      if (current.getNext() == node){
        // Simply make node's next be this next
        current.setNext(node.getNext());
        // Make this node point to nothing
        node.setNext(null);
        return;
      }
      current = current.getNext();
    }
  }

    
  /**
   * Insert the input node after this node.
   * @param node element to insert after this.
   **/
  public void insertAfter(SoundElement node){
    // Save what "this" currently points at
    SoundElement oldNext = this.getNext();
    this.setNext(node);
    node.setNext(oldNext);
  }
  
  /**
   * Return the last element in the list
   **/
    public SoundElement last() {
      SoundElement current;
    
      current = this;
      while (current.getNext() != null)
      {
        current = current.getNext();
      };
      return current;
  }

  /**
   * Add the input node after the last node in this list.
   * @param node element to insert after this.
   **/
  public void add(SoundElement node){
    this.last().insertAfter(node);
  }

    /**
   * copyNode returns a copy of this element
   * @return another element with the same sound
   */
  public SoundElement copyNode(){
    Sound copySound;
    if (this.mySound.getFileName() == null)
    {copySound = this.mySound.scale(1.0);} // Does nothing -- copies
    else
    {copySound = new Sound(this.mySound.getFileName());} // Copy from file
    SoundElement returnMe = new SoundElement(copySound);
    return returnMe;
  }
  
  /**
   * Repeat the input phrase for the number of times specified.
   * It always appends to the current node, NOT insert.
   * @param nextOne node to be copied in to list
   * @param count number of times to copy it in.
   */
  public void repeatNext(SoundElement nextOne,int count) {
    SoundElement current = this; // Start from here
    SoundElement copy; // Where we keep the current copy
    
    for (int i=1; i <= count; i++)
    {
      copy = nextOne.copyNode(); // Make a copy
      current.setNext(copy); // Set as next
      current = copy; // Now append to copy
    }
  }
  
    /**
   * Weave the input sound count times every skipAmount elements
   * @param nextOne SoundElement to be copied into the list
   * @param count how many times to copy
   * @param skipAmount how many nodes to skip per weave
   */
  public void weave(SoundElement nextOne, int count, int skipAmount) 
  {
    SoundElement current = this; // Start from here
    SoundElement copy; // Where we keep the one to be weaved in
    SoundElement oldNext; // Need this to insert properly
    int skipped; // Number skipped currently
    
    for (int i=1; i <= count; i++)
    {
      copy = nextOne.copyNode(); // Make a copy
      
      //Skip skipAmount nodes
      skipped = 1;
      while ((current.getNext() != null) && (skipped < skipAmount))
      {
        current = current.getNext();
        skipped++;
      };
      
      if (current.getNext() == null) // Did we actually get to the end early?
        break; // Leave the loop
      
      oldNext = current.getNext(); // Save its next
      current.insertAfter(copy); // Insert the copy after this one
      current = oldNext; // Continue on with the rest
    }
  }

  /**
   * Replace the one sound with the other sound
   * in all the elements from me on.
   * Decide two sounds are equal if come from same filename
   * @param oldSound sound to be replaced
   * @param newSOund sound to put in its place
   **/
  public void replace(Sound oldSound, Sound newSound){
    if (mySound.getFileName() != null)
    {if (mySound.getFileName().equals(oldSound.getFileName()))
      {mySound = newSound;}}
      
      if (next != null)
    {next.replace(oldSound,newSound);}
    }
  
}
    
