/**
 * A linked list of ContactNodes that mimics a contact list or an address book
 **/
public abstract class AbstractContactList{
  
  /** the head of the list **/
  private ContactNode head;
  
  /**
   * Method to return the variable head
   * @return head
   **/
  public ContactNode getHead(){
    return head;
  }
  
  /**
   * Method to set the variable head
   * @param head new head of the list
   **/
  public void setHead(ContactNode head){
    this.head = head;
  }
  
  /**
   * Method to insert a new node into the list. If the list is empty i.e. head equals null,
   * then set the head equal to the new node.
   * @param node ContactNode that to be inserted
   */
  public void add(ContactNode node){
    //if the list is empty, let head equal the incoming node
    if (head == null)
      head = node;
    
    //if the list is not empty, add after the last node
    else
      last().setNext(node);
  }
  
 /**
  * Method to delete a node from the list and return true if it was successful.
  * @param node the node to be deleted
  * @return true if the removal was successful
  **/
  public boolean delete(ContactNode node){
    ContactNode current = head;
    //if the node to remove is the head, set head equal to its next node
   if (node == head){
      ContactNode temp = head;
      head = head.getNext();
      temp.setNext(null);
      return true;
    }
    //if the node is not the head, loop through the list
    while(current.getNext()!= null){
      //if the current's next is equal to the node, then current must be the node previous to the one we want to remove
      if (current.getNext() == node){
        //set current's next equal to the next of the node that is about to be deleted
        current.setNext(node.getNext());
        //set node's next to null
        node.setNext(null);
        //the method is successful
        return true;
      }
      current = current.getNext();
    }
    //the incoming node does not exist within the list; method is not successful
    return false;
  } 
  
  /**
   * Method to find the minimum String name in a certain interval of the list
   * @param size the number of items to look at from the beginning
   * @return the ContactNode containing the ContactEntry with the minimum String
   **/
  public ContactNode findMinName(int size){
    //if the list is of only one item, no point to compare just return that one item
    if (size == 1)
      return head;
    //if the list is of no items or negative items, return null
    if (size <= 0)
      return null;
    
    //Assume the minimum is the head of the list
    ContactNode min = head; 
    //Start comparing with the second node
    ContactNode current = head.getNext();
    for (int i = 1; i < size; i++){
      if (current.getData().getName().compareTo(min.getData().getName()) < 1)
        min = current;
      current = current.getNext();
    }
    return min;
  }
  
  /**
   * Method to insert a new node (with the ContactEntry contactToInsert as the data) 
   * after the specified node and return true if successful.
   * @param node node to insert after
   * @param contactToInsert node that will be inserted after node
   * @return true if insertion was successful, false otherwise.
   **/
  public boolean insertAfter(ContactNode node, ContactNode contactToInsert){
    ContactNode current = head; 
    while (current != null){
      if (current.equals(node))
        break;
      current = current.getNext();
    }
    
    if (current == null)
      return false;
    else{
      ContactNode oldNext = node.getNext();
      node.setNext(contactToInsert);
      contactToInsert.setNext(oldNext);
      return true;
    }
  }
  
  /**
   * Method to return the last node in the list
   * @return the last node in the list, null if the list is empty
   **/
  public ContactNode last(){
    ContactNode current = head; 
    while (current.getNext() != null){
      current = current.getNext();
    }
    return current;
  }
  
  /**
   * Method to search for a node within the list.
   * @param node the node to find
   * @return true if the node is in the list, false otherwise
   **/
  public boolean search(ContactNode node){
    ContactNode current = head; 
    while (current != null){
      if (current == node)
        return true;
      current = current.getNext();
    }
    return false;
  }
  
  /**
   * Method to return the size of or the number of elements in the list
   * @return the size of the list
   **/
  public int size(){
    if (head == null)
      return 0;
    else{
      int count = 0;
      ContactNode current = head; 
      while (current != null){
        count++;
        current = current.getNext();
      }
      return count;
    }
  }
  
  /**
   * Method to sort the entries in the list by their name.
   **/
  public void sortByName(){
    for (int i = size(); i > 0; i-- ){
      //find the ContactNode containing the ContactEntry with the minimum String name
      ContactNode maxNode = findMinName(i);
      //delete that node from the list
      delete(maxNode);
      //and add it to the end
      add(maxNode);
    }
  }
  
  /**
   * Provides a String representation of the entire list by calling the toString of each item in the list.
   * @return a String representation of the entire list
   **/
  public String toString(){
    if (head == null)
      return "The list is empty";
    else{
      String ret = "";
      ContactNode current = head; 
      while (current != null){
        ret = ret + current.toString() + "\n\n";
        current = current.getNext();
      }
      return ret;
    }
  }
  
  /**
   * Method to insert a new node into the list at the given index where 0 is the position 
   * of the first node. If the index is negative or if the index is greater than the size 
   * of list minus 1, do not proceed with the method and warn the user of the invalid input.
   * @param node ContactNode that is to be inserted
   */
  public abstract void add(ContactNode node, int index);
  
  
  /**
   * Method to find and return the node previous to the one inputted
   * @param node find the node previous this one
   */
  public abstract ContactNode getPrevious(ContactNode node);
  
  
  /**
   * Method to swap the second node and the second to last node in the list. May not use copies. 
   * Must switch the exact nodes and not merely the data within the modes. Method can only be 
   * performed on list with sizes greater than or equal to 4. 
   **/
  public abstract void swapSecondAndSecondToLast();
  
}    
