//
//  IntQueue2.swift
//  Queues
//

import Foundation


/**
 IntQueue2 is our second implementation of a basic first-in, first-out queue
 for Integers.
 
 An IntQueue can be described as [n0, n1, ..., n_k], where n1 is the
 least-recently-added item in the queue and is the next item to be
 removed.  n_k is the most-recently-added and will be the last of the
 current elements to be removed.
 
 An IntQueue can also be described constructively, with the append operation,
 ':', such that [n0, n1, ..., n_k] : n_k+1 is the result of enqueing n_k+1
 at the end of the queue.
 */
public class IntQueue2 {
  
  // This class represents a queue as a circular ring buffer. An array
  // stores the values in the queue. Because the number of elements
  // currently in the queue is usually less than the size of the
  // array, we store the index of the first item in the queue and the
  // total number of elements in the queue. For example, a queue with 4
  // items might look like this:
  //
  // [__ __ n0   n1   n2   n3 __ __]
  //        ^frontIndex    ^frontIndex+size-1
  //
  // As items are enqueued, front remains unchanged while size is
  // incremented.  As items are dequeued, front is incremented and size
  // is decremented.
  
  // TODO: write abstraction function and representation invarant
  
  /// Starting size for the array
  private let initialSize = 50
  
  private var entries : [Int]
  private var frontIndex : Int
  private var size : Int
  
  /**
   **Effects**: constructs an empty queue
   */
  public init() {
    entries = Array<Int>(repeating: 0, count: initialSize)
    frontIndex = 0
    size = 0
    checkRep()
  }
  
  /**
   Enqueue an item
   
   **Modifies**: self
   
   **Effects**: places entry at the end of the queue
   
   - Parameter entry: item to be added to the queue
   */
  public func enqueue(entry: Int) {
    checkRep()

    // Enlarge queue if necessary
    if (size == entries.count) {
      var newEntries = Array<Int>(repeating: 0, count: 2 * entries.count)
      for i in 0..<entries.count {
        newEntries[i] = entries[(front+i)%entries.count]
      }
      entries = newEntries
      frontIndex = 0
    }
    
    // Add item to the end of the queue, wrapping around to the front if necessary
    entries[(frontIndex + size) % entries.count] = entry
    size += 1
    
    checkRep()
  }
  
  /**
   Dequeue an item
   
   **Requires**: count > 0
   
   **Modifies**: self
   
   **Effects**: removes the item at the front of the queue
   
   - Returns:  the item that was first in the queue
   */
  public func dequeue() -> Int {
    checkRep()

    let ret = entries[frontIndex]
    size -= 1
    frontIndex = (frontIndex + 1) % entries.count
    
    checkRep()
    return ret
  }
  
  /// The item currently first in the queue
  public var front : Int {
    checkRep()
    return entries[frontIndex]
  }
  
  /// Number of elements in the queue
  public var count : Int {
    checkRep()
    return size
  }
  
  /// Whether count == 0
  public var isEmpty : Bool 
    checkRep()
    return entries.isEmpty
  }
  
  public func checkRep() {
    // ...
  }
}